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 / guitext.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-05  |  36.5 KB  |  1,164 lines

  1. /*+==========================================================================
  2.   File:      GUITEXT.CPP
  3.  
  4.   Summary:   Implementation file for the CGuiText C++ class. A GuiText is
  5.              a C++ object that encapsulates the human interface for
  6.              editing the text of a persistent text object.
  7.  
  8.              CGuiText is anchored to the Windows GUI (Graphical User
  9.              Interface) environment--it retains knowledge of window
  10.              handles and device contexts on the local machine. This
  11.              GuiText object relies on a persistent Text object for storage
  12.              of the text data. This text object (a COTextPage object) is
  13.              instantiated as a COM object in a separate thread-safe
  14.              In-process server, PERTEXT.
  15.  
  16.              For a comprehensive tutorial code tour of GUITEXT's contents
  17.              and offerings see the tutorial PERCLIEN.HTM file. For more
  18.              specific technical details on the internal workings see the
  19.              comments dispersed throughout the GUITEXT source code.
  20.  
  21.   Classes:   CGuiText.
  22.  
  23.   Origin:    5-20-97: atrent - Editor inheritance from GUIPAPER.CPP in the
  24.              STOCLIEN source.
  25.  
  26. ----------------------------------------------------------------------------
  27.   This file is part of the Microsoft COM Tutorial Code Samples.
  28.  
  29.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  30.  
  31.   This source code is intended only as a supplement to Microsoft
  32.   Development Tools and/or on-line documentation.  See these other
  33.   materials for detailed information regarding Microsoft code samples.
  34.  
  35.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  36.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  37.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  38.   PARTICULAR PURPOSE.
  39. ==========================================================================+*/
  40.  
  41. /*--------------------------------------------------------------------------
  42.   We include WINDOWS.H for all Win32 applications.
  43.   We include OLE2.H because we will be calling the COM/OLE Libraries.
  44.   We include OLECTL.H because it has definitions for connectable objects.
  45.   We include APPUTIL.H because we will be building this application using
  46.     the convenient Virtual Window and Dialog classes and other
  47.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  48.   We include IPAGES.H and PAGEGUID.H for the common page-related Interface
  49.     class, GUID, and CLSID specifications.
  50.   We include RESDEF.H because it has the resource definitions specific
  51.     to this application.
  52.   We include TEXTWIN.H because CGuiText creates and uses a CTextWin to
  53.     encapsulate a multiline edit control/window.
  54.   We include GUITEXT.H because it has the C++ class used for GUI display
  55.     of the edited text.
  56.   We include TEXTSINK.H because it has the C++ class used for the sink that
  57.     receives event notifications from the COTextPage object in the server.
  58. ---------------------------------------------------------------------------*/
  59. #include <windows.h>
  60. #include <ole2.h>
  61. #include <olectl.h>
  62. #include <apputil.h>
  63. #include <ipages.h>
  64. #include <pageguid.h>
  65. #include "resdef.h"
  66. #include "textwin.h"
  67. #include "guitext.h"
  68. #include "textsink.h"
  69.  
  70.  
  71. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  72.   Method:   CGuiText::CGuiText
  73.  
  74.   Summary:  Constructor.
  75.  
  76.   Args:     void
  77.  
  78.   Returns:  void
  79. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  80. CGuiText::CGuiText(HINSTANCE hInst, HWND hWndApp, INT iPage)
  81. {
  82.   m_hInst           = hInst;
  83.   m_hWndApp         = hWndApp;
  84.   m_hWnd            = NULL;
  85.   m_iPage           = iPage;
  86.   m_wszDataName[0]  = 0;
  87.   m_pTextWin        = NULL;
  88.   m_pIStorage_Root  = NULL;
  89.   m_pITextPage      = NULL;
  90.   m_pCOTextPageSink = NULL;
  91.   m_dwTextPageSink  = 0;
  92.   m_bChanged        = FALSE;
  93.  
  94.   // Save the ClassID of TextPages using overloaded '=' operator.
  95.   m_CidTextPage = CLSID_TextPage;
  96. }
  97.  
  98.  
  99. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  100.   Method:   CGuiText::~CGuiText
  101.  
  102.   Summary:  Destructor.
  103.  
  104.   Args:     void
  105.  
  106.   Returns:  void
  107. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  108. CGuiText::~CGuiText(void)
  109. {
  110.   // CGuiText is derived from CVirWindow which traps the WM_DESTROY
  111.   // message and causes a delete of CGuiText which in turn causes this
  112.   // destructor to run. The WM_DESTROY results when the window is destoyed
  113.   // after a close of the window.
  114.  
  115.   // Disconnect the Sink in the client from the connection points in
  116.   // the server.
  117.   DisconnectSink();
  118.  
  119.   // Release the interface held on the TextPageSink object.
  120.   RELEASE_INTERFACE(m_pCOTextPageSink);
  121.  
  122.   // Destroy the existing COTextPage by releasing the ITextPage interface.
  123.   RELEASE_INTERFACE(m_pITextPage);
  124.  
  125.   // Release the interface held on the root storage.
  126.   RELEASE_INTERFACE(m_pIStorage_Root);
  127.  
  128.   // Delete the CTextWin object (edit control window).
  129.   DELETE_POINTER(m_pTextWin);
  130.  
  131.   // Tell main app that this text page is gone.
  132.   PostMessage(m_hWndApp, WM_USER_PAGECLOSED, 0, m_iPage);
  133. }
  134.  
  135.  
  136. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  137.   Method:   CGuiText::Clear
  138.  
  139.   Summary:  Internal private method to clear (erase) the text content of
  140.             both the current text edit window as well as the COTextPage
  141.             server.
  142.  
  143.   Args:     void.
  144.  
  145.   Returns:  HRESULT
  146.               Standard result code. NOERROR for success.
  147. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  148. HRESULT CGuiText::Clear(
  149.           void)
  150. {
  151.   HRESULT hr = E_FAIL;
  152.  
  153.   if (NULL != m_pTextWin && NULL != m_pITextPage)
  154.   {
  155.     m_pTextWin->Clear();
  156.     m_pITextPage->Clear(TRUE);
  157.     m_bChanged = TRUE;
  158.     // Tell main app if this page was changed.
  159.     PostMessage(m_hWndApp, WM_USER_PAGECHANGED, 0, m_iPage);
  160.     hr = NOERROR;
  161.   }
  162.  
  163.   return hr;
  164. }
  165.  
  166.  
  167. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  168.   Method:   CGuiText::Load
  169.  
  170.   Summary:  Load TextPage data from the current storage Stream.
  171.  
  172.   Args:     void.
  173.  
  174.   Returns:  HRESULT
  175.               Standard result code. NOERROR for success.
  176. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  177. HRESULT CGuiText::Load(void)
  178. {
  179.   HRESULT hr = E_FAIL;
  180.   IStream* pIStream;
  181.   IPersistStreamInit* pIPersistStreamInit;
  182.  
  183.   // Can't do anything without a root storage.
  184.   if (NULL != m_pIStorage_Root)
  185.   {
  186.     // Use the root IStorage to open the existing stream for
  187.     // this particular Text Page. Load the data for the text page from
  188.     // the stream. This load internally uses the IPersistStreamInit
  189.     // features in a COTextPage object that is created and reconstituted
  190.     // from persistent storage.
  191.  
  192.     // Open the single stream named by m_wszDataName. It is under
  193.     // the root storage.
  194.     hr = m_pIStorage_Root->OpenStream(
  195.            m_wszDataName,
  196.            0,
  197.            STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
  198.            0,
  199.            &pIStream);
  200.     if (SUCCEEDED(hr))
  201.     {
  202.       // Read the ClassID for the class of Component Objects that can deal
  203.       // with Text Page data.
  204.       hr = ReadClassStm(pIStream, &m_CidTextPage);
  205.       if (SUCCEEDED(hr))
  206.       {
  207.         // Now use the obtained Class ID to create a COTextPage
  208.         // object. Initially ask for the ITextPage interface.
  209.         hr = CoCreateInstance(
  210.                m_CidTextPage,
  211.                NULL,
  212.                CLSCTX_INPROC_SERVER,
  213.                IID_ITextPage,
  214.                (PPVOID)&m_pITextPage);
  215.         if (SUCCEEDED(hr))
  216.         {
  217.           // We have a new COTextPage object. Now obtain the
  218.           // IPersistStreamInit interface on it. At this point in the
  219.           // client we are assuming that COTextPage uses only the
  220.           // IPersistStreamInit interface for its persistence.
  221.           hr = m_pITextPage->QueryInterface(
  222.                  IID_IPersistStreamInit,
  223.                  (PPVOID)&pIPersistStreamInit);
  224.           if (SUCCEEDED(hr))
  225.           {
  226.             // And as expected by this client, COTextPage exposes
  227.             // the IPersistStreamInit interface. Now use this interface
  228.             // to ask the COTextPage object to load the Text Page data.
  229.             hr = pIPersistStreamInit->Load(pIStream);
  230.  
  231.             // Done with IPersistStreamInit for now so release it.
  232.             pIPersistStreamInit->Release();
  233.           }
  234.         }
  235.       }
  236.       // Done with the interface held on the stream.
  237.       pIStream->Release();
  238.     }
  239.     else
  240.     {
  241.       // If there was no existing Stream then create a new one.
  242.       hr = m_pIStorage_Root->CreateStream(
  243.              m_wszDataName,
  244.              STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
  245.              0,
  246.              0,
  247.              &pIStream);
  248.       if (SUCCEEDED(hr))
  249.       {
  250.         // Write the ClassID of the COTextPage component object class.
  251.         hr = WriteClassStm(pIStream, m_CidTextPage);
  252.         if (SUCCEEDED(hr))
  253.         {
  254.           // Now use the Class ID to create a COTextPage
  255.           // object. Initially ask for the ITextPage interface.
  256.           hr = CoCreateInstance(
  257.                  m_CidTextPage,
  258.                  NULL,
  259.                  CLSCTX_INPROC_SERVER,
  260.                  IID_ITextPage,
  261.                  (PPVOID)&m_pITextPage);
  262.           if (SUCCEEDED(hr))
  263.           {
  264.             // We have a new COTextPage object. Now obtain the
  265.             // IPersistStreamInit interface on it. At this point in the
  266.             // client we are assuming that COTextPage uses only the
  267.             // IPersistStreamInit interface for its persistence.
  268.             hr = m_pITextPage->QueryInterface(
  269.                    IID_IPersistStreamInit,
  270.                    (PPVOID)&pIPersistStreamInit);
  271.             if (SUCCEEDED(hr))
  272.             {
  273.               // And as expected by this client, COTextPage supports
  274.               // the IPersistStreamInit interface. Now use this interface
  275.               // to ask the COTextPage object to initialize a new empty
  276.               // text page. Then save it into the stream.
  277.               hr = pIPersistStreamInit->InitNew();
  278.               if (SUCCEEDED(hr))
  279.                 hr = pIPersistStreamInit->Save(pIStream, TRUE);
  280.  
  281.               // Done with IPersistStreamInit for now so release it.
  282.               pIPersistStreamInit->Release();
  283.             }
  284.           }
  285.         }
  286.  
  287.         // Done with the interface held on the stream.
  288.         pIStream->Release();
  289.  
  290.         if (FAILED(hr))
  291.           m_pIStorage_Root->DestroyElement(m_wszDataName);
  292.       }
  293.     }
  294.   }
  295.  
  296.   return hr;
  297. }
  298.  
  299.  
  300. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  301.   Method:   CGuiText::OpenWin
  302.  
  303.   Summary:  Get CGuiText started. Make any subordinate objects, like
  304.             COTextPage and CTextWin and get them started. Create the
  305.             window. Read the page's text data from the specified stream
  306.             and put it in the edit control.
  307.  
  308.   Args:     IStorage* pIStorage_Root,
  309.               Root storage of the compound file containing the text page.
  310.             WCHAR* pwszPageTitle,
  311.               The user-displayable title for the text page. Generally
  312.               used in the window's title bar.
  313.             WCHAR* pwszDataName)
  314.               The internal storage name of the stream holding the
  315.               text page.
  316.  
  317.   Returns:  HRESULT
  318.               Standard result code. NOERROR for success.
  319. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  320. HRESULT CGuiText::OpenWin(
  321.           IStorage* pIStorage_Root,
  322.           WCHAR* pwszPageTitle,
  323.           WCHAR* pwszDataName)
  324. {
  325.   HRESULT hr = E_FAIL;
  326.   HWND hWnd = NULL;
  327.   COTextPageSink* pCobSink = NULL;
  328.   HCURSOR hCurPrev;
  329.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  330.   TCHAR szTitle[32+PAGE_TITLE_SIZE];
  331.   INT iTextLength;
  332.   WCHAR* pwszText;
  333.  
  334.   if (m_hInst && m_hWndApp && pwszDataName)
  335.   {
  336.     // Change cursor to the hour glass. This open could take awhile.
  337.     hCurPrev = SetCursor(hCurWait);
  338.  
  339.     // Set title string empty to start.
  340.     szTitle[0] = 0;
  341.  
  342.     lstrcpy(szTitle, TEXT(TEXT_WINDOW_NAME_STR));
  343. #if defined(UNICODE)
  344.     if (NULL != pwszPageTitle)
  345.       lstrcat(szTitle, pwszPageTitle);
  346.     lstrcpy(m_wszDataName, pwszDataName);
  347. #else
  348.     {
  349.       CHAR szAnsi[PAGE_TITLE_SIZE];
  350.  
  351.       // Convert PageTitle from Unicode to Ansi.
  352.       if (NULL != pwszPageTitle)
  353.       {
  354.         UcToAnsi(pwszPageTitle, szAnsi, PAGE_TITLE_SIZE);
  355.         lstrcat(szTitle, szAnsi);
  356.       }
  357.       // Copy/save DataName.
  358.       memcpy(m_wszDataName, pwszDataName, PAGE_NAME_SIZE*sizeof(WCHAR));
  359.     }
  360. #endif
  361.     // Create the main GuiText Window. Size the window reasonably. The
  362.     // Create method sets both m_hInst and m_hWnd.
  363.     hWnd = Create(
  364.              TEXT(TEXT_WINDOW_CLASS_NAME_STR),
  365.              szTitle,
  366.              WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
  367.                | WS_MAXIMIZEBOX | WS_THICKFRAME,
  368.              CW_USEDEFAULT,
  369.              CW_USEDEFAULT,
  370.              ::GetSystemMetrics(SM_CXSCREEN)*1/3,
  371.              ::GetSystemMetrics(SM_CYSCREEN)*1/3,
  372.              m_hWndApp,
  373.              NULL,
  374.              m_hInst);
  375.     if (NULL != hWnd)
  376.     {
  377.       // Create the CTextWin object so we can edit/show text data
  378.       // in the text window using a standard text edit control.
  379.       m_pTextWin = new CTextWin;
  380.       if (NULL != m_pTextWin)
  381.       {
  382.         // Init the CTextWin. This creates the subordinate edit control
  383.         // window and fits it into the client area of the main edit window.
  384.         hr = m_pTextWin->Init(m_hInst, m_hWnd, &m_hWndEdit);
  385.         if (SUCCEEDED(hr))
  386.         {
  387.           // Save an interface pointer to the root IStorage
  388.           // and AddRef it.
  389.           m_pIStorage_Root = pIStorage_Root;
  390.           pIStorage_Root->AddRef();
  391.  
  392.           // Now load the text edit control from the named stream in the
  393.           // root storage. This either opens and loads the existing
  394.           // stream or creates a new one.
  395.           hr = Load();
  396.           if (SUCCEEDED(hr))
  397.           {
  398.             // We loaded or created the Text Page stream and have a
  399.             // COTextPage component to manage the text data.
  400.  
  401.             // Create the COTextPageSink object to receive TextPage events.
  402.             pCobSink = new COTextPageSink(NULL, this);
  403.             if (NULL != pCobSink)
  404.             {
  405.               // Save a pointer to the COTextPageSink IUnknown interface.
  406.               // AddRef because of this saved copy. Released in destructor.
  407.               m_pCOTextPageSink = pCobSink;
  408.               m_pCOTextPageSink->AddRef();
  409.  
  410.               // Ask the COTextPage for the text data and pass it on to
  411.               // the Edit control managed by CTextWin.
  412.  
  413.               // First ask for the size (in WCHARs) of the text data.
  414.               hr = m_pITextPage->GetLength(&iTextLength);
  415.               if (SUCCEEDED(hr))
  416.               {
  417.                 // Allocate some temporary space for the text.
  418.                 pwszText = new WCHAR[iTextLength+4];
  419.                 if (NULL != pwszText)
  420.                 {
  421.                   // Zero the text string.
  422.                   memset(pwszText, 0, (iTextLength+2) * sizeof(WCHAR));
  423.  
  424.                   // Get the text from the COTextPage object.
  425.                   hr = m_pITextPage->GetText(pwszText);
  426.                   if (SUCCEEDED(hr))
  427.                   {
  428.                     // Put the text into the edit control.
  429.                     hr = m_pTextWin->PutText(pwszText, iTextLength);
  430.                     if (SUCCEEDED(hr))
  431.                     {
  432.                       // Ensure the new window is shown on screen.
  433.                       ::ShowWindow(m_hWnd, SW_SHOWNORMAL);
  434.                       ::UpdateWindow(m_hWnd);
  435.                     }
  436.                   }
  437.                   // Delete the temporary string buffer.
  438.                   delete [] pwszText;
  439.                 }
  440.                 else
  441.                   hr = E_OUTOFMEMORY;
  442.               }
  443.             }
  444.             else
  445.               hr = E_OUTOFMEMORY;
  446.           }
  447.         }
  448.       }
  449.       else
  450.         hr = E_OUTOFMEMORY;
  451.  
  452.       if (SUCCEEDED(hr))
  453.       {
  454.         // If we created and reconstituted a COTextPage object from
  455.         // persistent stream storage then connect the sinks in the client
  456.         // to the connection sources in the server.
  457.         hr = ConnectSink();
  458.       }
  459.       else
  460.       {
  461.         HrMsgId(m_hInst, m_hWnd, IDS_OPENTEXTPAGE, hr);
  462.         PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  463.       }
  464.     }
  465.  
  466.     // Set Cursor from hourglass back to what it was.
  467.     SetCursor(hCurPrev);
  468.   }
  469.  
  470.   return (hr);
  471. }
  472.  
  473.  
  474. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  475.   Method:   CGuiText::TopWin
  476.  
  477.   Summary:  Brings the CGuiText window to the top of the z-order of the
  478.             the windows.
  479.  
  480.   Args:     void.
  481.  
  482.   Returns:  HRESULT
  483.               Standard result code. NOERROR for success.
  484. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  485. HRESULT CGuiText::TopWin(
  486.           void)
  487. {
  488.   HRESULT hr = E_FAIL;
  489.  
  490.   if (IsWindow(m_hWnd))
  491.   {
  492.     BringWindowToTop(m_hWnd);
  493.     hr = NOERROR;
  494.   }
  495.  
  496.   return hr;
  497. }
  498.  
  499.  
  500. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  501.   Method:   CGuiText::ResizeWin
  502.  
  503.   Summary:  Resize the current CGuiText window. Pass this resize on to the
  504.             text edit control child window.
  505.  
  506.   Args:     WORD wWidth,
  507.               New window width in pixels.
  508.             WORD wHeight)
  509.               New window height in pixels.
  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 CGuiText::ResizeWin(
  515.           WORD wWidth,
  516.           WORD wHeight)
  517. {
  518.   HRESULT hr;
  519.  
  520.   // Tell Text Edit window  about new size.
  521.   hr = m_pTextWin->Resize(wWidth, wHeight);
  522.  
  523.   return hr;
  524. }
  525.  
  526.  
  527. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  528.   Method:   CGuiText::Renumber
  529.  
  530.   Summary:  Re-assign the current dynamic page number for this page.
  531.  
  532.   Args:     INT iPage
  533.               New page number.
  534.  
  535.   Returns:  HRESULT
  536.               Standard result code. NOERROR for success.
  537. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  538. HRESULT CGuiText::Renumber(
  539.           INT iPage)
  540. {
  541.   HRESULT hr = NOERROR;
  542.  
  543.   m_iPage = iPage;
  544.  
  545.   return hr;
  546. }
  547.  
  548.  
  549. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  550.   Method:   CGuiText::ReleasePage
  551.  
  552.   Summary:  Release the root storage held by the Text page.
  553.  
  554.   Args:     none.
  555.  
  556.   Returns:  HRESULT
  557.               Standard result code. NOERROR for success.
  558. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  559. HRESULT CGuiText::ReleasePage(
  560.           void)
  561. {
  562.   HRESULT hr = E_FAIL;
  563.  
  564.   // Release the interface held on the root storage by CGuiText.
  565.   RELEASE_INTERFACE(m_pIStorage_Root);
  566.  
  567.   return hr;
  568. }
  569.  
  570.  
  571. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  572.   Method:   CGuiText::RestorePage
  573.  
  574.   Summary:  Restores the root storage interface for a new file.
  575.  
  576.   Args:     IStorage* pIStorage_Root
  577.               New storage for the Text page.
  578.  
  579.   Returns:  HRESULT
  580.               Standard result code. NOERROR for success.
  581. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  582. HRESULT CGuiText::RestorePage(
  583.           IStorage* pIStorage_Root)
  584. {
  585.   HRESULT hr = E_POINTER;
  586.  
  587.   if (NULL != pIStorage_Root)
  588.   {
  589.     // Save the new interface pointer to the root IStorage
  590.     // and AddRef it.
  591.     m_pIStorage_Root = pIStorage_Root;
  592.     m_pIStorage_Root->AddRef();
  593.     hr = NOERROR;
  594.   }
  595.  
  596.   return hr;
  597. }
  598.  
  599.  
  600. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  601.   Method:   CGuiText::Save
  602.  
  603.   Summary:  Uses COTextPage's IPersistStreamInit interface to save the
  604.             text data in the text page's stream.
  605.  
  606.   Args:     void.
  607.  
  608.   Returns:  HRESULT
  609.               Standard result code. NOERROR for success.
  610. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  611. HRESULT CGuiText::Save(void)
  612. {
  613.   HRESULT hr = E_FAIL;
  614.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  615.   HCURSOR hCurPrev;
  616.   INT iTextLength;
  617.   WCHAR* pwszText;
  618.   LARGE_INTEGER liSeek;
  619.   IStream* pIStream;
  620.   IPersistStreamInit* pIPersistStreamInit;
  621.  
  622.   if (NULL != m_pIStorage_Root && NULL != m_pITextPage)
  623.   {
  624.     // Change cursor to the hour glass.
  625.     hCurPrev = SetCursor(hCurWait);
  626.  
  627.     // Open the single stream named by m_wszDataName. It is under
  628.     // the root storage.
  629.     hr = m_pIStorage_Root->OpenStream(
  630.            m_wszDataName,
  631.            0,
  632.            STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
  633.            0,
  634.            &pIStream);
  635.     if (SUCCEEDED(hr))
  636.     {
  637.       // First ask the edit control how long its text data is.
  638.       hr = m_pTextWin->GetLength(&iTextLength);
  639.       if (SUCCEEDED(hr))
  640.       {
  641.         // Allocate some temporary space for the text.
  642.         pwszText = new WCHAR[iTextLength+4];
  643.         if (NULL != pwszText)
  644.         {
  645.           // Zero the text string.
  646.           memset(pwszText, 0, (iTextLength+2) * sizeof(WCHAR));
  647.  
  648.           // Get the text from the the edit control.
  649.           hr = m_pTextWin->GetText(pwszText);
  650.           if (SUCCEEDED(hr))
  651.           {
  652.             // Put the text into the COTextPage object.
  653.             hr = m_pITextPage->PutText(pwszText, iTextLength);
  654.             if (SUCCEEDED(hr))
  655.             {
  656.               // Clear the TextWin changed flag.
  657.               m_bChanged = FALSE;
  658.  
  659.               // Tell the COTextPage object to save itself
  660.               // (via its IPersistStreamInit interface).
  661.               hr = m_pITextPage->QueryInterface(
  662.                      IID_IPersistStreamInit,
  663.                      (PPVOID)&pIPersistStreamInit);
  664.               if (SUCCEEDED(hr))
  665.               {
  666.                 // Recue Stream to start of page text data.
  667.                 LISet32(liSeek, sizeof(CLSID));
  668.                 hr = pIStream->Seek(liSeek, STREAM_SEEK_SET, NULL);
  669.                 if (SUCCEEDED(hr))
  670.                   hr = pIPersistStreamInit->Save(pIStream, TRUE);
  671.  
  672.                 // Done with IPersistStreamInit for now so release it.
  673.                 pIPersistStreamInit->Release();
  674.               }
  675.             }
  676.           }
  677.           // Delete the temporary text buffer.
  678.           delete [] pwszText;
  679.         }
  680.         else
  681.           hr = E_OUTOFMEMORY;
  682.       }
  683.       // Done with the interface held on the stream.
  684.       pIStream->Release();
  685.     }
  686.  
  687.     // Set Cursor back to what it was.
  688.     SetCursor(hCurPrev);
  689.   }
  690.  
  691.   return hr;
  692. }
  693.  
  694.  
  695. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  696.   Method:   CGuiText::AskSave
  697.  
  698.   Summary:  Check if user edited any changes in the edit window. Also
  699.             checks dirty flag (ie, if current TextPage data was modified
  700.             and is out of sync with the TextPage data stored in the
  701.             compound file). If changed or dirty, then ask user in simple
  702.             dialog if he wants to save new data. If he says yes, then save
  703.             the current TextPage data into the current compound file.
  704.  
  705.   Args:     void.
  706.  
  707.   Returns:  HRESULT
  708.               Standard result code. NOERROR for success.
  709. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  710. int CGuiText::AskSave(void)
  711. {
  712.   int iAns = IDNO;
  713.   HRESULT hr;
  714.   TCHAR szTitle[MAX_STRING_LENGTH];
  715.   TCHAR szAsk[MAX_STRING_LENGTH];
  716.   TCHAR szMsg[MAX_PATH + MAX_STRING_LENGTH];
  717.   IPersistStreamInit* pIPersistStreamInit;
  718.  
  719.   hr = m_pITextPage->QueryInterface(
  720.            IID_IPersistStreamInit,
  721.            (PPVOID)&pIPersistStreamInit);
  722.   if (SUCCEEDED(hr))
  723.   {
  724.     // If changed or if the TextPage object is dirty.
  725.     if (m_bChanged || S_FALSE != pIPersistStreamInit->IsDirty())
  726.     {
  727.       // The current data was changed; ask user if he wants to save it.
  728.       if (LoadString(m_hInst, IDS_TEXT_PAGE, szTitle, MAX_STRING_LENGTH)
  729.           && LoadString(m_hInst, IDS_ASK_SAVE, szAsk, MAX_STRING_LENGTH))
  730.       {
  731.         lstrcpy(szMsg, szTitle);
  732.         lstrcat(szMsg, szAsk);
  733.         // Display AskSaveDlg to user. Ask if he wants to save.
  734.         iAns = MessageBox(
  735.                  m_hWnd,
  736.                  szMsg,
  737.                  szTitle,
  738.                  MB_YESNOCANCEL | MB_ICONEXCLAMATION);
  739.         switch (iAns)
  740.         {
  741.           case IDYES:
  742.             // Tell CGuiText to save itself to the current text page stream.
  743.             Save();
  744.             break;
  745.           case IDNO:
  746.             // User clicked No. So don't save; abandon changes.
  747.             break;
  748.           case IDCANCEL:
  749.           default:
  750.             break;
  751.         }
  752.       }
  753.     }
  754.  
  755.     // Done with IPersistStreamInit for now so release it.
  756.     pIPersistStreamInit->Release();
  757.   }
  758.  
  759.   return iAns;
  760. }
  761.  
  762.  
  763. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  764.   Method:   CGuiText::Close
  765.  
  766.   Summary:  Close this page.
  767.  
  768.   Args:     void.
  769.  
  770.   Returns:  HRESULT
  771.               Standard result code. NOERROR for success.
  772. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  773. HRESULT CGuiText::Close(
  774.           void)
  775. {
  776.   HRESULT hr = NOERROR;
  777.  
  778.   // Close the GuiText window. This will cause a delete of this CGuiText.
  779.   ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  780.  
  781.   return hr;
  782. }
  783.  
  784.  
  785. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  786.   Method:   CGuiText::Delete
  787.  
  788.   Summary:  Delete the stored Text Page. Shuts down the text page and edit
  789.             windows. Disconnects the Sink and does final release of the
  790.             COTextPage object.
  791.  
  792.   Args:     void.
  793.  
  794.   Returns:  HRESULT
  795.               Standard result code. NOERROR for success.
  796. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  797. HRESULT CGuiText::Delete(void)
  798. {
  799.   HRESULT hr = E_FAIL;
  800.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  801.   HCURSOR hCurPrev;
  802.  
  803.   // Change cursor to the hour glass.
  804.   hCurPrev = SetCursor(hCurWait);
  805.  
  806.   // Clear the text page.
  807.   if (NULL != m_pTextWin && NULL != m_pITextPage)
  808.   {
  809.     m_pTextWin->Clear();
  810.     m_pITextPage->Clear(FALSE);
  811.     m_bChanged = FALSE;
  812.   }
  813.  
  814.   // Destroy the entire text page's stream.
  815.   hr = m_pIStorage_Root->DestroyElement(m_wszDataName);
  816.  
  817.   // Set Cursor back to what it was.
  818.   SetCursor(hCurPrev);
  819.  
  820.   return hr;
  821. }
  822.  
  823.  
  824. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  825.   Method:   CGuiText::GetConnectionPoint
  826.  
  827.   Summary:  Internal private method to obtain a connection point interface.
  828.  
  829.   Args:     REFIID riid
  830.               IID of the requested connection point Interface.
  831.  
  832.   Returns:  IConnectionPoint*
  833.               Requested IConnectionPoint interface pointer. NULL if none.
  834. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  835. IConnectionPoint* CGuiText::GetConnectionPoint(
  836.                     REFIID riid)
  837. {
  838.   IConnectionPoint* pConnPoint = NULL;
  839.   IConnectionPointContainer* pConnPointContainer = NULL;
  840.   IConnectionPoint* pConnPt;
  841.   HRESULT hr;
  842.  
  843.   // First query the object for its Connection Point Container. This
  844.   // essentially asks the object in the server if it is connectable.
  845.   hr = m_pITextPage->QueryInterface(
  846.          IID_IConnectionPointContainer,
  847.          (PPVOID)&pConnPointContainer);
  848.   if SUCCEEDED(hr)
  849.   {
  850.     // Find the requested Connection Point. This AddRef's the
  851.     // returned pointer.
  852.     hr = pConnPointContainer->FindConnectionPoint(riid, &pConnPt);
  853.     if (SUCCEEDED(hr))
  854.       pConnPoint = pConnPt;
  855.  
  856.     RELEASE_INTERFACE(pConnPointContainer);
  857.   }
  858.  
  859.   return pConnPoint;
  860. }
  861.  
  862.  
  863. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  864.   Method:   CGuiText::ConnectSink
  865.  
  866.   Summary:  Connect the TextPageSink to the server COTextPage event
  867.             source.
  868.  
  869.   Args:     void
  870.  
  871.   Returns:  HRESULT
  872.               Standard result code. NOERROR for success.
  873. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  874. HRESULT CGuiText::ConnectSink(void)
  875. {
  876.   HRESULT hr = E_FAIL;
  877.   DWORD dwKey;
  878.   IConnectionPoint* pConnPoint;
  879.  
  880.   if (!m_dwTextPageSink)
  881.   {
  882.     // Get the Text Page Sink connection point in the server.
  883.     pConnPoint = GetConnectionPoint(IID_ITextPageSink);
  884.     if (NULL != pConnPoint)
  885.     {
  886.       // Connect the server's object to the Text Page Sink in this client.
  887.       hr = pConnPoint->Advise(m_pCOTextPageSink, &dwKey);
  888.       if (SUCCEEDED(hr))
  889.         m_dwTextPageSink = dwKey;
  890.  
  891.       RELEASE_INTERFACE(pConnPoint);
  892.     }
  893.   }
  894.  
  895.   return hr;
  896. }
  897.  
  898.  
  899. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  900.   Method:   CGuiText::DisconnectSink
  901.  
  902.   Summary:  Disconnect the Text Page from the server COTextPage event
  903.             source.
  904.  
  905.   Args:     void.
  906.  
  907.   Returns:  HRESULT
  908.               Standard result code. NOERROR for success.
  909. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  910. HRESULT CGuiText::DisconnectSink(void)
  911. {
  912.   HRESULT hr = E_FAIL;
  913.   IConnectionPoint* pConnPoint;
  914.  
  915.   if (m_dwTextPageSink)
  916.   {
  917.     // Get the TextPage Sink connection point.
  918.     pConnPoint = GetConnectionPoint(IID_ITextPageSink);
  919.     if (NULL != pConnPoint)
  920.     {
  921.       // Disconnect the object in the server from the Text Page Sink in
  922.       // this client.
  923.       hr = pConnPoint->Unadvise(m_dwTextPageSink);
  924.       if (SUCCEEDED(hr))
  925.         m_dwTextPageSink = 0;
  926.  
  927.       RELEASE_INTERFACE(pConnPoint);
  928.     }
  929.   }
  930.  
  931.   return hr;
  932. }
  933.  
  934.  
  935. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  936.   Method:   CGuiText::InitEditMenu
  937.  
  938.   Summary:  Init the Parent Window's Edit menu for edit operations.
  939.  
  940.   Args:     HMENU hEditMenu)
  941.               Handle of the Edit Menu of the parent window.
  942.  
  943.   Returns:  HRESULT
  944.               Standard result code. NOERROR for success.
  945. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  946. HRESULT CGuiText::InitEditMenu(
  947.           HMENU hEditMenu)
  948. {
  949.   HRESULT hr = E_FAIL;
  950.   INT iSelect, iEnable;
  951.  
  952.   if (IsWindow(m_hWnd))
  953.   {
  954.     // Enable/Disable the Undo choice.
  955.     EnableMenuItem(
  956.       hEditMenu,
  957.       IDM_EDIT_UNDO,
  958.       SendMessage(m_hWndEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED);
  959.  
  960.     // Enable/Disable the Paste choice.
  961.     EnableMenuItem(
  962.       hEditMenu,
  963.       IDM_EDIT_PASTE,
  964.       IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
  965.  
  966.     // Enable/Disable the Cut, Copy, Delete choices.
  967.     iSelect = SendMessage(m_hWndEdit, EM_GETSEL, 0, 0);
  968.     iEnable = (HIWORD(iSelect) == LOWORD(iSelect)) ? MF_GRAYED : MF_ENABLED;
  969.     EnableMenuItem(hEditMenu, IDM_EDIT_CUT, iEnable);
  970.     EnableMenuItem(hEditMenu, IDM_EDIT_COPY, iEnable);
  971.     EnableMenuItem(hEditMenu, IDM_EDIT_DELETE, iEnable);
  972.  
  973.     hr = NOERROR;
  974.   }
  975.  
  976.   return hr;
  977. }
  978.  
  979.  
  980. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  981.   Method:   CGuiText::DoCommand
  982.  
  983.   Summary:  Dispatch and handle the main menu and other commands.
  984.  
  985.   Args:     WPARAM wParam,
  986.               First message parameter.
  987.             LPARAM lParam)
  988.               Second message parameter.
  989.  
  990.   Returns:  LRESULT
  991.               Standard Windows WindowProc return value.
  992. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  993. LRESULT CGuiText::DoCommand(
  994.           WPARAM wParam,
  995.           LPARAM lParam)
  996. {
  997.   LRESULT lResult = FALSE;
  998.   HMENU hMenu  = ::GetMenu(m_hWnd);
  999.  
  1000.   switch (LOWORD(wParam))
  1001.   {
  1002.     //----------------------------------------------------------------------
  1003.     // Handle Text Menu Commands.
  1004.     //----------------------------------------------------------------------
  1005.     case IDM_TEXT_SAVE:
  1006.       Save();
  1007.       break;
  1008.     case IDM_TEXT_CLEAR:
  1009.       Clear();
  1010.       break;
  1011.     case IDM_TEXT_EXIT:
  1012.       // The user commands us to exit this page so we tell the
  1013.       // window to close itself.
  1014.       ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  1015.       break;
  1016.  
  1017.     //----------------------------------------------------------------------
  1018.     // Handle Edit Menu Commands.
  1019.     //----------------------------------------------------------------------
  1020.     case IDM_EDIT_UNDO:
  1021.       m_pTextWin->EditUndo();
  1022.       break;
  1023.     case IDM_EDIT_SELECTALL:
  1024.       m_pTextWin->EditSelectAll();
  1025.       break;
  1026.     case IDM_EDIT_CUT:
  1027.       m_pTextWin->EditCut();
  1028.       break;
  1029.     case IDM_EDIT_COPY:
  1030.       m_pTextWin->EditCopy();
  1031.       break;
  1032.     case IDM_EDIT_PASTE:
  1033.       m_pTextWin->EditPaste();
  1034.       break;
  1035.     case IDM_EDIT_DELETE:
  1036.       m_pTextWin->EditDelete();
  1037.       break;
  1038.  
  1039.     //----------------------------------------------------------------------
  1040.     // Handle Help Menu Commands. Pass these commands on to main app.
  1041.     //----------------------------------------------------------------------
  1042.     case IDM_THELP_CONTENTS:
  1043.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_CONTENTS, 0);
  1044.       break;
  1045.     case IDM_THELP_TUTORIAL:
  1046.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_TUTORIAL, 0);
  1047.       break;
  1048.     case IDM_THELP_TUTSERVER:
  1049.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_PERTEXT, 0);
  1050.       break;
  1051.     case IDM_THELP_READSOURCE:
  1052.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_READSOURCE, 0);
  1053.       break;
  1054.     case IDM_THELP_ABOUT:
  1055.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_ABOUT, 0);
  1056.       break;
  1057.  
  1058.     //----------------------------------------------------------------------
  1059.     // Handle TextWin Commands (ie, from the child edit control).
  1060.     //----------------------------------------------------------------------
  1061.     case IDC_TEXTWIN:
  1062.       switch (HIWORD(wParam))
  1063.       {
  1064.         case EN_CHANGE:
  1065.           if (!m_bChanged)
  1066.           {
  1067.             m_bChanged = TRUE;
  1068.             // Tell main app if this page was changed.
  1069.             PostMessage(m_hWndApp, WM_USER_PAGECHANGED, 0, m_iPage);
  1070.           }
  1071.           break;
  1072.         case EN_MAXTEXT:
  1073.         case EN_ERRSPACE:
  1074.           ErrorBox(m_hInst, m_hWnd, IDS_PAGEFULL);
  1075.           break;
  1076.         default:
  1077.           break;
  1078.       }
  1079.       break;
  1080.  
  1081.     default:
  1082.       // Defer all messages NOT handled here to the Default Window Proc.
  1083.       lResult = ::DefWindowProc(m_hWnd, WM_COMMAND, wParam, lParam);
  1084.       break;
  1085.   }
  1086.  
  1087.   return(lResult);
  1088. }
  1089.  
  1090.  
  1091. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1092.   Method:   CGuiText::WindowProc
  1093.  
  1094.   Summary:  Main window procedure for this window object.  See CVirWindow
  1095.             in the APPUTIL library (APPUTIL.CPP) for details on how this
  1096.             method gets called by the global WindowProc.
  1097.  
  1098.   Args:     UINT uMsg,
  1099.               Windows message that is "sent" to this window.
  1100.             WPARAM wParam,
  1101.               First message parameter.
  1102.             LPARAM lParam)
  1103.               Second message parameter.
  1104.  
  1105.   Returns:  LRESULT
  1106.               Standard Windows WindowProc return value.
  1107. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1108. LRESULT CGuiText::WindowProc(
  1109.           UINT uMsg,
  1110.           WPARAM wParam,
  1111.           LPARAM lParam)
  1112. {
  1113.   LRESULT lResult = FALSE;
  1114.  
  1115.   switch (uMsg)
  1116.   {
  1117.     case WM_CREATE:
  1118.       break;
  1119.  
  1120.     case WM_SIZE:
  1121.       // Handle a resize of this window.
  1122.       // Inform CGuiText and CTextWin of the change.
  1123.       ResizeWin(LOWORD(lParam), HIWORD(lParam));
  1124.       break;
  1125.  
  1126.     case WM_INITMENUPOPUP:
  1127.       // Enable Edit Menu items.
  1128.       if (LOWORD(lParam) == 1)
  1129.         InitEditMenu((HMENU) wParam);
  1130.       break;
  1131.  
  1132.     case WM_COMMAND:
  1133.       // Dispatch and handle any Menu and command messages received.
  1134.       lResult = DoCommand(wParam, lParam);
  1135.       break;
  1136.  
  1137.     case WM_CHAR:
  1138.       if (wParam == 0x1b)
  1139.       {
  1140.         // Exit this app if user hits ESC key.
  1141.         ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  1142.       }
  1143.       break;
  1144.  
  1145.     case WM_CLOSE:
  1146.       // The user selected Close on the main window's System menu
  1147.       // or Exit on the File menu.
  1148.       // If there is data that has not been saved then ask user
  1149.       // if it should be saved. If user cancels then cancel the exit.
  1150.       if (IDCANCEL == AskSave())
  1151.         break;
  1152.     case WM_QUIT:
  1153.       BringWindowToTop(m_hWndApp);
  1154.       // If the app is being quit then close any associated help windows.
  1155.       // For later evolution...
  1156.     default:
  1157.       // Defer all messages NOT handled above to the Default Window Proc.
  1158.       lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  1159.       break;
  1160.   }
  1161.  
  1162.   return(lResult);
  1163. }
  1164.