📄 doccore.cpp
字号:
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#include "io.h" // for _access
#ifdef AFX_CORE2_SEG
#pragma code_seg(AFX_CORE2_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CDocument
BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
//{{AFX_MSG_MAP(CDocument)
ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
ON_COMMAND(ID_FILE_SAVE, OnFileSave)
ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDocument construction/destruction
CDocument::CDocument()
{
m_pDocTemplate = NULL;
m_bModified = FALSE;
m_bAutoDelete = TRUE; // default to auto delete document
m_bEmbedded = FALSE; // default to file-based document
ASSERT(m_viewList.IsEmpty());
}
CDocument::~CDocument()
{
// do not call DeleteContents here !
#ifdef _DEBUG
if (IsModified())
TRACE0("Warning: destroying an unsaved document.\n");
#endif
// there should be no views left!
DisconnectViews();
ASSERT(m_viewList.IsEmpty());
if (m_pDocTemplate != NULL)
m_pDocTemplate->RemoveDocument(this);
ASSERT(m_pDocTemplate == NULL); // must be detached
}
void CDocument::OnFinalRelease()
{
ASSERT_VALID(this);
OnCloseDocument(); // may 'delete this'
}
void CDocument::DisconnectViews()
{
while (!m_viewList.IsEmpty())
{
CView* pView = (CView*)m_viewList.RemoveHead();
ASSERT_VALID(pView);
ASSERT_KINDOF(CView, pView);
pView->m_pDocument = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
// CDocument attributes, general services
void CDocument::SetTitle(LPCTSTR lpszTitle)
{
m_strTitle = lpszTitle;
UpdateFrameCounts(); // will cause name change in views
}
void CDocument::DeleteContents()
{
}
/////////////////////////////////////////////////////////////////////////////
// Closing documents or views
void CDocument::OnChangedViewList()
{
// if no more views on the document, delete ourself
// not called if directly closing the document or terminating the app
if (m_viewList.IsEmpty() && m_bAutoDelete)
{
OnCloseDocument();
return;
}
// update the frame counts as needed
UpdateFrameCounts();
}
void CDocument::UpdateFrameCounts()
// assumes 1 doc per frame
{
// walk all frames of views (mark and sweep approach)
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
ASSERT(::IsWindow(pView->m_hWnd));
if (pView->IsWindowVisible()) // Do not count invisible windows.
{
CFrameWnd* pFrame = pView->GetParentFrame();
if (pFrame != NULL)
pFrame->m_nWindow = -1; // unknown
}
}
// now do it again counting the unique ones
int nFrames = 0;
pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
ASSERT(::IsWindow(pView->m_hWnd));
if (pView->IsWindowVisible()) // Do not count invisible windows.
{
CFrameWnd* pFrame = pView->GetParentFrame();
if (pFrame != NULL && pFrame->m_nWindow == -1)
{
ASSERT_VALID(pFrame);
// not yet counted (give it a 1 based number)
pFrame->m_nWindow = ++nFrames;
}
}
}
// lastly walk the frames and update titles (assume same order)
// go through frames updating the appropriate one
int iFrame = 1;
pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
ASSERT(::IsWindow(pView->m_hWnd));
if (pView->IsWindowVisible()) // Do not count invisible windows.
{
CFrameWnd* pFrame = pView->GetParentFrame();
if (pFrame != NULL && pFrame->m_nWindow == iFrame)
{
ASSERT_VALID(pFrame);
if (nFrames == 1)
pFrame->m_nWindow = 0; // the only one of its kind
pFrame->OnUpdateFrameTitle(TRUE);
iFrame++;
}
}
}
ASSERT(iFrame == nFrames + 1);
}
BOOL CDocument::CanCloseFrame(CFrameWnd* pFrameArg)
// permission to close all views using this frame
// (at least one of our views must be in this frame)
{
ASSERT_VALID(pFrameArg);
UNUSED(pFrameArg); // unused in release builds
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
ASSERT_VALID(pView);
CFrameWnd* pFrame = pView->GetParentFrame();
// assume frameless views are ok to close
if (pFrame != NULL)
{
// assumes 1 document per frame
ASSERT_VALID(pFrame);
if (pFrame->m_nWindow > 0)
return TRUE; // more than one frame refering to us
}
}
// otherwise only one frame that we know about
return SaveModified();
}
void CDocument::PreCloseFrame(CFrameWnd* /*pFrameArg*/)
{
// default does nothing
}
/////////////////////////////////////////////////////////////////////////////
// File/Path commands
void CDocument::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU)
{
// store the path fully qualified
TCHAR szFullPath[_MAX_PATH];
AfxFullPath(szFullPath, lpszPathName);
m_strPathName = szFullPath;
ASSERT(!m_strPathName.IsEmpty()); // must be set to something
m_bEmbedded = FALSE;
ASSERT_VALID(this);
// set the document title based on path name
TCHAR szTitle[_MAX_FNAME];
if (AfxGetFileTitle(szFullPath, szTitle, _MAX_FNAME) == 0)
SetTitle(szTitle);
// add it to the file MRU list
if (bAddToMRU)
AfxGetApp()->AddToRecentFileList(m_strPathName);
ASSERT_VALID(this);
}
/////////////////////////////////////////////////////////////////////////////
// Standard file menu commands
void CDocument::OnFileClose()
{
if (!SaveModified())
return;
// shut it down
OnCloseDocument();
// this should destroy the document
}
void CDocument::OnFileSave()
{
DoFileSave();
}
void CDocument::OnFileSaveAs()
{
if (!DoSave(NULL))
TRACE0("Warning: File save-as failed.\n");
}
BOOL CDocument::DoFileSave()
{
DWORD dwAttrib = GetFileAttributes(m_strPathName);
if (dwAttrib & FILE_ATTRIBUTE_READONLY)
{
// we do not have read-write access or the file does not (now) exist
if (!DoSave(NULL))
{
TRACE0("Warning: File save with new name failed.\n");
return FALSE;
}
}
else
{
if (!DoSave(m_strPathName))
{
TRACE0("Warning: File save failed.\n");
return FALSE;
}
}
return TRUE;
}
BOOL CDocument::DoSave(LPCTSTR lpszPathName, BOOL bReplace)
// Save the document data to a file
// lpszPathName = path name where to save document file
// if lpszPathName is NULL then the user will be prompted (SaveAs)
// note: lpszPathName can be different than 'm_strPathName'
// if 'bReplace' is TRUE will change file name if successful (SaveAs)
// if 'bReplace' is FALSE will not change path name (SaveCopyAs)
{
CString newName = lpszPathName;
if (newName.IsEmpty())
{
CDocTemplate* pTemplate = GetDocTemplate();
ASSERT(pTemplate != NULL);
newName = m_strPathName;
if (bReplace && newName.IsEmpty())
{
newName = m_strTitle;
// check for dubious filename
int iBad = newName.FindOneOf(_T(" #%;/\\"));
if (iBad != -1)
newName.ReleaseBuffer(iBad);
// append the default suffix if there is one
CString strExt;
if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) &&
!strExt.IsEmpty())
{
ASSERT(strExt[0] == '.');
newName += strExt;
}
}
if (!AfxGetApp()->DoPromptFileName(newName,
bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY,
OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, pTemplate))
return FALSE; // don't even attempt to save
}
CWaitCursor wait;
if (!OnSaveDocument(newName))
{
if (lpszPathName == NULL)
{
// be sure to delete the file
TRY
{
CFile::Remove(newName);
}
CATCH_ALL(e)
{
TRACE0("Warning: failed to delete file after failed SaveAs.\n");
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
}
return FALSE;
}
// reset the title and change the document name
if (bReplace)
SetPathName(newName);
return TRUE; // success
}
BOOL CDocument::SaveModified()
{
if (!IsModified())
return TRUE; // ok to continue
// get name/title of document
CString name;
if (m_strPathName.IsEmpty())
{
// get name based on caption
name = m_strTitle;
if (name.IsEmpty())
VERIFY(name.LoadString(AFX_IDS_UNTITLED));
}
else
{
// get name based on file title of path name
name = m_strPathName;
if (afxData.bMarked4)
{
AfxGetFileTitle(m_strPathName, name.GetBuffer(_MAX_PATH), _MAX_PATH);
name.ReleaseBuffer();
}
}
CString prompt;
AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, name);
switch (AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_SAVE))
{
case IDCANCEL:
return FALSE; // don't continue
case IDYES:
// If so, either Save or Update, as appropriate
if (!DoFileSave())
return FALSE; // don't continue
break;
case IDNO:
// If not saving changes, revert the document
break;
default:
ASSERT(FALSE);
break;
}
return TRUE; // keep going
}
HMENU CDocument::GetDefaultMenu()
{
return NULL; // just use original default
}
HACCEL CDocument::GetDefaultAccelerator()
{
return NULL; // just use original default
}
void CDocument::ReportSaveLoadException(LPCTSTR lpszPathName,
CException* e, BOOL bSaving, UINT nIDPDefault)
{
UINT nIDP = nIDPDefault;
UINT nHelpContext = nIDPDefault;
CString prompt;
if (e != NULL)
{
ASSERT_VALID(e);
if (e->IsKindOf(RUNTIME_CLASS(CUserException)))
return; // already reported
if (e->IsKindOf(RUNTIME_CLASS(CArchiveException)))
{
switch (((CArchiveException*)e)->m_cause)
{
case CArchiveException::badSchema:
case CArchiveException::badClass:
case CArchiveException::badIndex:
case CArchiveException::endOfFile:
nIDP = AFX_IDP_FAILED_INVALID_FORMAT;
break;
default:
break;
}
}
else if (e->IsKindOf(RUNTIME_CLASS(CFileException)))
{
TRACE1("Reporting file I/O exception on Save/Load with lOsError = $%lX.\n",
((CFileException*)e)->m_lOsError);
CFileException* pFileException = (CFileException*)e;
if (pFileException->m_strFileName.IsEmpty())
pFileException->m_strFileName = lpszPathName;
LPTSTR lpszMessage = prompt.GetBuffer(255);
ASSERT(lpszMessage != NULL);
if (!e->GetErrorMessage(lpszMessage, 256, &nHelpContext))
{
switch (((CFileException*)e)->m_cause)
{
case CFileException::fileNotFound:
case CFileException::badPath:
nIDP = AFX_IDP_FAILED_INVALID_PATH;
break;
case CFileException::diskFull:
nIDP = AFX_IDP_FAILED_DISK_FULL;
break;
case CFileException::accessDenied:
nIDP = bSaving ? AFX_IDP_FAILED_ACCESS_WRITE :
AFX_IDP_FAILED_ACCESS_READ;
break;
case CFileException::badSeek:
case CFileException::generic:
case CFileException::tooManyOpenFiles:
case CFileException::invalidFile:
case CFileException::hardIO:
case CFileException::directoryFull:
break;
default:
break;
}
}
prompt.ReleaseBuffer();
}
}
if (prompt.IsEmpty())
{
TCHAR szTitle[_MAX_PATH];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -