doccore.cpp 23 KB

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