// wordpdoc.cpp : implementation of the CWordPadDoc class // // Copyright (C) 1992-1999 Microsoft Corporation // All rights reserved. #include "stdafx.h" #include "wordpad.h" #include "wordpdoc.h" #include "wordpvw.h" #include "cntritem.h" #include "srvritem.h" #include "formatba.h" #include "mainfrm.h" #include "ipframe.h" #include "helpids.h" #include "strings.h" #include "unitspag.h" #include "docopt.h" #include "optionsh.h" #include "multconv.h" #include "fixhelp.h" BOOL AskAboutFormatLoss(CWordPadDoc *pDoc) ; // // These defines are from ..\shell\userpri\uconvert.h // #define REVERSE_BYTE_ORDER_MARK 0xFFFE #define BYTE_ORDER_MARK 0xFEFF BOOL CheckForUnicodeTextFile(LPCTSTR lpszPathName) ; #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif extern BOOL AFXAPI AfxFullPath(LPTSTR lpszPathOut, LPCTSTR lpszFileIn); extern UINT AFXAPI AfxGetFileTitle(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax); #ifndef OFN_EXPLORER #define OFN_EXPLORER 0x00080000L #endif // // This small class implements the "This is an unsupported save format" dialog. // It's main purpose is to provide a place to hang the "always convert to RTF" // checkbox. // class UnsupportedSaveFormatDialog : public CDialog { public: UnsupportedSaveFormatDialog() : CDialog(TEXT("UnsupportedSaveFormatDialog")), m_always_convert_to_rtf(false) { } BOOL ShouldAlwaysConvertToRTF() {return m_always_convert_to_rtf;} protected: BOOL m_always_convert_to_rtf; void DoDataExchange(CDataExchange *pDX) { CDialog::DoDataExchange(pDX); DDX_Check(pDX, IDC_ALWAYS_RTF, m_always_convert_to_rtf); } }; ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc IMPLEMENT_DYNCREATE(CWordPadDoc, CRichEdit2Doc) BEGIN_MESSAGE_MAP(CWordPadDoc, CRichEdit2Doc) //{{AFX_MSG_MAP(CWordPadDoc) ON_COMMAND(ID_VIEW_OPTIONS, OnViewOptions) ON_UPDATE_COMMAND_UI(ID_OLE_VERB_POPUP, OnUpdateOleVerbPopup) ON_COMMAND(ID_FILE_SEND_MAIL, OnFileSendMail) ON_UPDATE_COMMAND_UI(ID_FILE_NEW, OnUpdateIfEmbedded) ON_UPDATE_COMMAND_UI(ID_FILE_OPEN, OnUpdateIfEmbedded) ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateIfEmbedded) ON_UPDATE_COMMAND_UI(ID_FILE_PRINT, OnUpdateIfEmbedded) ON_UPDATE_COMMAND_UI(ID_FILE_PRINT_DIRECT, OnUpdateIfEmbedded) ON_UPDATE_COMMAND_UI(ID_FILE_PRINT_PREVIEW, OnUpdateIfEmbedded) //}}AFX_MSG_MAP ON_UPDATE_COMMAND_UI(ID_FILE_SEND_MAIL, OnUpdateFileSendMail) ON_COMMAND(ID_OLE_EDIT_LINKS, OnEditLinks) ON_UPDATE_COMMAND_UI(ID_OLE_VERB_FIRST, CRichEdit2Doc::OnUpdateObjectVerbMenu) ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_CONVERT, CRichEdit2Doc::OnUpdateObjectVerbMenu) ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, CRichEdit2Doc::OnUpdateEditLinksMenu) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc construction/destruction CWordPadDoc::CWordPadDoc() { m_nDocType = -1; m_nNewDocType = -1; m_short_filename = NULL; } BOOL CWordPadDoc::OnNewDocument() { if (!CRichEdit2Doc::OnNewDocument()) return FALSE; //correct type already set in theApp.m_nNewDocType; int nDocType = (IsEmbedded()) ? RD_EMBEDDED : theApp.m_nNewDocType; GetView()->SetDefaultFont(IsTextType(nDocType)); SetDocType(nDocType); return TRUE; } void CWordPadDoc::ReportSaveLoadException(LPCTSTR lpszPathName, CException* e, BOOL bSaving, UINT nIDP) { if (!m_bDeferErrors && e != NULL) { ASSERT_VALID(e); if (e->IsKindOf(RUNTIME_CLASS(CFileException))) { 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 = AFX_IDP_FILE_ACCESS_DENIED; if (((CFileException*)e)->m_lOsError == ERROR_WRITE_PROTECT) nIDP = IDS_WRITEPROTECT; break; case CFileException::tooManyOpenFiles: nIDP = IDS_TOOMANYFILES; break; case CFileException::directoryFull: nIDP = IDS_DIRFULL; break; case CFileException::sharingViolation: nIDP = IDS_SHAREVIOLATION; break; case CFileException::lockViolation: case CFileException::badSeek: case CFileException::generic: case CFileException::invalidFile: case CFileException::hardIO: nIDP = bSaving ? AFX_IDP_FAILED_IO_ERROR_WRITE : AFX_IDP_FAILED_IO_ERROR_READ; break; default: break; } CString prompt; AfxFormatString1(prompt, nIDP, lpszPathName); AfxMessageBox(prompt, MB_ICONEXCLAMATION, nIDP); return; } } CRichEdit2Doc::ReportSaveLoadException(lpszPathName, e, bSaving, nIDP); return; } BOOL CheckForUnicodeTextFile(LPCTSTR lpszPathName) { BOOL fRet = FALSE ; HANDLE hFile = (HANDLE) 0 ; WORD wBOM ; DWORD dwBytesRead = 0 ; BOOL bTmp ; if (lpszPathName == NULL) { return FALSE ; } hFile = CreateFile( lpszPathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ; if (hFile == INVALID_HANDLE_VALUE) { return FALSE ; } bTmp = ReadFile( hFile, &wBOM, sizeof(WORD), &dwBytesRead, NULL) ; if (bTmp) { if (dwBytesRead == sizeof(WORD)) { if ( (wBOM == BYTE_ORDER_MARK) || (wBOM == REVERSE_BYTE_ORDER_MARK) ) { fRet = TRUE ; } } } CloseHandle(hFile) ; return fRet ; } BOOL CWordPadDoc::OnOpenDocument2(LPCTSTR lpszPathName, bool defaultToText, BOOL* pbAccessDenied) { if (pbAccessDenied) *pbAccessDenied = FALSE; if (m_lpRootStg != NULL) // we are embedded { // we really want to use the converter on this storage m_nNewDocType = RD_EMBEDDED; } else { if (theApp.cmdInfo.m_bForceTextMode) m_nNewDocType = RD_TEXT; else { CFileException fe; m_nNewDocType = GetDocTypeFromName(lpszPathName, fe, defaultToText); if (m_nNewDocType == -1) { if (defaultToText) { ReportSaveLoadException(lpszPathName, &fe, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); } return FALSE; } if (RD_FEWINWORD5 == m_nNewDocType) { AfxMessageBox(IDS_FEWINWORD5_DOC, MB_OK, MB_ICONINFORMATION); if (pbAccessDenied) *pbAccessDenied = TRUE; return FALSE; } if (m_nNewDocType == RD_TEXT && theApp.m_bForceOEM) m_nNewDocType = RD_OEMTEXT; } ScanForConverters(); if (!doctypes[m_nNewDocType].bRead || DocTypeDisabled(m_nNewDocType)) { CString str; CString strName = doctypes[m_nNewDocType].GetString(DOCTYPE_DOCTYPE); AfxFormatString1(str, IDS_CANT_LOAD, strName); AfxMessageBox(str, MB_OK|MB_ICONINFORMATION); if (pbAccessDenied) *pbAccessDenied = TRUE; return FALSE; } } if (RD_TEXT == m_nNewDocType) { if (CheckForUnicodeTextFile(lpszPathName)) m_nNewDocType = RD_UNICODETEXT; } if (!CRichEdit2Doc::OnOpenDocument(lpszPathName)) return FALSE; // Update any Ole links COleUpdateDialog(this).DoModal(); return TRUE; } BOOL CWordPadDoc::OnOpenDocument(LPCTSTR lpszPathName) { BOOL bAccessDenied = FALSE; if (OnOpenDocument2(lpszPathName, NO_DEFAULT_TO_TEXT, &bAccessDenied)) { delete [] m_short_filename; m_short_filename = NULL; return TRUE; } // if we know we failed, don't try the short name if (bAccessDenied) return FALSE; LPTSTR short_filename = new TCHAR[MAX_PATH]; if (NULL == short_filename) AfxThrowMemoryException(); if (0 == ::GetShortPathName(lpszPathName, short_filename, MAX_PATH)) { delete [] short_filename; if (ERROR_FILE_NOT_FOUND == GetLastError()) { CFileException fe(CFileException::fileNotFound); ReportSaveLoadException(lpszPathName, &fe, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); return FALSE; } AfxThrowFileException( CFileException::generic, GetLastError(), lpszPathName); } if (OnOpenDocument2(short_filename)) { delete [] m_short_filename; m_short_filename = short_filename; return TRUE; } delete [] short_filename; return FALSE; } void CWordPadDoc::Serialize(CArchive& ar) { COleMessageFilter* pFilter = AfxOleGetMessageFilter(); ASSERT(pFilter != NULL); pFilter->EnableBusyDialog(FALSE); if (ar.IsLoading()) SetDocType(m_nNewDocType); // // Strip (or output) the byte order mark if this is a Unicode file // if (m_bUnicode) { if (ar.IsLoading()) { WORD byte_order_mark; ar >> byte_order_mark; // No support for byte-reversed files ASSERT(BYTE_ORDER_MARK == byte_order_mark); } else { ar << (WORD) BYTE_ORDER_MARK; } } CRichEdit2Doc::Serialize(ar); pFilter->EnableBusyDialog(TRUE); } BOOL AskAboutFormatLoss(CWordPadDoc *pDoc) { UNREFERENCED_PARAMETER(pDoc); return (IDYES == AfxMessageBox(IDS_SAVE_FORMAT_TEXT, MB_YESNO)); } BOOL CWordPadDoc::DoSave(LPCTSTR pszPathName, BOOL bReplace /*=TRUE*/) // Save the document data to a file // pszPathName = path name where to save document file // if pszPathName is NULL then the user will be prompted (SaveAs) // note: pszPathName 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) { if (NULL != pszPathName) if (pszPathName == m_strPathName && NULL != m_short_filename) pszPathName = m_short_filename; CString newName = pszPathName; int nOrigDocType = m_nDocType; //saved in case of SaveCopyAs or failure int nDocType ; // newName bWrite type result // empty TRUE - SaveAs dialog // empty FALSE - SaveAs dialog // notempty TRUE - nothing // notempty FALSE W6 warn (change to wordpad, save as, cancel) // notempty FALSE other warn (save as, cancel) BOOL bModified = IsModified(); ScanForConverters(); BOOL bSaveAs = FALSE; if (newName.IsEmpty()) { bSaveAs = TRUE; } else if (!doctypes[m_nDocType].bWrite) { if (!theApp.ShouldAlwaysConvertToRTF()) { UnsupportedSaveFormatDialog dialog; if (IDOK != dialog.DoModal()) return FALSE; if (dialog.ShouldAlwaysConvertToRTF()) theApp.SetAlwaysConvertToRTF(); } m_nDocType = RD_RICHTEXT; } if (m_lpRootStg == NULL && IsTextType(m_nDocType) && !bSaveAs && !GetView()->IsFormatText()) { if (!AskAboutFormatLoss(this)) bSaveAs = TRUE; } GetView()->GetParentFrame()->RecalcLayout(); if (bSaveAs) { newName = m_strPathName; if (bReplace && newName.IsEmpty()) { newName = m_strTitle; int iBad = newName.FindOneOf(_T(" #%;/\\")); // dubious filename if (iBad != -1) newName.ReleaseBuffer(iBad); // append the default suffix if there is one newName += GetExtFromType(m_nDocType); } nDocType = m_nDocType; promptloop: if (!theApp.PromptForFileName(newName, bReplace ? AFX_IDS_SAVEFILE : AFX_IDS_SAVEFILECOPY, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, FALSE, &nDocType)) { SetDocType(nOrigDocType, TRUE); return FALSE; // don't even try to save } else { // // If we are transitioning from non-text to text, we need // to warn the user if there is any formatting / graphics // that will be lost // if (IsTextType(nDocType)) { if (m_lpRootStg == NULL && !GetView()->IsFormatText()) { if (!AskAboutFormatLoss(this)) goto promptloop; } } } SetDocType(nDocType, TRUE); } BeginWaitCursor(); if (!OnSaveDocument(newName)) { // // The original code deleted the file if an error occurred, on the // assumption that if we tried to save a file and something went wrong // but there was a file there after the save, the file is probably // bogus. This fails if there is an existing file that doesn't have // write access but does have delete access. How can this happen? // The security UI does not remove delete access when you remove // write access. // // restore orginal document type SetDocType(nOrigDocType, TRUE); EndWaitCursor(); return FALSE; } EndWaitCursor(); if (bReplace) { int nType = m_nDocType; SetDocType(nOrigDocType, TRUE); SetDocType(nType); // Reset the title and change the document name if (NULL == m_short_filename || 0 != newName.CompareNoCase(m_short_filename)) { SetPathName(newName, TRUE); // If we saved to a new filename, reset the short name if (bSaveAs) { delete [] m_short_filename; m_short_filename = NULL; } } } else // SaveCopyAs { SetDocType(nOrigDocType, TRUE); SetModifiedFlag(bModified); } return TRUE; // success } class COIPF : public COleIPFrameWnd { public: CFrameWnd* GetMainFrame() { return m_pMainFrame;} CFrameWnd* GetDocFrame() { return m_pDocFrame;} }; void CWordPadDoc::OnDeactivateUI(BOOL bUndoable) { if (GetView()->m_bDelayUpdateItems) UpdateAllItems(NULL); SaveState(m_nDocType); CRichEdit2Doc::OnDeactivateUI(bUndoable); COIPF* pFrame = (COIPF*)m_pInPlaceFrame; if (pFrame != NULL) { if (pFrame->GetMainFrame() != NULL) ForceDelayed(pFrame->GetMainFrame()); if (pFrame->GetDocFrame() != NULL) ForceDelayed(pFrame->GetDocFrame()); } } void CWordPadDoc::ForceDelayed(CFrameWnd* pFrameWnd) { ASSERT_VALID(this); ASSERT_VALID(pFrameWnd); POSITION pos = pFrameWnd->m_listControlBars.GetHeadPosition(); while (pos != NULL) { // show/hide the next control bar CControlBar* pBar = (CControlBar*)pFrameWnd->m_listControlBars.GetNext(pos); BOOL bVis = pBar->GetStyle() & WS_VISIBLE; UINT swpFlags = 0; if ((pBar->m_nStateFlags & CControlBar::delayHide) && bVis) swpFlags = SWP_HIDEWINDOW; else if ((pBar->m_nStateFlags & CControlBar::delayShow) && !bVis) swpFlags = SWP_SHOWWINDOW; pBar->m_nStateFlags &= ~(CControlBar::delayShow|CControlBar::delayHide); if (swpFlags != 0) { pBar->SetWindowPos(NULL, 0, 0, 0, 0, swpFlags| SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); } } } ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc Attributes CLSID CWordPadDoc::GetClassID() { return (m_pFactory == NULL) ? CLSID_NULL : m_pFactory->GetClassID(); } void CWordPadDoc::SetDocType(int nNewDocType, BOOL bNoOptionChange) { ASSERT(nNewDocType != -1); if (nNewDocType == m_nDocType) return; m_bRTF = !IsTextType(nNewDocType); m_bUnicode = (nNewDocType == RD_UNICODETEXT); if (bNoOptionChange) m_nDocType = nNewDocType; else { SaveState(m_nDocType); m_nDocType = nNewDocType; RestoreState(m_nDocType); } } CWordPadView* CWordPadDoc::GetView() { POSITION pos = GetFirstViewPosition(); return (CWordPadView* )GetNextView( pos ); } ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc Operations CFile* CWordPadDoc::GetFile(LPCTSTR pszPathName, UINT nOpenFlags, CFileException* pException) { CTrackFile* pFile = NULL; CFrameWnd* pWnd = GetView()->GetParentFrame(); #ifdef CONVERTERS ScanForConverters(); // if writing use current doc type otherwise use new doc type int nType = (nOpenFlags & CFile::modeReadWrite) ? m_nDocType : m_nNewDocType; // m_nNewDocType will be same as m_nDocType except when opening a new file if (doctypes[nType].pszConverterName != NULL) pFile = new CConverter(doctypes[nType].pszConverterName, pWnd); else #endif if (nType == RD_OEMTEXT) pFile = new COEMFile(pWnd); else pFile = new CTrackFile(pWnd); if (!pFile->Open(pszPathName, nOpenFlags, pException)) { delete pFile; return NULL; } if (nOpenFlags & (CFile::modeWrite | CFile::modeReadWrite)) pFile->m_dwLength = 0; // can't estimate this else pFile->m_dwLength = pFile->GetLength(); return pFile; } CRichEdit2CntrItem* CWordPadDoc::CreateClientItem(REOBJECT* preo) const { // cast away constness of this return new CWordPadCntrItem(preo, (CWordPadDoc*)this); } ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc server implementation COleServerItem* CWordPadDoc::OnGetEmbeddedItem() { // OnGetEmbeddedItem is called by the framework to get the COleServerItem // that is associated with the document. It is only called when necessary. CEmbeddedItem* pItem = new CEmbeddedItem(this); ASSERT_VALID(pItem); return pItem; } ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc serialization ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc diagnostics #ifdef _DEBUG void CWordPadDoc::AssertValid() const { CRichEdit2Doc::AssertValid(); } void CWordPadDoc::Dump(CDumpContext& dc) const { CRichEdit2Doc::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CWordPadDoc commands int CWordPadDoc::MapType(int nType) { if (nType == RD_OEMTEXT || nType == RD_UNICODETEXT) nType = RD_TEXT; else if (!IsInPlaceActive() && nType == RD_EMBEDDED) nType = RD_RICHTEXT; return nType; } void CWordPadDoc::OnViewOptions() { int nType = MapType(m_nDocType); int nFirstPage = 3; if (nType == RD_TEXT) nFirstPage = 1; else if (nType == RD_RICHTEXT) nFirstPage = 2; else if (nType == RD_WRITE) nFirstPage = 4; else if (nType == RD_EMBEDDED) nFirstPage = 5; SaveState(nType); COptionSheet sheet(IDS_OPTIONS, NULL, nFirstPage); if (sheet.DoModal() == IDOK) { CWordPadView* pView = GetView(); if (theApp.m_bWordSel) pView->GetRichEditCtrl().SetOptions(ECOOP_OR, ECO_AUTOWORDSELECTION); else { pView->GetRichEditCtrl().SetOptions(ECOOP_AND, ~(DWORD)ECO_AUTOWORDSELECTION); } RestoreState(nType); } } void CWordPadDoc::OnUpdateOleVerbPopup(CCmdUI* pCmdUI) { pCmdUI->m_pParentMenu = pCmdUI->m_pMenu; CRichEdit2Doc::OnUpdateObjectVerbMenu(pCmdUI); } BOOL CWordPadDoc::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { if (nCode == CN_COMMAND && nID == ID_OLE_VERB_POPUP) nID = ID_OLE_VERB_FIRST; return CRichEdit2Doc::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } void CWordPadDoc::SaveState(int nType) { if (nType == -1) return; nType = MapType(nType); CWordPadView* pView = GetView(); if (pView != NULL) { CFrameWnd* pFrame = pView->GetParentFrame(); ASSERT(pFrame != NULL); // save current state pFrame->SendMessage(WPM_BARSTATE, 0, nType); theApp.GetDocOptions(nType).m_nWordWrap = pView->m_nWordWrap; } } void CWordPadDoc::RestoreState(int nType) { if (nType == -1) return; nType = MapType(nType); CWordPadView* pView = GetView(); if (pView != NULL) { CFrameWnd* pFrame = pView->GetParentFrame(); ASSERT(pFrame != NULL); // set new state pFrame->SendMessage(WPM_BARSTATE, 1, nType); int nWrapNew = theApp.GetDocOptions(nType).m_nWordWrap; if (pView->m_nWordWrap != nWrapNew) { pView->m_nWordWrap = nWrapNew; pView->WrapChanged(); } } } void CWordPadDoc::OnCloseDocument() { SaveState(m_nDocType); CRichEdit2Doc::OnCloseDocument(); } void CWordPadDoc::PreCloseFrame(CFrameWnd* pFrameArg) { CRichEdit2Doc::PreCloseFrame(pFrameArg); SaveState(m_nDocType); } void CWordPadDoc::OnFileSendMail() { if (m_strTitle.Find('.') == -1) { // add the extension because the default extension will be wrong CString strOldTitle = m_strTitle; m_strTitle += GetExtFromType(m_nDocType); CRichEdit2Doc::OnFileSendMail(); m_strTitle = strOldTitle; } else CRichEdit2Doc::OnFileSendMail(); } void CWordPadDoc::OnUpdateIfEmbedded(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsEmbedded()); } void CWordPadDoc::OnEditLinks() { g_fDisableStandardHelp = TRUE ; SetHelpFixHook() ; COleLinksDialog dlg(this, GetRoutingView_()); dlg.m_el.dwFlags |= ELF_DISABLECANCELLINK; dlg.DoModal(); RemoveHelpFixHook() ; g_fDisableStandardHelp = FALSE ; }