home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / MFC / SRC / DOCCORE.CP_ / DOCCORE.CP
Encoding:
Text File  |  1993-02-08  |  15.9 KB  |  639 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and Microsoft
  7. // QuickHelp and/or WinHelp documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11.  
  12. #include "stdafx.h"
  13. #include "io.h" // for access
  14.  
  15. #ifdef AFX_CORE2_SEG
  16. #pragma code_seg(AFX_CORE2_SEG)
  17. #endif
  18.  
  19. #ifdef _DEBUG
  20. #undef THIS_FILE
  21. static char BASED_CODE THIS_FILE[] = __FILE__;
  22. #endif
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CDocument
  26.  
  27. IMPLEMENT_DYNAMIC(CDocument, CCmdTarget)
  28.  
  29. BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
  30.     //{{AFX_MSG_MAP(CDocument)
  31.     ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
  32.     ON_COMMAND(ID_FILE_SAVE, OnFileSave)
  33.     ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
  34.     //}}AFX_MSG_MAP
  35. END_MESSAGE_MAP()
  36.  
  37. /////////////////////////////////////////////////////////////////////////////
  38. // CDocument construction/destruction
  39.  
  40. CDocument::CDocument()
  41. {
  42.     m_pDocTemplate = NULL;
  43.     m_bModified = FALSE;
  44.     m_bAutoDelete = TRUE;       // default to auto delete document
  45.     ASSERT(m_viewList.IsEmpty());
  46. }
  47.  
  48. CDocument::~CDocument()
  49. {
  50.     // do not call DeleteContents here !
  51. #ifdef _DEBUG
  52.     if (IsModified())
  53.         TRACE0("Warning: destroying an unsaved document\n");
  54. #endif
  55.  
  56.     // there should be no views left!
  57.     ASSERT(m_viewList.IsEmpty());
  58.  
  59.     if (m_pDocTemplate != NULL)
  60.         m_pDocTemplate->RemoveDocument(this);
  61.     ASSERT(m_pDocTemplate == NULL);     // must be detached
  62. }
  63.  
  64. /////////////////////////////////////////////////////////////////////////////
  65. // CDocument attributes, general services
  66.  
  67. void CDocument::SetTitle(const char* pszTitle)
  68. {
  69.     m_strTitle = pszTitle;
  70.     UpdateFrameCounts();        // will cause name change in views
  71. }
  72.  
  73. void CDocument::DeleteContents()
  74. {
  75. }
  76.  
  77. /////////////////////////////////////////////////////////////////////////////
  78. // Closing documents or views
  79.  
  80. void CDocument::OnChangedViewList()
  81. {
  82.     // if no more views on the document, delete ourself
  83.     // not called if directly closing the document or terminating the app
  84.     if (m_viewList.IsEmpty() && m_bAutoDelete)
  85.     {
  86.         // get rid of this object
  87.         DeleteContents(); // do it before destruction for correct virtual call
  88.         delete this;
  89.         return;
  90.     }
  91.  
  92.     // update the frame counts as needed
  93.     UpdateFrameCounts();
  94. }
  95.  
  96. void CDocument::UpdateFrameCounts()
  97.      // assumes 1 doc per frame
  98. {
  99.     // walk all frames of views (mark and sweep approach)
  100.     POSITION pos = GetFirstViewPosition();
  101.     while (pos)
  102.     {
  103.         CView* pView = GetNextView(pos);
  104.         ASSERT_VALID(pView);
  105.         if (pView->IsWindowVisible())   // Do not count invisible windows.
  106.         {
  107.             CFrameWnd* pFrame = pView->GetParentFrame();
  108.             if (pFrame != NULL && (pFrame->GetStyle() & FWS_ADDTOTITLE))
  109.                 pFrame->m_nWindow = -1;     // unknown
  110.         }
  111.     }
  112.  
  113.     // now do it again counting the unique ones
  114.     int nFrames = 0;
  115.     pos = GetFirstViewPosition();
  116.     while (pos)
  117.     {
  118.         CView* pView = GetNextView(pos);
  119.         ASSERT_VALID(pView);
  120.         if (pView->IsWindowVisible())   // Do not count invisible windows.
  121.         {
  122.             CFrameWnd* pFrame = pView->GetParentFrame();
  123.             if (pFrame != NULL && (pFrame->GetStyle() & FWS_ADDTOTITLE) &&
  124.                 pFrame->m_nWindow == -1)
  125.             {
  126.                 ASSERT_VALID(pFrame);
  127.                 // not yet counted (give it a 1 based number)
  128.                 pFrame->m_nWindow = ++nFrames;
  129.             }
  130.         }
  131.     }
  132.  
  133.     // lastly walk the frames and update titles (assume same order)
  134.     // go through frames updating the appropriate one
  135.     int iFrame = 1;
  136.     pos = GetFirstViewPosition();
  137.     while (pos)
  138.     {
  139.         CView* pView = GetNextView(pos);
  140.         ASSERT_VALID(pView);
  141.         if (pView->IsWindowVisible())   // Do not count invisible windows.
  142.         {
  143.             CFrameWnd* pFrame = pView->GetParentFrame();
  144.             if (pFrame != NULL && (pFrame->GetStyle() & FWS_ADDTOTITLE) &&
  145.                 pFrame->m_nWindow == iFrame)
  146.             {
  147.                 ASSERT_VALID(pFrame);
  148.                 if (nFrames == 1)
  149.                     pFrame->m_nWindow = 0;      // the only one of it's kind
  150.                 pFrame->OnUpdateFrameTitle(TRUE);
  151.                 iFrame++;
  152.             }
  153.         }
  154.     }
  155.     ASSERT(iFrame == nFrames + 1);
  156. }
  157.  
  158. BOOL CDocument::CanCloseFrame(CFrameWnd* pFrameArg)
  159.     // permission to close all views using this frame
  160.     //  (at least one of our views must be in this frame)
  161. {
  162.     ASSERT_VALID(pFrameArg);
  163.  
  164.     POSITION pos = GetFirstViewPosition();
  165.     while (pos)
  166.     {
  167.         CView* pView = GetNextView(pos);
  168.         ASSERT_VALID(pView);
  169.         CFrameWnd* pFrame = pView->GetParentFrame();
  170.         // assume frameless views are ok to close
  171.         if (pFrame != NULL)
  172.         {
  173.             // assumes 1 document per frame
  174.             ASSERT_VALID(pFrame);
  175.             if (pFrame->m_nWindow > 0)
  176.                 return TRUE;        // more than one frame refering to us
  177.         }
  178.     }
  179.  
  180.     // otherwise only one frame that we know about
  181.     if (IsModified())
  182.         return SaveModified();
  183.     return TRUE;
  184. }
  185.  
  186.  
  187. /////////////////////////////////////////////////////////////////////////////
  188. // File/Path commands
  189.  
  190. void CDocument::SetPathName(const char* pszPathName)
  191. {
  192.     m_strPathName = pszPathName;
  193.     ASSERT(!m_strPathName.IsEmpty());       // must be set to something
  194.  
  195.     // Set the document title based on path name
  196.     char szTitle[_MAX_FNAME];
  197.     if (::GetFileTitle(m_strPathName, szTitle, _MAX_FNAME) == 0)
  198.     {
  199.         ::AnsiUpper(szTitle);       // always upper case
  200.         SetTitle(szTitle);
  201.     }
  202.  
  203.     AfxGetApp()->AddToRecentFileList(pszPathName);
  204. }
  205.  
  206. /////////////////////////////////////////////////////////////////////////////
  207. // Standard file menu commands
  208.  
  209. void CDocument::OnFileClose()
  210. {
  211.     if (!SaveModified())
  212.         return;
  213.  
  214.     // shut it down
  215.     OnCloseDocument();
  216.         // this should destroy the document
  217. }
  218.  
  219.  
  220. void CDocument::OnFileSave()
  221. {
  222.     if (_access(m_strPathName, 6) != 0)
  223.     {
  224.         // we do not have read-write access or the file does not (now) exist
  225.         if (!DoSave(NULL))
  226.             TRACE0("Warning: File save with new name failed\n");
  227.     }
  228.     else
  229.     {
  230.         if (!DoSave(m_strPathName))
  231.             TRACE0("Warning: File save failed\n");
  232.     }
  233. }
  234.  
  235.  
  236. void CDocument::OnFileSaveAs()
  237. {
  238.     if (!DoSave(NULL))
  239.         TRACE0("Warning: File save-as failed\n");
  240. }
  241.  
  242.  
  243. BOOL CDocument::DoSave(const char* pszPathName, BOOL bReplace /*=TRUE*/)
  244.     // Save the document data to a file
  245.     // pszPathName = path name where to save document file
  246.     // if pszPathName is NULL then the user will be prompted (SaveAs)
  247.     // note: pszPathName can be different than 'm_strPathName'
  248.     // if 'bReplace' is TRUE will change file name if successful (SaveAs)
  249.     // if 'bReplace' is FALSE will not change path name (SaveCopyAs)
  250. {
  251.     CString newName = pszPathName;
  252.     if (newName.IsEmpty())
  253.     {
  254.         CDocTemplate* pTemplate = GetDocTemplate();
  255.         ASSERT(pTemplate != NULL);
  256.  
  257.         newName = m_strPathName;
  258.         if (newName.IsEmpty())
  259.         {
  260.             newName = m_strTitle;
  261.             if (newName.GetLength() > 8)
  262.                 newName.ReleaseBuffer(8);
  263.             int iBad = newName.FindOneOf(" #%;/\\");    // dubious filename
  264.             if (iBad != -1)
  265.                 newName.ReleaseBuffer(iBad);
  266.  
  267.             // append the default suffix if there is one
  268.             CString strExt;
  269.             if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) &&
  270.               !strExt.IsEmpty())
  271.             {
  272.                 ASSERT(strExt[0] == '.');
  273.                 newName += strExt;
  274.             }
  275.         }
  276.  
  277.         if (!AfxGetApp()->DoPromptFileName(newName, AFX_IDS_SAVEFILE,
  278.           OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate))
  279.             return FALSE;       // don't even try to save
  280.     }
  281.  
  282.     if (!OnSaveDocument(newName))
  283.     {
  284.         if (pszPathName == NULL)
  285.         {
  286.             // be sure to delete the file
  287.             TRY 
  288.             {
  289.                 CFile::Remove(newName);
  290.             }
  291.             CATCH_ALL(e)
  292.             {
  293.                 TRACE0("Warning: failed to delete file after failed SaveAs\n");
  294.             }
  295.             END_CATCH_ALL
  296.         }
  297.         return FALSE;
  298.     }
  299.  
  300.     if (bReplace)
  301.     {
  302.         // Reset the title and change the document name
  303.         SetPathName(newName);
  304.         ASSERT(m_strPathName == newName);       // must be set
  305.     }
  306.     return TRUE;        // success
  307. }
  308.  
  309.  
  310. BOOL CDocument::SaveModified()
  311. {
  312.     if (!IsModified())
  313.         return TRUE;        // ok to continue
  314.  
  315.     CString name = m_strPathName;
  316.     if (name.IsEmpty())
  317.         VERIFY(name.LoadString(AFX_IDS_UNTITLED));
  318.  
  319.     CString prompt;
  320.     AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, name);
  321.     switch (AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_SAVE))
  322.     {
  323.     case IDCANCEL:
  324.         return FALSE;       // don't continue
  325.  
  326.     case IDYES:
  327.         // If so, either Save or Update, as appropriate
  328.         if (!DoSave(m_strPathName))
  329.             return FALSE;       // don't continue
  330.         break;
  331.  
  332.     case IDNO:
  333.         // If not saving changes, revert the document
  334.         break;
  335.  
  336.     default:
  337.         ASSERT(FALSE);
  338.         break;
  339.     }
  340.     return TRUE;    // keep going
  341. }
  342.  
  343. void CDocument::ReportSaveLoadException(const char* pszPathName, CException* e,
  344.         BOOL bSaving, UINT nIDPDefault)
  345. {
  346.     ASSERT_VALID(e);
  347.  
  348.     UINT nIDP = nIDPDefault;
  349.  
  350.     if (e->IsKindOf(RUNTIME_CLASS(CUserException)))
  351.         return; // already reported
  352.  
  353.     if (e->IsKindOf(RUNTIME_CLASS(CArchiveException)))
  354.     {
  355.         switch (((CArchiveException*)e)->m_cause)
  356.         {
  357.         case CArchiveException::badSchema:
  358.         case CArchiveException::badClass:
  359.         case CArchiveException::badIndex:
  360.         case CArchiveException::endOfFile:
  361.             nIDP = AFX_IDP_FAILED_INVALID_FORMAT;
  362.             break;
  363.         default:
  364.             break;
  365.         }
  366.     } 
  367.     else if (e->IsKindOf(RUNTIME_CLASS(CFileException)))
  368.     {
  369.         TRACE1("Reporting file I/O exception on Save/Load"
  370.             " with lOsError = $%lX\n", ((CFileException*)e)->m_lOsError);
  371.             
  372.         switch (((CFileException*)e)->m_cause)
  373.         {
  374.         case CFileException::fileNotFound:
  375.         case CFileException::badPath:
  376.             nIDP = AFX_IDP_FAILED_INVALID_PATH;
  377.             break;
  378.         case CFileException::diskFull:
  379.             nIDP = AFX_IDP_FAILED_DISK_FULL;
  380.             break;
  381.         case CFileException::accessDenied:
  382.             nIDP = bSaving ? AFX_IDP_FAILED_ACCESS_WRITE :
  383.                     AFX_IDP_FAILED_ACCESS_READ;
  384.             break;
  385.  
  386.         case CFileException::badSeek:
  387.         case CFileException::generic:
  388.         case CFileException::tooManyOpenFiles:
  389.         case CFileException::invalidFile:
  390.         case CFileException::hardIO:
  391.         case CFileException::directoryFull:
  392.             nIDP = bSaving ? AFX_IDP_FAILED_IO_ERROR_WRITE :
  393.                     AFX_IDP_FAILED_IO_ERROR_READ;
  394.             break;
  395.         default:
  396.             break;
  397.         }
  398.     }
  399.  
  400.     CString prompt;
  401.     AfxFormatString1(prompt, nIDP, pszPathName);
  402.     AfxMessageBox(prompt, MB_ICONEXCLAMATION, nIDP);
  403. }
  404.  
  405.  
  406. /////////////////////////////////////////////////////////////////////////////
  407. // File operations (default uses CDocument::Serialize)
  408.  
  409. BOOL CDocument::OnNewDocument()
  410. {
  411.     if (IsModified())
  412.         TRACE0("Warning: OnNewDocument replaces an unsaved document\n");
  413.     DeleteContents();
  414.  
  415.     m_strPathName.Empty();      // no path name yet
  416.  
  417.     SetModifiedFlag(FALSE);     // make clean
  418.     return TRUE;
  419. }
  420.  
  421.  
  422. BOOL CDocument::OnOpenDocument(const char* pszPathName)
  423. {
  424.     if (IsModified())
  425.         TRACE0("Warning: OnOpenDocument replaces an unsaved document\n");
  426.  
  427.     CFile file;
  428.     CFileException fe;
  429.     if (!file.Open(pszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe))
  430.     {
  431.         ReportSaveLoadException(pszPathName, &fe,
  432.             FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  433.         return FALSE;
  434.     }
  435.  
  436.     DeleteContents();
  437.     SetModifiedFlag(TRUE);  // dirty during de-serialize
  438.  
  439.     CArchive loadArchive(&file, CArchive::load | CArchive::bNoFlushOnDelete);
  440.     TRY
  441.     {
  442.         BeginWaitCursor();
  443.         Serialize(loadArchive);     // load me
  444.         loadArchive.Close();
  445.         file.Close();
  446.     }
  447.     CATCH_ALL(e)
  448.     {
  449.         file.Abort(); // will not throw an exception
  450.         EndWaitCursor();
  451.         ReportSaveLoadException(pszPathName, e,
  452.             FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  453.  
  454.         DeleteContents();   // remove failed contents
  455.         return FALSE;
  456.     }
  457.     END_CATCH_ALL
  458.  
  459.     EndWaitCursor();
  460.     SetPathName(pszPathName);
  461.     SetModifiedFlag(FALSE);     // start off with unmodified
  462.     return TRUE;
  463. }
  464.  
  465. BOOL CDocument::OnSaveDocument(const char* pszPathName)
  466. {
  467.     CFile file;
  468.     CFileException fe;
  469.  
  470.     if (!file.Open(pszPathName, CFile::modeCreate |
  471.       CFile::modeReadWrite | CFile::shareExclusive, &fe))
  472.     {
  473.         ReportSaveLoadException(pszPathName, &fe,
  474.             TRUE, AFX_IDP_INVALID_FILENAME);
  475.         return FALSE;
  476.     }
  477.  
  478.     CArchive saveArchive(&file, CArchive::store | CArchive::bNoFlushOnDelete);
  479.     TRY
  480.     {
  481.         BeginWaitCursor();
  482.         Serialize(saveArchive);     // save me
  483.         saveArchive.Close();
  484.         file.Close();
  485.     }
  486.     CATCH_ALL(e)
  487.     {
  488.         file.Abort(); // will not throw an exception
  489.         EndWaitCursor();
  490.         ReportSaveLoadException(pszPathName, e,
  491.             TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
  492.         return FALSE;
  493.     }
  494.     END_CATCH_ALL
  495.  
  496.     EndWaitCursor();
  497.     SetModifiedFlag(FALSE);     // back to unmodified
  498.  
  499.     return TRUE;        // success
  500. }
  501.  
  502.  
  503. void CDocument::OnCloseDocument()
  504.     // must close all views now (no prompting) - usually destroy's this
  505. {
  506.     DeleteContents();       // clean up contents first
  507.  
  508.     // destroy all frames viewing this document
  509.     // the last destroy may destroy us
  510.     BOOL bOldAuto = m_bAutoDelete;
  511.     m_bAutoDelete = FALSE;  // don't destroy document while closing views
  512.     while (!m_viewList.IsEmpty())
  513.     {
  514.         CView* pView = (CView*)m_viewList.GetHead();
  515.         ASSERT_VALID(pView);
  516.         CFrameWnd* pFrame = pView->GetParentFrame();
  517.         ASSERT_VALID(pFrame);
  518.         pFrame->DestroyWindow();
  519.             // will destroy the view as well
  520.     }
  521.     m_bAutoDelete = bOldAuto;
  522.     OnChangedViewList();    // must be the last thing done to the document
  523. }
  524.  
  525. /////////////////////////////////////////////////////////////////////////////
  526. // View operations
  527.  
  528. void CDocument::AddView(CView* pView)
  529. {
  530.     ASSERT_VALID(pView);
  531.     ASSERT(pView->m_pDocument == NULL); // must not be already attached
  532.     ASSERT(m_viewList.Find(pView, NULL) == NULL);   // must not be in list
  533.  
  534.     m_viewList.AddTail(pView);
  535.     ASSERT(pView->m_pDocument == NULL); // must be un-attached
  536.     pView->m_pDocument = this;
  537.  
  538.     OnChangedViewList();    // must be the last thing done to the document
  539. }
  540.  
  541. void CDocument::RemoveView(CView* pView)
  542. {
  543.     ASSERT_VALID(pView);
  544.     ASSERT(pView->m_pDocument == this); // must be attached to us
  545.  
  546.     m_viewList.RemoveAt(m_viewList.Find(pView));
  547.     pView->m_pDocument = NULL;
  548.  
  549.     OnChangedViewList();    // must be the last thing done to the document
  550. }
  551.  
  552. POSITION CDocument::GetFirstViewPosition() const
  553. {
  554.     return m_viewList.GetHeadPosition();
  555. }
  556.  
  557. CView* CDocument::GetNextView(POSITION& rPosition) const
  558. {
  559.     ASSERT(rPosition != BEFORE_START_POSITION);
  560.         // use CDocument::GetFirstViewPosition instead !
  561.     if (rPosition == NULL)
  562.         return NULL;    // nothing left
  563.     CView* pView = (CView*)m_viewList.GetNext(rPosition);
  564.     ASSERT(pView->IsKindOf(RUNTIME_CLASS(CView)));
  565.     return pView;
  566. }
  567.  
  568. void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)
  569.     // walk through all views
  570. {
  571.     ASSERT(pSender == NULL || !m_viewList.IsEmpty());
  572.         // must have views if sent by one of them
  573.  
  574.     POSITION pos = GetFirstViewPosition();
  575.     while (pos)
  576.     {
  577.         CView* pView = GetNextView(pos);
  578.         ASSERT_VALID(pView);
  579.         if (pView != pSender)
  580.             pView->OnUpdate(pSender, lHint, pHint);
  581.     }
  582. }
  583.  
  584. /////////////////////////////////////////////////////////////////////////////
  585. // command routing
  586.  
  587. BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  588.     AFX_CMDHANDLERINFO* pHandlerInfo)
  589. {
  590.     if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  591.         return TRUE;
  592.  
  593.     // otherwise try template
  594.     if (m_pDocTemplate != NULL &&
  595.       m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  596.         return TRUE;
  597.  
  598.     return FALSE;
  599. }
  600.  
  601. /////////////////////////////////////////////////////////////////////////////
  602. // CDocument diagnostics
  603.  
  604. #ifdef _DEBUG
  605. void CDocument::Dump(CDumpContext& dc) const
  606. {
  607.     CObject::Dump(dc);
  608.     AFX_DUMP1(dc, "\nm_strTitle = ", m_strTitle);
  609.     AFX_DUMP1(dc, "\nm_strPathName = ", m_strPathName);
  610.     AFX_DUMP1(dc, "\nm_bModified = ", m_bModified);
  611.     AFX_DUMP1(dc, "\nm_pDocTemplate = ", (void*)m_pDocTemplate);
  612.  
  613.     if (dc.GetDepth() > 0)
  614.     {
  615.         POSITION pos = GetFirstViewPosition();
  616.         while (pos)
  617.         {
  618.             CView* pView = GetNextView(pos);
  619.             ASSERT_VALID(pView);
  620.             AFX_DUMP1(dc, "\nwith view ", (void*)pView);
  621.         }
  622.     }
  623. }
  624.  
  625. void CDocument::AssertValid() const
  626. {
  627.     CObject::AssertValid();
  628.  
  629.     POSITION pos = GetFirstViewPosition();
  630.     while (pos)
  631.     {
  632.         CView* pView = GetNextView(pos);
  633.         ASSERT_VALID(pView);
  634.     }
  635. }
  636. #endif //_DEBUG
  637.  
  638. /////////////////////////////////////////////////////////////////////////////
  639.