home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / doccore.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  24KB  |  951 lines

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