home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / DOCCORE.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-30  |  23.7 KB  |  992 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1997 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 related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #ifndef _MAC
  13. #include "io.h" // for _access
  14. #else
  15. #include <macname1.h>
  16. #include <Types.h>
  17. #include <Finder.h>
  18. #include <Files.h>
  19. #include <Resources.h>
  20. #include <Memory.h>
  21. #include <Script.h>
  22. #include <macname2.h>
  23.  
  24. // misspelled in Finder.h
  25. #ifndef kIsStationery
  26. #define kIsStationery kIsStationary
  27. #endif
  28. #endif //_MAC
  29.  
  30. #ifdef AFX_CORE2_SEG
  31. #pragma code_seg(AFX_CORE2_SEG)
  32. #endif
  33.  
  34. #ifdef _DEBUG
  35. #undef THIS_FILE
  36. static char THIS_FILE[] = __FILE__;
  37. #endif
  38.  
  39. /////////////////////////////////////////////////////////////////////////////
  40. // CDocument
  41.  
  42. BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
  43.     //{{AFX_MSG_MAP(CDocument)
  44.     ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
  45.     ON_COMMAND(ID_FILE_SAVE, OnFileSave)
  46.     ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
  47.     //}}AFX_MSG_MAP
  48. END_MESSAGE_MAP()
  49.  
  50. /////////////////////////////////////////////////////////////////////////////
  51. // CDocument construction/destruction
  52.  
  53. CDocument::CDocument()
  54. {
  55.     m_pDocTemplate = NULL;
  56.     m_bModified = FALSE;
  57.     m_bAutoDelete = TRUE;       // default to auto delete document
  58.     m_bEmbedded = FALSE;        // default to file-based document
  59.     ASSERT(m_viewList.IsEmpty());
  60. }
  61.  
  62. CDocument::~CDocument()
  63. {
  64.     // do not call DeleteContents here !
  65. #ifdef _DEBUG
  66.     if (IsModified())
  67.         TRACE0("Warning: destroying an unsaved document.\n");
  68. #endif
  69.  
  70.     // there should be no views left!
  71.     DisconnectViews();
  72.     ASSERT(m_viewList.IsEmpty());
  73.  
  74.     if (m_pDocTemplate != NULL)
  75.         m_pDocTemplate->RemoveDocument(this);
  76.     ASSERT(m_pDocTemplate == NULL);     // must be detached
  77. }
  78.  
  79. void CDocument::OnFinalRelease()
  80. {
  81.     ASSERT_VALID(this);
  82.  
  83.     OnCloseDocument();  // may 'delete this'
  84. }
  85.  
  86. void CDocument::DisconnectViews()
  87. {
  88.     while (!m_viewList.IsEmpty())
  89.     {
  90.         CView* pView = (CView*)m_viewList.RemoveHead();
  91.         ASSERT_VALID(pView);
  92.         ASSERT_KINDOF(CView, pView);
  93.         pView->m_pDocument = NULL;
  94.     }
  95. }
  96.  
  97. /////////////////////////////////////////////////////////////////////////////
  98. // CDocument attributes, general services
  99.  
  100. void CDocument::SetTitle(LPCTSTR lpszTitle)
  101. {
  102.     m_strTitle = lpszTitle;
  103.     UpdateFrameCounts();        // will cause name change in views
  104. }
  105.  
  106. void CDocument::DeleteContents()
  107. {
  108. }
  109.  
  110. /////////////////////////////////////////////////////////////////////////////
  111. // Closing documents or views
  112.  
  113. void CDocument::OnChangedViewList()
  114. {
  115.     // if no more views on the document, delete ourself
  116.     // not called if directly closing the document or terminating the app
  117.     if (m_viewList.IsEmpty() && m_bAutoDelete)
  118.     {
  119.         OnCloseDocument();
  120.         return;
  121.     }
  122.  
  123.     // update the frame counts as needed
  124.     UpdateFrameCounts();
  125. }
  126.  
  127. void CDocument::UpdateFrameCounts()
  128.      // assumes 1 doc per frame
  129. {
  130.     // walk all frames of views (mark and sweep approach)
  131.     POSITION pos = GetFirstViewPosition();
  132.     while (pos != NULL)
  133.     {
  134.         CView* pView = GetNextView(pos);
  135.         ASSERT_VALID(pView);
  136.         ASSERT(::IsWindow(pView->m_hWnd));
  137.         if (pView->IsWindowVisible())   // Do not count invisible windows.
  138.         {
  139.             CFrameWnd* pFrame = pView->GetParentFrame();
  140.             if (pFrame != NULL)
  141.                 pFrame->m_nWindow = -1;     // unknown
  142.         }
  143.     }
  144.  
  145.     // now do it again counting the unique ones
  146.     int nFrames = 0;
  147.     pos = GetFirstViewPosition();
  148.     while (pos != NULL)
  149.     {
  150.         CView* pView = GetNextView(pos);
  151.         ASSERT_VALID(pView);
  152.         ASSERT(::IsWindow(pView->m_hWnd));
  153.         if (pView->IsWindowVisible())   // Do not count invisible windows.
  154.         {
  155.             CFrameWnd* pFrame = pView->GetParentFrame();
  156.             if (pFrame != NULL && pFrame->m_nWindow == -1)
  157.             {
  158.                 ASSERT_VALID(pFrame);
  159.                 // not yet counted (give it a 1 based number)
  160.                 pFrame->m_nWindow = ++nFrames;
  161.             }
  162.         }
  163.     }
  164.  
  165.     // lastly walk the frames and update titles (assume same order)
  166.     // go through frames updating the appropriate one
  167.     int iFrame = 1;
  168.     pos = GetFirstViewPosition();
  169.     while (pos != NULL)
  170.     {
  171.         CView* pView = GetNextView(pos);
  172.         ASSERT_VALID(pView);
  173.         ASSERT(::IsWindow(pView->m_hWnd));
  174.         if (pView->IsWindowVisible())   // Do not count invisible windows.
  175.         {
  176.             CFrameWnd* pFrame = pView->GetParentFrame();
  177.             if (pFrame != NULL && pFrame->m_nWindow == iFrame)
  178.             {
  179.                 ASSERT_VALID(pFrame);
  180.                 if (nFrames == 1)
  181.                     pFrame->m_nWindow = 0;      // the only one of its kind
  182.                 pFrame->OnUpdateFrameTitle(TRUE);
  183.                 iFrame++;
  184.             }
  185.         }
  186.     }
  187.     ASSERT(iFrame == nFrames + 1);
  188. }
  189.  
  190. BOOL CDocument::CanCloseFrame(CFrameWnd* pFrameArg)
  191.     // permission to close all views using this frame
  192.     //  (at least one of our views must be in this frame)
  193. {
  194.     ASSERT_VALID(pFrameArg);
  195.     UNUSED(pFrameArg);   // unused in release builds
  196.  
  197.     POSITION pos = GetFirstViewPosition();
  198.     while (pos != NULL)
  199.     {
  200.         CView* pView = GetNextView(pos);
  201.         ASSERT_VALID(pView);
  202.         CFrameWnd* pFrame = pView->GetParentFrame();
  203.         // assume frameless views are ok to close
  204.         if (pFrame != NULL)
  205.         {
  206.             // assumes 1 document per frame
  207.             ASSERT_VALID(pFrame);
  208.             if (pFrame->m_nWindow > 0)
  209.                 return TRUE;        // more than one frame refering to us
  210.         }
  211.     }
  212.  
  213.     // otherwise only one frame that we know about
  214.     return SaveModified();
  215. }
  216.  
  217. void CDocument::PreCloseFrame(CFrameWnd* /*pFrameArg*/)
  218. {
  219.     // default does nothing
  220. }
  221.  
  222. /////////////////////////////////////////////////////////////////////////////
  223. // File/Path commands
  224.  
  225. void CDocument::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU)
  226. {
  227.     // store the path fully qualified
  228.     TCHAR szFullPath[_MAX_PATH];
  229.     AfxFullPath(szFullPath, lpszPathName);
  230.     m_strPathName = szFullPath;
  231.     ASSERT(!m_strPathName.IsEmpty());       // must be set to something
  232.     m_bEmbedded = FALSE;
  233.     ASSERT_VALID(this);
  234.  
  235.     // set the document title based on path name
  236.     TCHAR szTitle[_MAX_FNAME];
  237.     if (AfxGetFileTitle(szFullPath, szTitle, _MAX_FNAME) == 0)
  238.         SetTitle(szTitle);
  239.  
  240.     // add it to the file MRU list
  241.     if (bAddToMRU)
  242.         AfxGetApp()->AddToRecentFileList(m_strPathName);
  243.  
  244.     ASSERT_VALID(this);
  245. }
  246.  
  247. /////////////////////////////////////////////////////////////////////////////
  248. // Standard file menu commands
  249.  
  250. void CDocument::OnFileClose()
  251. {
  252.     if (!SaveModified())
  253.         return;
  254.  
  255.     // shut it down
  256.     OnCloseDocument();
  257.         // this should destroy the document
  258. }
  259.  
  260. void CDocument::OnFileSave()
  261. {
  262.     DoFileSave();
  263. }
  264.  
  265. void CDocument::OnFileSaveAs()
  266. {
  267.     if (!DoSave(NULL))
  268.         TRACE0("Warning: File save-as failed.\n");
  269. }
  270.  
  271. BOOL CDocument::DoFileSave()
  272. {
  273.     DWORD dwAttrib = GetFileAttributes(m_strPathName);
  274.     if (dwAttrib & FILE_ATTRIBUTE_READONLY)
  275.     {
  276.         // we do not have read-write access or the file does not (now) exist
  277.         if (!DoSave(NULL))
  278.         {
  279.             TRACE0("Warning: File save with new name failed.\n");
  280.             return FALSE;
  281.         }
  282.     }
  283.     else
  284.     {
  285.         if (!DoSave(m_strPathName))
  286.         {
  287.             TRACE0("Warning: File save failed.\n");
  288.             return FALSE;
  289.         }
  290.     }
  291.     return TRUE;
  292. }
  293.  
  294. BOOL CDocument::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
  295.     // Save the document data to a file
  296.     // lpszPathName = path name where to save document file
  297.     // if lpszPathName is NULL then the user will be prompted (SaveAs)
  298.     // note: lpszPathName can be different than 'm_strPathName'
  299.     // if 'bReplace' is TRUE will change file name if successful (SaveAs)
  300.     // if 'bReplace' is FALSE will not change path name (SaveCopyAs)
  301. {
  302.     CString newName = lpszPathName;
  303.     if (newName.IsEmpty())
  304.     {
  305.         CDocTemplate* pTemplate = GetDocTemplate();
  306.         ASSERT(pTemplate != NULL);
  307.  
  308.         newName = m_strPathName;
  309.         if (bReplace && newName.IsEmpty())
  310.         {
  311.             newName = m_strTitle;
  312. #ifndef _MAC
  313.             // check for dubious filename
  314.             int iBad = newName.FindOneOf(_T(" #%;/\\"));
  315. #else
  316.             int iBad = newName.FindOneOf(_T(":"));
  317. #endif
  318.             if (iBad != -1)
  319.                 newName.ReleaseBuffer(iBad);
  320.  
  321. #ifndef _MAC
  322.             // append the default suffix if there is one
  323.             CString strExt;
  324.             if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) &&
  325.               !strExt.IsEmpty())
  326.             {
  327.                 ASSERT(strExt[0] == '.');
  328.                 newName += strExt;
  329.             }
  330. #endif
  331.         }
  332.  
  333.         if (!AfxGetApp()->DoPromptFileName(newName,
  334.           bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY,
  335.           OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate))
  336.             return FALSE;       // don't even attempt to save
  337.     }
  338.  
  339.     CWaitCursor wait;
  340.  
  341.     if (!OnSaveDocument(newName))
  342.     {
  343.         if (lpszPathName == NULL)
  344.         {
  345.             // be sure to delete the file
  346.             TRY
  347.             {
  348.                 CFile::Remove(newName);
  349.             }
  350.             CATCH_ALL(e)
  351.             {
  352.                 TRACE0("Warning: failed to delete file after failed SaveAs.\n");
  353.                 DELETE_EXCEPTION(e);
  354.             }
  355.             END_CATCH_ALL
  356.         }
  357.         return FALSE;
  358.     }
  359.  
  360.     // reset the title and change the document name
  361.     if (bReplace)
  362.         SetPathName(newName);
  363.  
  364.     return TRUE;        // success
  365. }
  366.  
  367. BOOL CDocument::SaveModified()
  368. {
  369.     if (!IsModified())
  370.         return TRUE;        // ok to continue
  371.  
  372. #ifdef _MAC
  373.     CWinApp* pApp = AfxGetApp();
  374.     if (pApp->m_nSaveOption == CWinApp::saveNo)
  375.         return TRUE;
  376.     else if (pApp->m_nSaveOption == CWinApp::saveYes)
  377.     {
  378.         DoFileSave();
  379.         return TRUE;
  380.     }
  381. #endif
  382.  
  383.     // get name/title of document
  384.     CString name;
  385.     if (m_strPathName.IsEmpty())
  386.     {
  387.         // get name based on caption
  388.         name = m_strTitle;
  389.         if (name.IsEmpty())
  390.             VERIFY(name.LoadString(AFX_IDS_UNTITLED));
  391.     }
  392.     else
  393.     {
  394.         // get name based on file title of path name
  395.         name = m_strPathName;
  396.         if (afxData.bMarked4)
  397.         {
  398.             AfxGetFileTitle(m_strPathName, name.GetBuffer(_MAX_PATH), _MAX_PATH);
  399.             name.ReleaseBuffer();
  400.         }
  401.     }
  402.  
  403. #ifdef _MAC
  404.     AfxGetFileTitle(m_strPathName, name.GetBuffer(_MAX_FNAME), _MAX_FNAME);
  405.     name.ReleaseBuffer();
  406. #endif
  407.  
  408.     CString prompt;
  409. #ifndef _MAC
  410.     AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, name);
  411.     switch (AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_SAVE))
  412. #else
  413.     AfxFormatString2(prompt, AFX_IDP_ASK_TO_SAVE, AfxGetAppName(), name);
  414.     switch (AfxMessageBox(prompt, MB_SAVEDONTSAVECANCEL, AFX_IDP_ASK_TO_SAVE))
  415. #endif
  416.     {
  417.     case IDCANCEL:
  418.         return FALSE;       // don't continue
  419.  
  420.     case IDYES:
  421.         // If so, either Save or Update, as appropriate
  422.         if (!DoFileSave())
  423.             return FALSE;       // don't continue
  424.         break;
  425.  
  426.     case IDNO:
  427.         // If not saving changes, revert the document
  428.         break;
  429.  
  430.     default:
  431.         ASSERT(FALSE);
  432.         break;
  433.     }
  434.     return TRUE;    // keep going
  435. }
  436.  
  437. HMENU CDocument::GetDefaultMenu()
  438. {
  439.     return NULL;    // just use original default
  440. }
  441.  
  442. HACCEL CDocument::GetDefaultAccelerator()
  443. {
  444.     return NULL;    // just use original default
  445. }
  446.  
  447. void CDocument::ReportSaveLoadException(LPCTSTR lpszPathName,
  448.     CException* e, BOOL bSaving, UINT nIDPDefault)
  449. {
  450.     UINT nIDP = nIDPDefault;
  451.     UINT nHelpContext = nIDPDefault;
  452.     CString prompt;
  453.  
  454.     if (e != NULL)
  455.     {
  456.         ASSERT_VALID(e);
  457.         if (e->IsKindOf(RUNTIME_CLASS(CUserException)))
  458.             return; // already reported
  459.  
  460.         if (e->IsKindOf(RUNTIME_CLASS(CArchiveException)))
  461.         {
  462.             switch (((CArchiveException*)e)->m_cause)
  463.             {
  464.             case CArchiveException::badSchema:
  465.             case CArchiveException::badClass:
  466.             case CArchiveException::badIndex:
  467.             case CArchiveException::endOfFile:
  468.                 nIDP = AFX_IDP_FAILED_INVALID_FORMAT;
  469.                 break;
  470.             default:
  471.                 break;
  472.             }
  473.         }
  474.         else if (e->IsKindOf(RUNTIME_CLASS(CFileException)))
  475.         {
  476.             TRACE1("Reporting file I/O exception on Save/Load with lOsError = $%lX.\n",
  477.                 ((CFileException*)e)->m_lOsError);
  478.  
  479.             CFileException* pFileException = (CFileException*)e;
  480.             if (pFileException->m_strFileName.IsEmpty())
  481.                 pFileException->m_strFileName = lpszPathName;
  482.  
  483.             LPTSTR lpszMessage = prompt.GetBuffer(255);
  484.             ASSERT(lpszMessage != NULL);
  485.             if (!e->GetErrorMessage(lpszMessage, 256, &nHelpContext))
  486.             {
  487.                 switch (((CFileException*)e)->m_cause)
  488.                 {
  489.                     case CFileException::fileNotFound:
  490.                     case CFileException::badPath:
  491.                         nIDP = AFX_IDP_FAILED_INVALID_PATH;
  492.                         break;
  493.                     case CFileException::diskFull:
  494.                         nIDP = AFX_IDP_FAILED_DISK_FULL;
  495.                         break;
  496.                     case CFileException::accessDenied:
  497.                         nIDP = bSaving ? AFX_IDP_FAILED_ACCESS_WRITE :
  498.                                 AFX_IDP_FAILED_ACCESS_READ;
  499.                         break;
  500.  
  501.                     case CFileException::badSeek:
  502.                     case CFileException::generic:
  503.                     case CFileException::tooManyOpenFiles:
  504.                     case CFileException::invalidFile:
  505.                     case CFileException::hardIO:
  506.                     case CFileException::directoryFull:
  507.                         break;
  508.  
  509.                     default:
  510.                         break;
  511.                 }
  512.             }
  513.             prompt.ReleaseBuffer();
  514.         }
  515.     }
  516.  
  517.     if (prompt.IsEmpty())
  518.     {
  519. #ifndef _MAC
  520.         TCHAR szTitle[_MAX_PATH];
  521.         if (afxData.bMarked4)
  522.             AfxGetFileTitle(lpszPathName, szTitle, _countof(szTitle));
  523.         else
  524.             lstrcpyn(szTitle, lpszPathName, _countof(szTitle));
  525.         AfxFormatString1(prompt, nIDP, szTitle);
  526. #else
  527.         TCHAR szTitle[_MAX_FNAME];
  528.         AfxGetFileTitle(lpszPathName, szTitle, _MAX_FNAME);
  529.         AfxFormatString1(prompt, nIDP, szTitle);
  530. #endif
  531.     }
  532.  
  533.     AfxMessageBox(prompt, MB_ICONEXCLAMATION, nHelpContext);
  534. }
  535.  
  536. /////////////////////////////////////////////////////////////////////////////
  537. // File operations (default uses CDocument::Serialize)
  538.  
  539. BOOL CMirrorFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,
  540.     CFileException* pError)
  541. {
  542.     ASSERT(lpszFileName != NULL);
  543.     m_strMirrorName.Empty();
  544.  
  545. #ifndef _MAC
  546.     CFileStatus status;
  547.     if (nOpenFlags & CFile::modeCreate) //opened for writing
  548.     {
  549.         if (CFile::GetStatus(lpszFileName, status))
  550.         {
  551.             CString strRoot;
  552.             AfxGetRoot(lpszFileName, strRoot);
  553.  
  554.             DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
  555.             int nBytes = 0;
  556.             if (GetDiskFreeSpace(strRoot, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus,
  557.                 &dwTotalClus))
  558.             {
  559.                 nBytes = dwFreeClus*dwSecPerClus*dwBytesPerSec;
  560.             }
  561.             if (nBytes > 2*status.m_size) // at least 2x free space avail
  562.             {
  563.                 // get the directory for the file
  564.                 TCHAR szPath[_MAX_PATH];
  565.                 LPTSTR lpszName;
  566.                 GetFullPathName(lpszFileName, _MAX_PATH, szPath, &lpszName);
  567.                 *lpszName = NULL;
  568.  
  569.                 //let's create a temporary file name
  570.                 GetTempFileName(szPath, _T("MFC"), 0,
  571.                     m_strMirrorName.GetBuffer(_MAX_PATH+1));
  572.                 m_strMirrorName.ReleaseBuffer();
  573.             }
  574.         }
  575.     }
  576. #endif
  577.  
  578.     if (!m_strMirrorName.IsEmpty() &&
  579.         CFile::Open(m_strMirrorName, nOpenFlags, pError))
  580.     {
  581.         m_strFileName = lpszFileName;
  582. #ifndef _MAC
  583.         FILETIME ftCreate, ftAccess, ftModify;
  584.         if (::GetFileTime((HANDLE)m_hFile, &ftCreate, &ftAccess, &ftModify))
  585.         {
  586.             AfxTimeToFileTime(status.m_ctime, &ftCreate);
  587.             SetFileTime((HANDLE)m_hFile, &ftCreate, &ftAccess, &ftModify);
  588.         }
  589.  
  590.         DWORD dwLength = 0;
  591.         PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
  592.         if (GetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION,
  593.             NULL, dwLength, &dwLength))
  594.         {
  595.             pSecurityDescriptor = (PSECURITY_DESCRIPTOR) new BYTE[dwLength];
  596.             if (::GetFileSecurity(lpszFileName, DACL_SECURITY_INFORMATION,
  597.                 pSecurityDescriptor, dwLength, &dwLength))
  598.             {
  599.                 SetFileSecurity(m_strMirrorName, DACL_SECURITY_INFORMATION, pSecurityDescriptor);
  600.             }
  601.             delete[] (BYTE*)pSecurityDescriptor;
  602.         }
  603. #endif
  604.         return TRUE;
  605.     }
  606.     m_strMirrorName.Empty();
  607.     return CFile::Open(lpszFileName, nOpenFlags, pError);
  608. }
  609.  
  610. void CMirrorFile::Abort()
  611. {
  612.     CFile::Abort();
  613.     if (!m_strMirrorName.IsEmpty())
  614.         CFile::Remove(m_strMirrorName);
  615. }
  616.  
  617. void CMirrorFile::Close()
  618. {
  619.     CString m_strName = m_strFileName; //file close empties string
  620.     CFile::Close();
  621.     if (!m_strMirrorName.IsEmpty())
  622.     {
  623.         CFile::Remove(m_strName);
  624.         CFile::Rename(m_strMirrorName, m_strName);
  625.     }
  626. }
  627.  
  628. CFile* CDocument::GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,
  629.     CFileException* pError)
  630. {
  631.     CMirrorFile* pFile = new CMirrorFile;
  632.     ASSERT(pFile != NULL);
  633.     if (!pFile->Open(lpszFileName, nOpenFlags, pError))
  634.     {
  635.         delete pFile;
  636.         pFile = NULL;
  637.     }
  638.     return pFile;
  639. }
  640.  
  641. void CDocument::ReleaseFile(CFile* pFile, BOOL bAbort)
  642. {
  643.     ASSERT_KINDOF(CFile, pFile);
  644.     if (bAbort)
  645.         pFile->Abort(); // will not throw an exception
  646.     else
  647.         pFile->Close();
  648.     delete pFile;
  649. }
  650.  
  651. BOOL CDocument::OnNewDocument()
  652. {
  653.     if (IsModified())
  654.         TRACE0("Warning: OnNewDocument replaces an unsaved document.\n");
  655.  
  656.     DeleteContents();
  657.     m_strPathName.Empty();      // no path name yet
  658.     SetModifiedFlag(FALSE);     // make clean
  659.  
  660.     return TRUE;
  661. }
  662.  
  663. BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)
  664. {
  665.     if (IsModified())
  666.         TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n");
  667.  
  668.     CFileException fe;
  669.     CFile* pFile = GetFile(lpszPathName,
  670.         CFile::modeRead|CFile::shareDenyWrite, &fe);
  671.     if (pFile == NULL)
  672.     {
  673.         ReportSaveLoadException(lpszPathName, &fe,
  674.             FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  675.         return FALSE;
  676.     }
  677.  
  678.     DeleteContents();
  679.     SetModifiedFlag();  // dirty during de-serialize
  680.  
  681.     CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
  682.     loadArchive.m_pDocument = this;
  683.     loadArchive.m_bForceFlat = FALSE;
  684.     TRY
  685.     {
  686.         CWaitCursor wait;
  687.         if (pFile->GetLength() != 0)
  688.             Serialize(loadArchive);     // load me
  689.         loadArchive.Close();
  690.         ReleaseFile(pFile, FALSE);
  691.     }
  692.     CATCH_ALL(e)
  693.     {
  694.         ReleaseFile(pFile, TRUE);
  695.         DeleteContents();   // remove failed contents
  696.  
  697.         TRY
  698.         {
  699.             ReportSaveLoadException(lpszPathName, e,
  700.                 FALSE, AFX_IDP_FAILED_TO_OPEN_DOC);
  701.         }
  702.         END_TRY
  703.         DELETE_EXCEPTION(e);
  704.         return FALSE;
  705.     }
  706.     END_CATCH_ALL
  707.  
  708.     SetModifiedFlag(FALSE);     // start off with unmodified
  709.  
  710. #ifdef _MAC
  711.     WIN32_FIND_DATA fileData;
  712.     HANDLE hFind = FindFirstFile(lpszPathName, &fileData);
  713.     ASSERT(hFind != (HANDLE)-1);
  714.     VERIFY(FindClose(hFind));
  715.  
  716.     if ((fileData.wFinderFlags & kIsStationery) != 0)
  717.     {
  718.         SetModifiedFlag();
  719.         if (m_pDocTemplate != NULL)
  720.             m_pDocTemplate->SetDefaultTitle(this);
  721.  
  722.         // normally SetPathName would do this, but we aren't calling it
  723.         AfxGetApp()->AddToRecentFileList(lpszPathName);
  724.     }
  725. #endif
  726.  
  727.     return TRUE;
  728. }
  729.  
  730. BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName)
  731. {
  732.     CFileException fe;
  733.     CFile* pFile = NULL;
  734.     pFile = GetFile(lpszPathName, CFile::modeCreate |
  735.         CFile::modeReadWrite | CFile::shareExclusive, &fe);
  736.  
  737.     if (pFile == NULL)
  738.     {
  739.         ReportSaveLoadException(lpszPathName, &fe,
  740.             TRUE, AFX_IDP_INVALID_FILENAME);
  741.         return FALSE;
  742.     }
  743.  
  744.     CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete);
  745.     saveArchive.m_pDocument = this;
  746.     saveArchive.m_bForceFlat = FALSE;
  747.     TRY
  748.     {
  749.         CWaitCursor wait;
  750.         Serialize(saveArchive);     // save me
  751.         saveArchive.Close();
  752.         ReleaseFile(pFile, FALSE);
  753.     }
  754.     CATCH_ALL(e)
  755.     {
  756.         ReleaseFile(pFile, TRUE);
  757.  
  758.         TRY
  759.         {
  760.             ReportSaveLoadException(lpszPathName, e,
  761.                 TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);
  762.         }
  763.         END_TRY
  764.         DELETE_EXCEPTION(e);
  765.         return FALSE;
  766.     }
  767.     END_CATCH_ALL
  768.  
  769. #ifdef _MAC
  770.     RecordDataFileOwner(lpszPathName, AfxGetAppName());
  771. #endif
  772.  
  773.     SetModifiedFlag(FALSE);     // back to unmodified
  774.  
  775.     return TRUE;        // success
  776. }
  777.  
  778. #ifdef _MAC
  779. void CDocument::RecordDataFileOwner(LPCTSTR lpszPathName, LPCTSTR lpszAppName)
  780. {
  781.     FSSpec fss;
  782.  
  783.     if (UnwrapFile(lpszPathName, &fss))
  784.     {
  785.         FInfo finfo;
  786.         short refNum;
  787.  
  788.         if (FSpGetFInfo(&fss, &finfo) == noErr)
  789.             FSpCreateResFile(&fss, finfo.fdCreator, finfo.fdType, smSystemScript);
  790.         refNum = FSpOpenResFile(&fss, fsRdWrPerm);
  791.  
  792.         if (ResError() == noErr)
  793.         {
  794.             Handle hv = Get1Resource('STR ', -16396);
  795.             if (hv == NULL)
  796.             {
  797.                 hv = NewHandle(0);
  798.                 if (hv != NULL)
  799.                     AddResource(hv, 'STR ', -16396, "\p");
  800.             }
  801.  
  802.             if (hv != NULL)
  803.             {
  804.                 int cbAppName = lstrlen(lpszAppName);
  805.  
  806.                 SetHandleSize(hv, cbAppName + 1);
  807.                 if (MemError() == noErr)
  808.                 {
  809.                     BlockMove(lpszAppName, *hv + 1, cbAppName);
  810.                     **hv = (char) cbAppName;
  811.                 }
  812.  
  813.                 ChangedResource(hv);
  814.                 WriteResource(hv);
  815.             }
  816.  
  817.             CloseResFile(refNum);
  818.         }
  819.     }
  820. }
  821. #endif
  822.  
  823. void CDocument::OnCloseDocument()
  824.     // must close all views now (no prompting) - usually destroys this
  825. {
  826.     // destroy all frames viewing this document
  827.     // the last destroy may destroy us
  828.     BOOL bAutoDelete = m_bAutoDelete;
  829.     m_bAutoDelete = FALSE;  // don't destroy document while closing views
  830.     while (!m_viewList.IsEmpty())
  831.     {
  832.         // get frame attached to the view
  833.         CView* pView = (CView*)m_viewList.GetHead();
  834.         ASSERT_VALID(pView);
  835.         CFrameWnd* pFrame = pView->GetParentFrame();
  836.         ASSERT_VALID(pFrame);
  837.  
  838.         // and close it
  839.         PreCloseFrame(pFrame);
  840.         pFrame->DestroyWindow();
  841.             // will destroy the view as well
  842.     }
  843.     m_bAutoDelete = bAutoDelete;
  844.  
  845.     // clean up contents of document before destroying the document itself
  846.     DeleteContents();
  847.  
  848.     // delete the document if necessary
  849.     if (m_bAutoDelete)
  850.         delete this;
  851. }
  852.  
  853. void CDocument::OnIdle()
  854. {
  855.     // default does nothing
  856. }
  857.  
  858. /////////////////////////////////////////////////////////////////////////////
  859. // View operations
  860.  
  861. void CDocument::AddView(CView* pView)
  862. {
  863.     ASSERT_VALID(pView);
  864.     ASSERT(pView->m_pDocument == NULL); // must not be already attached
  865.     ASSERT(m_viewList.Find(pView, NULL) == NULL);   // must not be in list
  866.  
  867.     m_viewList.AddTail(pView);
  868.     ASSERT(pView->m_pDocument == NULL); // must be un-attached
  869.     pView->m_pDocument = this;
  870.  
  871.     OnChangedViewList();    // must be the last thing done to the document
  872. }
  873.  
  874. void CDocument::RemoveView(CView* pView)
  875. {
  876.     ASSERT_VALID(pView);
  877.     ASSERT(pView->m_pDocument == this); // must be attached to us
  878.  
  879.     m_viewList.RemoveAt(m_viewList.Find(pView));
  880.     pView->m_pDocument = NULL;
  881.  
  882.     OnChangedViewList();    // must be the last thing done to the document
  883. }
  884.  
  885. POSITION CDocument::GetFirstViewPosition() const
  886. {
  887.     return m_viewList.GetHeadPosition();
  888. }
  889.  
  890. CView* CDocument::GetNextView(POSITION& rPosition) const
  891. {
  892.     ASSERT(rPosition != BEFORE_START_POSITION);
  893.         // use CDocument::GetFirstViewPosition instead !
  894.     if (rPosition == NULL)
  895.         return NULL;    // nothing left
  896.     CView* pView = (CView*)m_viewList.GetNext(rPosition);
  897.     ASSERT_KINDOF(CView, pView);
  898.     return pView;
  899. }
  900.  
  901. void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint)
  902.     // walk through all views
  903. {
  904.     ASSERT(pSender == NULL || !m_viewList.IsEmpty());
  905.         // must have views if sent by one of them
  906.  
  907.     POSITION pos = GetFirstViewPosition();
  908.     while (pos != NULL)
  909.     {
  910.         CView* pView = GetNextView(pos);
  911.         ASSERT_VALID(pView);
  912.         if (pView != pSender)
  913.             pView->OnUpdate(pSender, lHint, pHint);
  914.     }
  915. }
  916.  
  917. void CDocument::SendInitialUpdate()
  918.     // walk through all views and call OnInitialUpdate
  919. {
  920.     POSITION pos = GetFirstViewPosition();
  921.     while (pos != NULL)
  922.     {
  923.         CView* pView = GetNextView(pos);
  924.         ASSERT_VALID(pView);
  925.         pView->OnInitialUpdate();
  926.     }
  927. }
  928.  
  929. /////////////////////////////////////////////////////////////////////////////
  930. // command routing
  931.  
  932. BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  933.     AFX_CMDHANDLERINFO* pHandlerInfo)
  934. {
  935.     if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  936.         return TRUE;
  937.  
  938.     // otherwise check template
  939.     if (m_pDocTemplate != NULL &&
  940.       m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  941.         return TRUE;
  942.  
  943.     return FALSE;
  944. }
  945.  
  946. /////////////////////////////////////////////////////////////////////////////
  947. // CDocument diagnostics
  948.  
  949. #ifdef _DEBUG
  950. void CDocument::Dump(CDumpContext& dc) const
  951. {
  952.     CObject::Dump(dc);
  953.  
  954.     dc << "m_strTitle = " << m_strTitle;
  955.     dc << "\nm_strPathName = " << m_strPathName;
  956.     dc << "\nm_bModified = " << m_bModified;
  957.     dc << "\nm_pDocTemplate = " << (void*)m_pDocTemplate;
  958.  
  959.     if (dc.GetDepth() > 0)
  960.     {
  961.         POSITION pos = GetFirstViewPosition();
  962.         while (pos != NULL)
  963.         {
  964.             CView* pView = GetNextView(pos);
  965.             dc << "\nwith view " << (void*)pView;
  966.         }
  967.     }
  968.  
  969.     dc << "\n";
  970. }
  971.  
  972. void CDocument::AssertValid() const
  973. {
  974.     CObject::AssertValid();
  975.  
  976.     POSITION pos = GetFirstViewPosition();
  977.     while (pos != NULL)
  978.     {
  979.         CView* pView = GetNextView(pos);
  980.         ASSERT_VALID(pView);
  981.     }
  982. }
  983. #endif //_DEBUG
  984.  
  985. #ifdef AFX_INIT_SEG
  986. #pragma code_seg(AFX_INIT_SEG)
  987. #endif
  988.  
  989. IMPLEMENT_DYNAMIC(CDocument, CCmdTarget)
  990.  
  991. /////////////////////////////////////////////////////////////////////////////
  992.