/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ // // Editor save stuff. LWT 06/01/95 // // Added all ITapeFileSystem stuff, hardts 10/96 #ifdef EDITOR #include "editor.h" #include "streams.h" #include "shist.h" #include "xpgetstr.h" #include "net.h" #include "mime.h" #include "prefapi.h" // Other error values are created from ALLXPSTR.RC during compiling //extern "C" int MK_UNABLE_TO_LOCATE_FILE; //extern "C" int MK_MALFORMED_URL_ERROR; //----------------------------------------------------------------------------- // CEditImageLoader //----------------------------------------------------------------------------- CEditImageLoader::CEditImageLoader( CEditBuffer *pBuffer, EDT_ImageData *pImageData, XP_Bool bReplaceImage ): m_pBuffer( pBuffer ), m_pImageData( edt_DupImageData( pImageData ) ), m_bReplaceImage( bReplaceImage ) { char *pStr; if( !pBuffer->m_pContext->is_new_document ){ intn retVal = NET_MakeRelativeURL( LO_GetBaseURL( m_pBuffer->m_pContext ), m_pImageData->pSrc, &pStr ); if( retVal != NET_URL_FAIL ){ XP_FREE( m_pImageData->pSrc ); m_pImageData->pSrc = pStr; } retVal = NET_MakeRelativeURL( LO_GetBaseURL( m_pBuffer->m_pContext ), m_pImageData->pLowSrc, &pStr ); if( retVal != NET_URL_FAIL ){ XP_FREE( m_pImageData->pLowSrc ); m_pImageData->pLowSrc = pStr; } } } CEditImageLoader::~CEditImageLoader(){ m_pBuffer->m_pLoadingImage = 0; FE_ImageLoadDialogDestroy( m_pBuffer->m_pContext ); FE_EnableClicking( m_pBuffer->m_pContext ); edt_FreeImageData( m_pImageData ); } void CEditImageLoader::LoadImage(){ char *pStr; XP_ObserverList image_obs_list; // List of image observers. m_pLoImage = LO_NewImageElement( m_pBuffer->m_pContext ); // convert the URL to an absolute path pStr = NET_MakeAbsoluteURL( LO_GetBaseURL( m_pBuffer->m_pContext ), m_pImageData->pSrc ); m_pLoImage->image_url = PA_strdup( pStr ); XP_FREE( pStr ); // convert the URL to an absolute path pStr = NET_MakeAbsoluteURL( LO_GetBaseURL( m_pBuffer->m_pContext ), m_pImageData->pLowSrc ); m_pLoImage->lowres_image_url = PA_strdup( pStr ); XP_FREE( pStr ); // tell layout to pass the size info back to us. m_pLoImage->ele_id = ED_IMAGE_LOAD_HACK_ID; FE_ImageLoadDialog( m_pBuffer->m_pContext ); image_obs_list = lo_NewImageObserverList(m_pBuffer->m_pContext, m_pLoImage); if (!image_obs_list) { XP_ASSERT(FALSE); // Out of memory. return; } lo_GetImage(m_pBuffer->m_pContext, m_pBuffer->m_pContext->img_cx, m_pLoImage, image_obs_list, NET_DONT_RELOAD); } // The image library is telling us the size of the image. Actually do the // insert now. void CEditImageLoader::SetImageInfo(int32 width, int32 height){ if( m_pImageData->iHeight == 0 || m_pImageData->iWidth == 0 ){ m_pImageData->iHeight = height; m_pImageData->iWidth = width; m_pImageData->iOriginalHeight = height; m_pImageData->iOriginalWidth = width; } if( m_bReplaceImage ){ // whack this in here... m_pBuffer->m_pCurrent->Image()->SetImageData( m_pImageData ); // we could be doing a better job here. We probably repaint too much... CEditElement *pContainer = m_pBuffer->m_pCurrent->FindContainer(); m_pBuffer->Relayout( pContainer, 0, pContainer->GetLastMostChild(), RELAYOUT_NOCARET ); m_pBuffer->SelectCurrentElement(); } else { m_pBuffer->InsertImage( m_pImageData ); } delete this; } //----------------------------------------------------------------------------- // CEditSaveObject //----------------------------------------------------------------------------- #if defined(XP_OS2) extern "C" #else PRIVATE #endif unsigned int edt_file_save_stream_write_ready( NET_StreamClass * /* stream */ ){ return MAX_WRITE_READY; } #if defined(XP_OS2) extern "C" #else PRIVATE #endif int edt_FileSaveStreamWrite (NET_StreamClass *stream, const char *block, int32 length) { CFileSaveObject *pSaver = (CFileSaveObject*)stream->data_object; return pSaver->NetStreamWrite( block, length ); } #if defined(XP_OS2) extern "C" #else PRIVATE #endif void edt_file_save_stream_complete( NET_StreamClass * /* stream */ ){ } #if defined(XP_OS2) extern "C" #else PRIVATE #endif void edt_file_save_stream_abort (NET_StreamClass * /* stream */, int /* status */ ) { } #if defined(XP_OS2) extern "C" #else PRIVATE #endif void edt_UrlExit( URL_Struct *pURL, int status, MWContext * /* context */ ) { // hardts, changed from CEditSaveObject to CFileSaveObject ((CFileSaveObject*)(pURL->fe_data))->NetFetchDone(status); NET_FreeURLStruct(pURL); } #if defined(XP_OS2) extern "C" #else PRIVATE #endif NET_StreamClass * edt_MakeFileSaveStream (int /* format_out */, void * /* closure */, URL_Struct *url, MWContext *context ) { NET_StreamClass *stream; CFileSaveObject *pSaver = (CFileSaveObject*)url->fe_data; // OpenOutputFile will return 0 if OK if( !pSaver || pSaver->OpenOutputFile() != 0 ){ return (NULL); } // ### mwelch Relay the information stored in this URL. We do it here instead of // in the url exit routine because edt_MakeFileSaveStream could // have been invoked by an inferior URL which represents a parsed // piece of a mail or news message. If this URL is what was originally // invoked, the correct mime type should still be present here. if (url) pSaver->CopyURLInfo(url); TRACEMSG(("Setting up attachment stream. Have URL: %s\n", url->address)); stream = XP_NEW (NET_StreamClass); if (stream == NULL) return (NULL); XP_MEMSET (stream, 0, sizeof (NET_StreamClass)); stream->name = "editor file save"; stream->complete = edt_file_save_stream_complete; stream->abort = edt_file_save_stream_abort; stream->put_block = edt_FileSaveStreamWrite; stream->is_write_ready = edt_file_save_stream_write_ready; stream->data_object = url->fe_data; stream->window_id = context; return stream; } static XP_Bool bNetRegistered = FALSE; #ifdef MOZ_MAIL_NEWS extern "C" { NET_StreamClass *MIME_MessageConverter (int format_out, void *closure, URL_Struct *url, MWContext *context); } #endif //----------------------------------------------------------------------------- // CFileSaveObject //----------------------------------------------------------------------------- CFileSaveObject::CFileSaveObject( MWContext *pContext, ITapeFileSystem *tapeFS, XP_Bool bAutoSave, CEditSaveToTempData *pSaveToTempData) : m_pContext( pContext ) , m_bOverwriteAll( FALSE ), m_bDontOverwriteAll( FALSE ), m_bDontOverwrite( FALSE ), m_iCurFile(0), m_pOutStream(0), m_bOpenOutputHandledError(FALSE), m_tapeFS(tapeFS), m_status( ED_ERROR_NONE ), m_bFromAutoSave(bAutoSave), m_pSaveToTempData(pSaveToTempData) { if( !bNetRegistered ){ #ifdef MOZ_MAIL_NEWS // ### mwelch Need to pass mail/news messages through // MIME_MessageConverter since we're likely to be // extracting a part of the message. NET_RegisterContentTypeConverter( MESSAGE_RFC822, FO_EDT_SAVE_IMAGE, NULL, MIME_MessageConverter); NET_RegisterContentTypeConverter( MESSAGE_NEWS, FO_EDT_SAVE_IMAGE, NULL, MIME_MessageConverter); #endif // For everything else, just write to the tapefs. NET_RegisterContentTypeConverter( "*", FO_EDT_SAVE_IMAGE, NULL, edt_MakeFileSaveStream ); //// Why is this duplicated? hardts #if defined(XP_WIN) || defined(XP_OS2) NET_RegisterContentTypeConverter( "*", FO_EDT_SAVE_IMAGE, NULL, edt_MakeFileSaveStream ); #else NET_RegisterContentTypeConverter( "*", FO_EDT_SAVE_IMAGE, NULL, edt_MakeFileSaveStream ); #endif /* XP_WIN */ bNetRegistered = TRUE; } // MUST DISABLE WINDOW INTERACTION HERE! // (It will always be cleared in the GetURLExitRoutine) // (This is normally set TRUE by front end before calling NET_GetURL) m_pContext->waitingMode = TRUE; // Set a flag so we know when we are saving a file // (also set in CEditBuffer::SaveFile() - may not be needed here, but its safe to set again) m_pContext->edit_saving_url = TRUE; // set base URL for the tape file system. char *pBaseURL = edt_GetDocRelativeBaseURL(m_pContext); if (pBaseURL) { m_tapeFS->SetSourceBaseURL(pBaseURL); XP_FREE(pBaseURL); } } CFileSaveObject::~CFileSaveObject(){ // Success message for publishing. Moved from WinFE to back end. if (m_tapeFS->GetType() == ITapeFileSystem::Publish && m_status == ED_ERROR_NONE) { char *msg = NULL; // Different message for 1 or multiple files. if (m_tapeFS->GetNumFiles() > 1) { char *tmplate = XP_GetString(XP_EDT_PUBLISH_REPORT_MSG); if (tmplate) { msg = PR_smprintf(tmplate,m_tapeFS->GetNumFiles()); } } else { msg = XP_STRDUP(XP_GetString(XP_EDT_PUBLISH_REPORT_ONE_FILE)); } if (msg) { char * pBrowse = NULL, *pPub = NULL, *pLastPub = NULL; PREF_CopyCharPref("editor.publish_browse_location",&pBrowse); PREF_CopyCharPref("editor.publish_location",&pPub); PREF_CopyCharPref("editor.publish_last_loc",&pLastPub); #if defined(XP_WIN) || defined(XP_OS2) /* FE_LoadUrl needs to be implemented in X */ if( EDT_IsSameURL(pPub, pLastPub,0,0) && pBrowse && *pBrowse ){ // We just saved to the default home location StrAllocCat(msg, XP_GetString(XP_EDT_BROWSE_TO_DEFAULT)); if( FE_Confirm(m_pContext,msg) ){ // User said Yes - load location into a BROWSER window (FALSE param) FE_LoadUrl(pBrowse, FALSE ); } } else { FE_Alert(m_pContext,msg); } XP_FREEIF(pBrowse); XP_FREEIF(pPub); #else // mac and unix FE_Message(m_pContext,msg); #endif XP_FREE(msg); } } // LOU: What should I do here. There appear to be connections on the // window that are still open.... if( NET_AreThereActiveConnectionsForWindow( m_pContext ) ){ //XP_ASSERT(FALSE); //NET_SilentInterruptWindow( m_pContext ); XP_TRACE(("NET_AreThereActiveConnectionsForWindow is TRUE in ~CFileSaveObject")); } ////XP_FREE( m_pDestPathURL ); // Allow front end interaction m_pContext->waitingMode = FALSE; // Clear flag (also done in CEditBuffer::FinishedLoad2) m_pContext->edit_saving_url = FALSE; #ifdef XP_WIN // Tell front end we finished autosave // so it can gray the Save toolbar button if( m_bFromAutoSave ) FE_UpdateEnableStates(m_pContext); #endif // Tape File system should not be deleted yet. if (m_tapeFS && m_tapeFS->GetType() == ITapeFileSystem::Publish && m_status == ED_ERROR_NONE) { // Call publishComplete event. char *pDestURL = m_tapeFS->GetDestAbsURL(); EDT_PerformEvent(m_pContext,"publishComplete",pDestURL,TRUE,FALSE,0,0); XP_FREEIF(pDestURL); } ///// This code will have to change if we implement a "saveComplete" event for ///// composer plugins. // Call callback for saving file to temporary location. if (m_tapeFS && m_tapeFS->GetType() == ITapeFileSystem::File && m_pSaveToTempData && m_pSaveToTempData->doneFn) { if (m_status == ED_ERROR_NONE) { (*m_pSaveToTempData->doneFn)(m_pSaveToTempData->pFileURL,m_pSaveToTempData->hook); } else { // Failed, pass in NULL as the temporary file name. (*m_pSaveToTempData->doneFn)(NULL,m_pSaveToTempData->hook); } } delete m_pSaveToTempData; } // Callback from tape file system. // //** Don't make this function static because it is a friend of CFileSaveObject //** so it must be extern. (at least on MAC) void edt_CFileSaveObjectDone(XP_Bool success,void *arg) { CFileSaveObject *saveObject = (CFileSaveObject *)arg; if (!saveObject) { XP_ASSERT(0); return; } if (!success) { saveObject->m_status = ED_ERROR_TAPEFS_COMPLETION; } // Tape File system should not be deleted yet. delete saveObject; } void CFileSaveObject::CopyURLInfo(const URL_Struct *pURL) { XP_ASSERT((m_tapeFS) && (m_iCurFile <= m_tapeFS->GetNumFiles())); if ((m_tapeFS) && (m_iCurFile <= m_tapeFS->GetNumFiles())) m_tapeFS->CopyURLInfo(m_iCurFile-1, pURL); } ED_FileError CFileSaveObject::FileFetchComplete() { XP_ASSERT(m_tapeFS->GetNumFiles()); if( !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878 m_tapeFS->GetType() != ITapeFileSystem::MailSend ){ // Suppress dialog for mail send. FE_SaveDialogDestroy( m_pContext, m_status, 0); } if (!m_tapeFS) { XP_ASSERT(0); return ED_ERROR_BLOCKED; // is this the right thing to return? } ED_FileError statusResult = m_status; m_tapeFS->Complete( m_status == ED_ERROR_NONE, edt_CFileSaveObjectDone, (void *)this ); // "this" may already be deleted at this point, so we can't just return m_status. return statusResult; } ED_FileError CFileSaveObject::SaveFiles(){ XP_ASSERT(m_tapeFS && m_tapeFS->GetNumFiles()); // Next param indicates if we are downloading (FALSE) // or uploading (TRUE), so this can be used for publishing if( !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878 m_tapeFS->GetType() != ITapeFileSystem::MailSend ){ // Suppress dialog for mail send. ED_SaveDialogType saveType; switch (m_tapeFS->GetType()) { case ITapeFileSystem::File: saveType = ED_SAVE_DLG_SAVE_LOCAL; break; case ITapeFileSystem::Publish: saveType = ED_SAVE_DLG_PREPARE_PUBLISH; break; default: XP_ASSERT(0); } FE_SaveDialogCreate( m_pContext, m_tapeFS->GetNumFiles(), saveType ); } if (SaveFirstFile()) { // Child of CFileSaveObject took care of the first file. m_iCurFile++; } // loop through the tree getting all the image elements... return FetchNextFile(); } // Just a wrapper around the Tape File System. intn CFileSaveObject::AddFile( char *pSrcURL, char *pMIMEType, int16 iDocCharSetID){ XP_ASSERT(m_tapeFS); return m_tapeFS->AddFile(pSrcURL,pMIMEType,iDocCharSetID); } // // open the file and get the Netlib to fetch it from the cache. // ED_FileError CFileSaveObject::FetchNextFile(){ XP_ASSERT(m_tapeFS); if( m_status != ED_ERROR_NONE || m_iCurFile >= m_tapeFS->GetNumFiles() ){ return FileFetchComplete(); } // m_iCurFile is 1-based, so i is 0-based. int i = m_iCurFile++; char *srcURL = m_tapeFS->GetSourceURL(i); if (!srcURL) { XP_ASSERT(0); return ED_ERROR_FILE_READ; } URL_Struct *pUrl; pUrl= NET_CreateURLStruct( srcURL, NET_DONT_RELOAD ); if (!pUrl) { XP_ASSERT(0); XP_FREE(srcURL); return ED_ERROR_FILE_READ; } XP_FREE(srcURL); // Store file save object so we can access OpenOutputFile etc. pUrl->fe_data = this; //TODO: Should we be setting the referer field as well? NET_GetURL( pUrl, FO_EDT_SAVE_IMAGE, m_pContext, edt_UrlExit ); return m_status; } void CFileSaveObject::CheckFinishedSave(intn oneBased,ED_FileError iError) // one-based index. { if (m_tapeFS->IsLocalPersistentFile(oneBased - 1)) { char *pAbsURL = GetDestAbsoluteURL(oneBased - 1); if (pAbsURL) { // Tell the front end that a new local file has been created. // We use number to tell between Main image (1) and Low-Res image (2) FE_FinishedSave( m_pContext, iError, pAbsURL, oneBased ); XP_FREE(pAbsURL); } } } void CFileSaveObject::NetFetchDone( int status ){ // close the file in any case. if( m_pOutStream ){ m_tapeFS->CloseStream(m_iCurFile-1); m_pOutStream = 0; } // Default: No error, show destination name for most errors ED_FileError iError = ED_ERROR_NONE; // NOTE: This is the NETLIB error code: (see merrors.h) // < 0 is an error if( status < 0 ){ // As we become familiar with typical errors, // add specialized error types here // Use " extern MK_..." to access the error value if( status == MK_UNABLE_TO_LOCATE_FILE || status == MK_MALFORMED_URL_ERROR ) { // Couldn't find source -- use source filename // for error report iError = ED_ERROR_SRC_NOT_FOUND; } else if( m_bDontOverwrite ) { // We get status = MK_UNABLE_TO_CONVERT // if user said NO to overwriting an existing file: // edt_MakeFileSaveStream returns 0, // making NET_GetURL think it failed, // but its not really an error iError = ED_ERROR_NONE; } else if( m_status == ED_ERROR_CANCEL ) { // User selected CANCEL at overwrite warning dialog, // thus stream was not created and returns error status, // but its not really an error iError = ED_ERROR_CANCEL; } else { // "Unknown" error default iError = ED_ERROR_FILE_READ; } } // ED_ERROR_FILE_OPEN was already handled in CFileSaveObject::OpenOutputFile() if (!m_bOpenOutputHandledError) { // No longer uses FE_SaveErrorContinueDialog() if (!SaveErrorContinueDialog(iError) ) { Cancel(); } CheckFinishedSave(m_iCurFile,iError); } FetchNextFile(); } intn CFileSaveObject::OpenOutputFile(){ m_bOpenOutputHandledError = FALSE; // Return if we already have an open stream if( m_pOutStream != NULL){ return 0; } // Current number incremented before comming here, // or think of it as 1-based counting int i = m_iCurFile-1; XP_Bool bWriteThis = TRUE; char *pHumanName = m_tapeFS->GetHumanName(i); // Tell front end the filename even if we are not writing the file // if (!m_bDontOverwriteAll && !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878 m_tapeFS->GetType() != ITapeFileSystem::MailSend) { // Suppress dialog for mail send. FE_SaveDialogSetFilename( m_pContext, pHumanName ); } m_bDontOverwrite = m_bDontOverwriteAll; if( !m_bOverwriteAll){ if( m_tapeFS->FileExists(i) ){ // file exists. if( m_bDontOverwriteAll ){ bWriteThis = FALSE; CheckFinishedSave(m_iCurFile,ED_ERROR_FILE_EXISTS); } else { ED_SaveOption e; e = FE_SaveFileExistsDialog( m_pContext, pHumanName); switch( e ){ case ED_SAVE_OVERWRITE_THIS: break; case ED_SAVE_OVERWRITE_ALL: m_bOverwriteAll = TRUE; break; case ED_SAVE_DONT_OVERWRITE_ALL: m_bDontOverwriteAll = TRUE; // intentionally fall through case ED_SAVE_DONT_OVERWRITE_THIS: // This flag prevents us from reporting // "failed" file stream creation as an error m_bDontOverwrite = TRUE; bWriteThis = FALSE; CheckFinishedSave(m_iCurFile,ED_ERROR_FILE_EXISTS); break; case ED_SAVE_CANCEL: Cancel(); bWriteThis = FALSE; } } } } if( bWriteThis ){ m_pOutStream = m_tapeFS->OpenStream( i ); } else { // We are returning, not doing the save, but not an error condition XP_FREE(pHumanName); return -1; } XP_TRACE(("Opening stream number %d gives %d.", i, (int)m_pOutStream)); if ( m_pOutStream == NULL ) { // No longer uses FE_SaveErrorContinueDialog() if(!SaveErrorContinueDialog( ED_ERROR_FILE_OPEN ) ){ Cancel(); } XP_FREE(pHumanName); CheckFinishedSave(m_iCurFile,ED_ERROR_FILE_OPEN); // So we don't report error twice, once on opening file and once // when netlib is unable to write to it. m_bOpenOutputHandledError = TRUE; return -1; } XP_FREE(pHumanName); return 0; // success } int CFileSaveObject::NetStreamWrite( const char *block, int32 length ){ XP_TRACE(("NetStreamWrite: length = %lu", length)); XP_ASSERT(m_pOutStream); m_pOutStream->Write((char *)block,length); return m_pOutStream->Status() == IStreamOut::EOS_NoError; } void CFileSaveObject::Cancel(){ // tell net URL to stop... m_status = ED_ERROR_CANCEL; // Above alone does seem to work! Don't we need this? // NET_InterruptWindow causes a "reentrant interrupt" error message now! NET_SilentInterruptWindow( m_pContext ); } char *CFileSaveObject::GetDestName( intn index ){ if (!m_tapeFS) { XP_ASSERT(0); return NULL; } return m_tapeFS->GetDestURL(index); } char *CFileSaveObject::GetDestAbsoluteURL( intn index ){ if (!m_tapeFS) { XP_ASSERT(0); return NULL; } char *pDestPathURL = m_tapeFS->GetDestPathURL(); char *pDestRelativeURL = m_tapeFS->GetDestURL(index); if (!pDestRelativeURL) { XP_ASSERT(0); XP_FREEIF(pDestPathURL); return NULL; } // Maybe should use NET_MakeAbsolute() instead of concatenation. char *pDestAbsURL = pDestPathURL ? PR_smprintf( "%s%s", pDestPathURL, pDestRelativeURL ) : XP_STRDUP(pDestRelativeURL); XP_FREEIF(pDestPathURL); XP_FREE(pDestRelativeURL); return pDestAbsURL; } char *CFileSaveObject::GetSrcName( intn index ){ if (!m_tapeFS) { XP_ASSERT(0); return NULL; } return m_tapeFS->GetSourceURL(index); /*if( index >= m_srcImageURLs.Size() ){ return 0; } else { return XP_STRDUP( m_srcImageURLs[index] ); } */ } // Convert file:// URLs into local file names for display to user. PRIVATE void edt_url_make_human(char **pURL) { if (NET_IsLocalFileURL(*pURL)) { char *pTemp; XP_ConvertUrlToLocalFile(*pURL,&pTemp); XP_FREEIF(*pURL); *pURL = pTemp; } } XP_Bool CFileSaveObject::SaveErrorContinueDialog(ED_FileError iError) { // No user interaction for these conditions. if (iError == ED_ERROR_NONE || iError == ED_ERROR_CANCEL || iError == ED_ERROR_FILE_EXISTS) { return TRUE; } char *tmplate = NULL; char *msg = NULL; // m_iCurFile is 1-based. char *pAbsDest = GetDestAbsoluteURL(m_iCurFile-1); char *pRelDest = m_tapeFS->GetHumanName(m_iCurFile-1); char *pAbsSrc = GetSrcName(m_iCurFile-1); char *pRootAbsDest = GetDestAbsoluteURL(0); edt_url_make_human(&pAbsDest); edt_url_make_human(&pAbsSrc); edt_url_make_human(&pRootAbsDest); // Two level switch, first on different types of file systems, // second on different classes of errors. switch (m_tapeFS->GetType()) { case ITapeFileSystem::File: switch (iError) { case ED_ERROR_FILE_OPEN: case ED_ERROR_FILE_WRITE: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_SAVE_FILE_WRITE)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_SAVE_CONTINUE)); msg = PR_smprintf(tmplate,pRelDest,pRootAbsDest,pRelDest); break; case ED_ERROR_SRC_NOT_FOUND: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_SAVE_SRC_NOT_FOUND)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_SAVE_CONTINUE)); msg = PR_smprintf(tmplate,pAbsSrc,pAbsSrc); break; case ED_ERROR_FILE_READ: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_SAVE_FILE_READ)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_SAVE_CONTINUE)); msg = PR_smprintf(tmplate,pRelDest,pRelDest); break; } break; case ITapeFileSystem::Publish: switch (iError) { case ED_ERROR_FILE_OPEN: case ED_ERROR_FILE_WRITE: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_PUBLISH_FILE_WRITE)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_PUBLISH_CONTINUE)); msg = PR_smprintf(tmplate,pRelDest,pRelDest); break; case ED_ERROR_SRC_NOT_FOUND: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_PUBLISH_SRC_NOT_FOUND)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_PUBLISH_CONTINUE)); msg = PR_smprintf(tmplate,pAbsSrc,pAbsSrc); break; case ED_ERROR_FILE_READ: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_PUBLISH_FILE_READ)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_PUBLISH_CONTINUE)); msg = PR_smprintf(tmplate,pRelDest,pRelDest); break; } break; case ITapeFileSystem::MailSend: switch (iError) { case ED_ERROR_FILE_OPEN: case ED_ERROR_FILE_WRITE: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_FILE_WRITE)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_MAIL_CONTINUE)); msg = PR_smprintf(tmplate,pRelDest,pRelDest); break; case ED_ERROR_SRC_NOT_FOUND: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_SRC_NOT_FOUND)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_MAIL_CONTINUE)); msg = PR_smprintf(tmplate,pAbsSrc,pAbsSrc); break; case ED_ERROR_FILE_READ: tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_FILE_READ)); StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_MAIL_CONTINUE)); msg = PR_smprintf(tmplate,pRelDest,pRelDest); break; } break; } XP_Bool retVal = FALSE; if (msg) { retVal = FE_Confirm(m_pContext,msg); } else { XP_ASSERT(0); } XP_FREEIF(tmplate); XP_FREEIF(msg); XP_FREEIF(pAbsDest); XP_FREEIF(pRelDest); XP_FREEIF(pAbsSrc); XP_FREEIF(pRootAbsDest); return retVal; } char * CEditSaveObject::m_pFailedPublishUrl = NULL; //----------------------------------------------------------------------------- // CEditSaveObject //----------------------------------------------------------------------------- CEditSaveObject::CEditSaveObject( CEditBuffer *pBuffer, ED_SaveFinishedOption finishedOpt, char *pSrcURL, ITapeFileSystem *tapeFS, XP_Bool, // bSaveAs is ignored. XP_Bool bKeepImagesWithDoc, XP_Bool bAutoAdjustLinks, XP_Bool bAutoSave, CEditSaveToTempData *pSaveToTempData ) : CFileSaveObject( pBuffer->m_pContext,tapeFS,bAutoSave, pSaveToTempData ), m_pBuffer( pBuffer ), m_pDocStateSave( 0 ), m_pSrcURL( XP_STRDUP( pSrcURL ) ), m_finishedOpt( finishedOpt ), m_bKeepImagesWithDoc( bKeepImagesWithDoc ), m_bAutoAdjustLinks( bAutoAdjustLinks ), m_backgroundIndex(IgnoreImage) { } CEditSaveObject::~CEditSaveObject(){ XP_Bool bRevertBuffer = FALSE; XP_Bool bRelayout = FALSE; /* * possible values for m_finishedOpt: * ED_FINISHED_GOTO_NEW, Point the editor to the location of the * newly saved document if successful, otherwise revert buffer. * ED_FINISHED_REVERT_BUFFER, Revert the buffer to the state before * the save operation began, regardless. * ED_FINISHED_SAVE_DRAFT Like ED_FINISHED_REVERT_BUFFER, except clears the dirty flag * on success. * ED_FINISHED_MAIL_SEND If we succeed we're going to throw the buffer * away, so don't revert it, but don't bother gotoNew. * If failure, revert the buffer. * Used for mail compose, we don't * want the editor to start any operation that * causes problems when libmsg destroys the editor * context. */ if ( m_finishedOpt == ED_FINISHED_GOTO_NEW && m_status == ED_ERROR_NONE ) { //// Go to new location. m_pBuffer->m_pContext->is_new_document = FALSE; // Set the document title and history entry to the new location. char *pDestFileURL = GetDestAbsoluteURL(0); if ( !pDestFileURL ) { XP_ASSERT( 0 ); m_status = ED_ERROR_FILE_WRITE; // Not really the right thing to set. return; } // We used to do the ftp://username@path here, but it's moved to earlier in // the save process, in EDT_PublishFile. // Set this first - FE_SetDocTitle will get URL from it LO_SetBaseURL( m_pBuffer->m_pContext, pDestFileURL ); History_entry * hist_ent = SHIST_GetCurrent(&(m_pBuffer->m_pContext->hist)); char * pTitle = NULL; if(hist_ent) { if ( hist_ent->title && XP_STRLEN(hist_ent->title) > 0 ){ // Note: hist_ent->title should always = m_pContext->title? // Change "file:///Untitled" into URL // Use the new address if old title == old address if( 0 != XP_STRCMP(hist_ent->title, XP_GetString(XP_EDIT_NEW_DOC_NAME) ) && 0 != XP_STRCMP(hist_ent->title, hist_ent->address) ) { pTitle = XP_STRDUP(hist_ent->title); } else { pTitle = XP_STRDUP(pDestFileURL); XP_FREE(hist_ent->title); hist_ent->title = XP_STRDUP(pDestFileURL); } } else { // Use the URL if no Document title pTitle = XP_STRDUP(pDestFileURL); XP_FREEIF(hist_ent->title); hist_ent->title = XP_STRDUP(pDestFileURL); } // Test if new URL is same as current before we replace the latter XP_Bool bSameURL = EDT_IsSameURL( hist_ent->address, pDestFileURL, NULL, NULL); // Set history entry address to new URL XP_FREEIF(hist_ent->address); hist_ent->address = XP_STRDUP(pDestFileURL); // We may inherit this URL (used for faster reloading in browser) // when saving a remote URL to local disk, // so always clear it since Editor never uses it. XP_FREEIF(hist_ent->wysiwyg_url); // This changes doc title, window caption, history title, /// but not history address if (pTitle) { FE_SetDocTitle(m_pBuffer->m_pContext, pTitle); XP_FREE(pTitle); } if( !bSameURL ) { // Make the new URL (now in current history entry) // as the most-recently-used URL in prefs. list // (Note: We should never be here in mail message composer) EDT_SyncEditHistory(m_pBuffer->m_pContext); } } // if (hist_ent) XP_FREE(pDestFileURL); // Saved successfully. m_pBuffer->DocumentStored(); // This flag must be cleared before GetFileWriteTime, // else the write time will not be recorded m_pContext->edit_saving_url = FALSE; // Get the current file time m_pBuffer->GetFileWriteTime(); // Some of the links may have changed, so relayout bRelayout = TRUE; } else if ( m_finishedOpt == ED_FINISHED_MAIL_SEND && m_status == ED_ERROR_NONE ) { //// Do nothing. // Don't relayout, we're throwing the buffer away. } else if (m_pDocStateSave) { //// Don't go to the new document, revert to m_pDocStateSave. bRevertBuffer = TRUE; // Don't relayout, reverting the buffer will take care of it. } if ( m_finishedOpt == ED_FINISHED_SAVE_DRAFT && m_status == ED_ERROR_NONE ) { // Note that we are still reverting the buffer. // Saved successfully. m_pBuffer->DocumentStored(); } // Originally, this was only called if m_status == ED_ERROR_NONE. m_pBuffer->FileFetchComplete(m_status); if (bRelayout) { // Don't always relayout because we may have "cid:" links which cause an error. // reverting the buffer will take care of it. m_pBuffer->RefreshLayout(); } // Already cleared above if ED_FINISHED_GOTO_NEW and success, // but it must be cleared eventually in all cases. m_pContext->edit_saving_url = FALSE; m_pBuffer->m_pSaveObject = 0; if (bRevertBuffer) { // Must be after CEditBuffer::FileFetchComplete(), because it deletes CEditBuffer. m_pBuffer->RestoreState(m_pDocStateSave); } if( m_tapeFS->GetType() == ITapeFileSystem::Publish ){ XP_FREEIF(m_pFailedPublishUrl); if( m_status == ED_ERROR_NONE ){ // Save the last-used location into the history list edt_SyncPublishingHistory(m_pBuffer->m_pContext); } else { // Save this URL so we can supply the "bad" location // to user if they try to publish that URL again m_pFailedPublishUrl = XP_STRDUP(m_pSrcURL); } } XP_FREE( m_pSrcURL ); delete m_pDocStateSave; } // Add all images in document that are also in ppIncludedFiles. // // If ppIncludedFiles is NULL, this should add exactly those files that would be returned by // CEditBuffer::GetAllDocumentFiles() with the selected flag set TRUE. // i.e. Regular Saving should save exactly those files that are published by default in remote publishing. XP_Bool CEditSaveObject::AddAllFiles(char **ppIncludedFiles){ //////// WARNING: if you change this function, fix CEditBuffer::GetAllDocumentFiles and CEditSaveObject::FixupLinks() also. EDT_ImageData *pData; EDT_PageData *pPageData; // Make sure all URLs in ppIncludedFiles are absolute if (ppIncludedFiles) { char **ppFile = ppIncludedFiles; while (*ppFile) { char *pAbs = NET_MakeAbsoluteURL(m_pSrcURL,*ppFile); if (pAbs) { // If making *ppFile absolute changed it, replace it with absolute URL. if (XP_STRCMP(pAbs,*ppFile)) { XP_FREE(*ppFile); *ppFile = pAbs; } else { XP_FREE(pAbs); } } ppFile++; } } // Add actual HTML document. intn firstIndex = AddFile(m_pSrcURL,TEXT_HTML,m_pBuffer->GetDocCharSetID()); if (firstIndex != 0) { m_status = ED_ERROR_BAD_URL; FreeList(ppIncludedFiles); return FALSE; } CEditElement *pImage = m_pBuffer->m_pRoot->FindNextElement( &CEditElement::FindImage, 0 ); // If there is a background Image, make it the first image. pPageData = m_pBuffer->GetPageData(); if( pPageData ) { if ( pPageData->pBackgroundImage && *pPageData->pBackgroundImage){ m_backgroundIndex = CheckAddFile( pPageData->pBackgroundImage, NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !pPageData->bBackgroundNoSave ); } // We now support > 1 FontDefURL per page int iFontDefCount = m_pBuffer->m_FontDefURL.Size(); for( int i = 0; i < iFontDefCount; i++ ) { intn iIndex = CheckAddFile( m_pBuffer->m_FontDefURL[i], NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !m_pBuffer->m_FontDefNoSave[i] ); m_FontDefIndex.Add(iIndex); } #if 0 if ( pPageData->pFontDefURL && *pPageData->pFontDefURL ){ m_fontDefIndex = CheckAddFile( pPageData->pFontDefURL, NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !pPageData->bFontDefNoSave ); } #endif } m_pBuffer->FreePageData( pPageData ); while( pImage ) { // If this assert fails, it is probably because FinishedLoad // didn't get called on the image. And that's probably because // of some bug in the recursive FinishedLoad code. // If the size isn't known, the relayout after the save will block, // and the fourth .. nth images in the document will get zero size. // XP_ASSERT(pImage->Image()->SizeIsKnown()); pImage->Image()->m_iSaveIndex = IgnoreImage; pImage->Image()->m_iSaveLowIndex = IgnoreImage; pData = pImage->Image()->GetImageData(); if( pData ) { // Only consider images that are not server-generated if( EDT_IsImageURL(pData->pSrc) ) { pImage->Image()->m_iSaveIndex = CheckAddFile( pData->pSrc, NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !pData->bNoSave ); } if( EDT_IsImageURL(pData->pLowSrc) ) { pImage->Image()->m_iSaveLowIndex = CheckAddFile( pData->pLowSrc, NULL,ppIncludedFiles, m_bKeepImagesWithDoc && !pData->bNoSave ); } edt_FreeImageData( pData ); } pImage = pImage->FindNextElement( &CEditElement::FindImage, 0 ); } //// Sure would be nice to abstract all the different types of table //// backgrounds. // table backgrounds CEditElement *pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTable, 0 )) ){ EDT_TableData *pData = ((CEditTableElement *)pNext)->GetData(); if (pData) { if ( pData->pBackgroundImage && *pData->pBackgroundImage) { ((CEditTableElement *)pNext)->m_iBackgroundSaveIndex = CheckAddFile( pData->pBackgroundImage, NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !pData->bBackgroundNoSave); } CEditTableElement::FreeData(pData); } } // table row backgrounds pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableRow, 0 )) ){ EDT_TableRowData *pData = ((CEditTableRowElement *)pNext)->GetData(); if (pData) { if ( pData->pBackgroundImage && *pData->pBackgroundImage) { ((CEditTableRowElement *)pNext)->m_iBackgroundSaveIndex = CheckAddFile( pData->pBackgroundImage, NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !pData->bBackgroundNoSave); } CEditTableRowElement::FreeData(pData); } } // table cell backgrounds
pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableCell, 0 )) ){ EDT_TableCellData *pData = ((CEditTableCellElement *)pNext)->GetData(0); if (pData) { if ( pData->pBackgroundImage && *pData->pBackgroundImage) { ((CEditTableCellElement *)pNext)->m_iBackgroundSaveIndex = CheckAddFile( pData->pBackgroundImage, NULL, ppIncludedFiles, m_bKeepImagesWithDoc && !pData->bBackgroundNoSave); } CEditTableCellElement::FreeData(pData); } } // UnknownHTML tags with LOCALDATA attribute. pNext = m_pBuffer->m_pRoot; if ( pNext ) { while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindUnknownHTML, 0 )) ){ CEditIconElement *pIcon = CEditIconElement::Cast(pNext); if (pIcon) { char **pMimeTypes; char **pURLs; int count = pIcon->ParseLocalData(&pMimeTypes,&pURLs); // Remember indices into save object. if (count) { XP_FREEIF(pIcon->m_piSaveIndices); // allocate memory. pIcon->m_piSaveIndices = new intn[count]; } // Maybe should make a check that pURLs[n] is a relative URL // in current directory. for (int n = 0; n < count; n++) { pIcon->m_piSaveIndices[n] = CheckAddFile(pURLs[n],pMimeTypes[n],ppIncludedFiles,TRUE); } CEditIconElement::FreeLocalDataLists(pMimeTypes,pURLs,count); } } // while } // Go through ppIncludedFiles once more to add anything we missed. // // This is for the "add all files in directory" button in publishing, // here ppIncludedFiles may contain files that CEditSaveObject knows // nothing about. // // Doesn't hurt to add files twice to tape file system, it'll just // return the previous index. if (ppIncludedFiles) { char **pFile = ppIncludedFiles; while (*pFile) { // Ignore returned index, we're not going to update any links here. AddFile(*pFile,NULL,m_pBuffer->GetDocCharSetID()); pFile++; } } FreeList(ppIncludedFiles); return TRUE; } // Returns the URL that should be used as the base for relative URLs in // the document. Similar to just calling LO_GetBaseURL(), except returns // the document temp directory for untitled documents. // Must free returned memory. char *edt_GetDocRelativeBaseURL(MWContext *pContext) { char *pDocURL = NULL; // Interpret URLs relative to temp directory if a new document. if (EDT_IS_NEW_DOCUMENT(pContext)) { char *pxpURL = EDT_GetDocTempDir(pContext); if (!pxpURL) { return pDocURL; } // prepend "file://" to make a URL. StrAllocCat(pDocURL,"file://"); StrAllocCat(pDocURL,pxpURL); XP_FREE(pxpURL); } else { pDocURL = edt_StrDup(LO_GetBaseURL(pContext)); } return pDocURL; } // Mostly a wrapper for CFileSaveObject::AddFile, except deals with adjusting links. // // Analogous to AddToBufferUnique() in edtbuf.cpp intn CEditSaveObject::CheckAddFile( char *pSrc, char *pMIMEType, char **ppIncludedFiles, XP_Bool bSaveDefault ){ intn retVal = IgnoreImage; if( EDT_IS_LIVEWIRE_MACRO( pSrc ) ){ return IgnoreImage; } char *pDocURL = edt_GetDocRelativeBaseURL(m_pContext); if (!pDocURL) { return IgnoreImage; } char *pAbsoluteSrc = NET_MakeAbsoluteURL(pDocURL,pSrc); XP_FREE(pDocURL); if (!pAbsoluteSrc) { return IgnoreImage; } // If ppIncludedFiles is passed in, use it to decide whether to add file. // Otherwise, use bSaveDefault to decide. XP_Bool saveMe; if (ppIncludedFiles) { saveMe = URLInList(ppIncludedFiles,pAbsoluteSrc); } else { saveMe = bSaveDefault; } if (saveMe) { retVal = AddFile(pSrc,pMIMEType,m_pBuffer->GetDocCharSetID()); if (retVal == ITapeFileSystem::Error || retVal == ITapeFileSystem::SourceDestSame) { // couldn't make a good local name, so try to fix it. retVal = AttemptAdjust; } } else if ( m_bAutoAdjustLinks ) { retVal = AttemptAdjust; } XP_FREE(pAbsoluteSrc); return retVal; } void CEditSaveObject::FreeList(char **ppList) { if (!ppList) return; int n = 0; while (ppList[n]) { XP_FREE(ppList[n]); n++; } XP_FREE(ppList); } // ppList and pURL must be absolute URLs. XP_Bool CEditSaveObject::URLInList(char **ppList, char *pURL ) { for (int n = 0; ppList[n]; n++) { if (EDT_IsSameURL(ppList[n],pURL,NULL,NULL)) return TRUE; } return FALSE; } // Should be called no matter what, even if error or cancel occurs. ED_FileError CEditSaveObject::FileFetchComplete(){ XP_ASSERT(XP_IsContextInList(m_pBuffer->m_pContext)); return CFileSaveObject::FileFetchComplete(); } // Always return TRUE. XP_Bool CEditSaveObject::SaveFirstFile() { if ( m_status != ED_ERROR_NONE ) { XP_ASSERT(0); return TRUE; } XP_ASSERT(!m_pDocStateSave); m_pDocStateSave = m_pBuffer->RecordState(); char *pBadLinks = FixupLinks(); PRBool needEncrypt = EDT_EncryptState( m_pContext ); // Tell user some links may be broken by this operation, give choice // of continuing or not. if (pBadLinks) { XP_Bool bCancel = FALSE; char *tmplate = XP_GetString(XP_EDT_BREAKING_LINKS); char *msg = NULL; if (tmplate) { msg = PR_smprintf(tmplate,pBadLinks); bCancel = !FE_Confirm(m_pContext,msg); } XP_FREEIF(msg); XP_FREEIF(pBadLinks); if (bCancel) { Cancel(); return TRUE; } } // Write root HTML document char *pHumanName = m_tapeFS->GetHumanName(0); if (pHumanName == NULL) { XP_ASSERT(0); return TRUE; } // Tell front end the filename. if( !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878 m_tapeFS->GetType() != ITapeFileSystem::MailSend ){// Suppress dialog for mail send. FE_SaveDialogSetFilename( m_pContext, pHumanName ); } IStreamOut *out = NULL; URL_Struct *URL_s = NULL; if (needEncrypt) { // by the time the file gets to the tapeFS, it will be binary. m_tapeFS->SetFirstBinary(); out = new CNetStreamToTapeFS(m_pContext,m_tapeFS); } else { out = m_tapeFS->OpenStream(0); } if (out == NULL) { m_status = ED_ERROR_FILE_OPEN; } else { m_pBuffer->WriteToStream( out ); if ( out->Status() != IStreamOut::EOS_NoError ) { m_status = ED_ERROR_FILE_WRITE; } if (!needEncrypt) { m_tapeFS->CloseStream(0); } else { delete out; } } if( m_status != ED_ERROR_NONE ){ char *tmplate = NULL; char *msg = NULL; char *pAbsDest = GetDestAbsoluteURL(0); char *pAbsSrc = GetSrcName(0); edt_url_make_human(&pAbsDest); edt_url_make_human(&pAbsSrc); switch (m_tapeFS->GetType()) { case ITapeFileSystem::File: tmplate = XP_GetString(XP_EDT_ERR_SAVE_WRITING_ROOT); if (tmplate) { char *pRelDest = m_tapeFS->GetHumanName(0); msg = PR_smprintf(tmplate,pRelDest,pAbsDest); XP_FREEIF(pRelDest); } break; case ITapeFileSystem::Publish: tmplate = XP_GetString(XP_EDT_ERR_PUBLISH_PREPARING_ROOT); if (tmplate) { msg = PR_smprintf(tmplate,pAbsSrc); StrAllocCat(msg,XP_GetString(XP_EDT_ERR_CHECK_DISK)); } break; case ITapeFileSystem::MailSend: msg = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_PREPARING_ROOT)); break; } if (msg) { FE_Alert(m_pContext,msg); } XP_FREEIF(msg); XP_FREEIF(pAbsDest); XP_FREEIF(pAbsSrc); } // We know this is file number 1, CFileSaveObject::m_iCurFile is 1-based. CheckFinishedSave(1,m_status); XP_FREE(pHumanName); return TRUE; } void CEditSaveObject::FixupLink(intn iIndex, // index to tape file system char **ppImageURL, // Value to fixup. char *pDestPathURL, ED_HREFList *badLinks) { if( iIndex == IgnoreImage ){ return; } if( iIndex == AttemptAdjust ){ CEditLinkManager::AdjustLink(ppImageURL,m_pSrcURL,pDestPathURL,badLinks); } else { // Look it up from tape file system. char *pNewURL = GetDestName(iIndex); if (pNewURL) { XP_FREEIF(*ppImageURL); *ppImageURL = pNewURL; } } } char *CEditSaveObject::FixupLinks(){ //////// WARNING: if you change this function, fix CEditSaveObject::AddAllFiles() and CEditBuffer::GetAllDocumentFiles() also. ED_HREFList badLinks; EDT_ImageData *pData; char *pDestPathURL = m_tapeFS->GetDestPathURL(); // Note: pDestPathURL may legally be NULL. // If there is a background Image, make it the first one. FixupLink(m_backgroundIndex,&m_pBuffer->m_pBackgroundImage,pDestPathURL,&badLinks); // We now support > 1 FontDefURLs int iFontDefCount = m_pBuffer->m_FontDefURL.Size(); for( int i=0; i < iFontDefCount; i++ ) FixupLink(m_FontDefIndex[i], &m_pBuffer->m_FontDefURL[i], pDestPathURL, &badLinks); // regular images. CEditElement *pImage = m_pBuffer->m_pRoot->FindNextElement( &CEditElement::FindImage, 0 ); while( pImage ) { pData = pImage->Image()->GetImageData(); if( pData ) { // Only consider images that are not server-generated if( EDT_IsImageURL(pData->pSrc) ) { // do the normal image FixupLink(pImage->Image()->m_iSaveIndex,&pData->pSrc,pDestPathURL,&badLinks); } if( EDT_IsImageURL(pData->pLowSrc) ) { // do the lowres image FixupLink(pImage->Image()->m_iSaveLowIndex,&pData->pLowSrc,pDestPathURL,&badLinks); } pImage->Image()->SetImageData( pData ); edt_FreeImageData( pData ); } pImage = pImage->FindNextElement( &CEditElement::FindImage, 0 ); } //// Sure would be nice to abstract all the different types of table //// backgrounds. // table backgrounds CEditElement *pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTable, 0 )) ){ CEditTableElement *pTable = (CEditTableElement *)pNext; EDT_TableData *pData = pTable->GetData(); if (pData) { if ( pData->pBackgroundImage && *pData->pBackgroundImage) { FixupLink(pTable->m_iBackgroundSaveIndex,&pData->pBackgroundImage,pDestPathURL,&badLinks); pTable->SetData(pData); } CEditTableElement::FreeData(pData); } } // table row backgrounds pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableRow, 0 )) ){ CEditTableRowElement *pTable = (CEditTableRowElement *)pNext; EDT_TableRowData *pData = pTable->GetData(); if (pData) { if ( pData->pBackgroundImage && *pData->pBackgroundImage) { FixupLink(pTable->m_iBackgroundSaveIndex,&pData->pBackgroundImage,pDestPathURL,&badLinks); pTable->SetData(pData); } CEditTableRowElement::FreeData(pData); } } // table cell backgrounds
pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableCell, 0 )) ){ CEditTableCellElement *pTable = (CEditTableCellElement *)pNext; EDT_TableCellData *pData = pTable->GetData(0); if (pData) { if ( pData->pBackgroundImage && *pData->pBackgroundImage) { FixupLink(pTable->m_iBackgroundSaveIndex,&pData->pBackgroundImage,pDestPathURL,&badLinks); pTable->SetData(pData); } CEditTableCellElement::FreeData(pData); } } // UnknownHTML tags with LOCALDATA attribute. pNext = m_pBuffer->m_pRoot; while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindUnknownHTML, 0 )) ){ CEditIconElement *pIcon = CEditIconElement::Cast(pNext); if (pIcon) { char **pMimeTypes; char **pURLs; int count = pIcon->ParseLocalData(&pMimeTypes,&pURLs); if (count) { if (pIcon->m_piSaveIndices) { // Change all occurances of pURLs[n] in the unknown tag to // take account of the new location. for (int n = 0; n < count; n++) { char *pPrev = XP_STRDUP(pURLs[n]); // Remember, so we can se if pURLs[n] changes. FixupLink(pIcon->m_piSaveIndices[n],&pURLs[n],pDestPathURL,&badLinks); // Note: ReplaceParamValue checks if the two are the same. // Also note: the LOCALDATA parameter itself will be updated. pIcon->ReplaceParamValues(pPrev,pURLs[n]); XP_FREEIF(pPrev); } } else { XP_ASSERT(0); } } // kill and zero memory for saved indices. XP_FREEIF(pIcon->m_piSaveIndices); CEditIconElement::FreeLocalDataLists(pMimeTypes,pURLs,count); } } // while // Adjust the HREFs. if( m_bAutoAdjustLinks && pDestPathURL ){ // No longer use CEditLinkManager::AdjustAllLinks because its ref counting is // messed up and has more links than are currently in document. It adds too // many lines to badLinks (which is actually displayed to the user.) // m_pBuffer->linkManager.AdjustAllLinks( m_pSrcURL, pDestPathURL, &badLinks ); // Walk the tree and find all HREFs. CEditElement *pLeaf = m_pBuffer->m_pRoot->FindNextElement( &CEditElement::FindLeafAll,0 ); // First sweep, mark all HREFs as not adjusted. while (pLeaf) { m_pBuffer->linkManager.SetAdjusted(pLeaf->Leaf()->GetHREF(),FALSE); pLeaf = pLeaf->FindNextElement(&CEditElement::FindLeafAll,0 ); } // Second sweep, actually adjust the HREFs. pLeaf = m_pBuffer->m_pRoot->FindNextElement( &CEditElement::FindLeafAll,0 ); while (pLeaf) { ED_LinkId linkId = pLeaf->Leaf()->GetHREF(); if (linkId && !m_pBuffer->linkManager.GetAdjusted(linkId)) { // linkManager can deal with NULL HREF. m_pBuffer->linkManager.AdjustLink(linkId,m_pSrcURL, pDestPathURL, &badLinks ); m_pBuffer->linkManager.SetAdjusted(linkId,TRUE); } pLeaf = pLeaf->FindNextElement(&CEditElement::FindLeafAll,0 ); } } XP_FREEIF(pDestPathURL); // Package up a string of all the bad links found. char *pRet = NULL; if (badLinks.Size() > 0) { int n; for (n = 0; n < badLinks.Size(); n++) { StrAllocCat(pRet,badLinks[n]); StrAllocCat(pRet,"\n"); XP_FREEIF(badLinks[n]); } } return pRet; } #if 0 //// Now obsolete //----------------------------------------------------------------------------- // CEditImageSaveObject //----------------------------------------------------------------------------- CEditImageSaveObject::CEditImageSaveObject( CEditBuffer *pBuffer, EDT_ImageData *pData, XP_Bool bReplaceImage ) : CFileSaveObject( pBuffer->m_pContext ), m_pBuffer( pBuffer ), m_pData( edt_DupImageData( pData ) ), m_srcIndex(-1), m_lowSrcIndex(-1), m_bReplaceImage( bReplaceImage ) { // we are going to write the image files into SetDestPathURL( LO_GetBaseURL( pBuffer->m_pContext ) ); } CEditImageSaveObject::~CEditImageSaveObject(){ edt_FreeImageData( m_pData ); m_pBuffer->m_pSaveObject = 0; } ED_FileError CEditImageSaveObject::FileFetchComplete(){ if( m_status == ED_ERROR_NONE ){ char *pName = 0; if( m_srcIndex != -1 && (pName = GetDestName(m_srcIndex)) != 0 ){ if( m_pData->pSrc) XP_FREE( m_pData->pSrc ); m_pData->pSrc = pName; } if( m_lowSrcIndex != -1 && (pName = GetDestName(m_lowSrcIndex)) != 0 ){ XP_FREE( m_pData->pLowSrc ); m_pData->pLowSrc = pName; } m_pBuffer->m_pLoadingImage = new CEditImageLoader( m_pBuffer, m_pData, m_bReplaceImage ); m_pBuffer->m_pLoadingImage->LoadImage(); } return CFileSaveObject::FileFetchComplete(); } #endif #if 0 // now obsolete //----------------------------------------------------------------------------- // CEditBackgroundImageSaveObject //----------------------------------------------------------------------------- CEditBackgroundImageSaveObject::CEditBackgroundImageSaveObject( CEditBuffer *pBuffer) : CFileSaveObject( pBuffer->m_pContext ), m_pBuffer( pBuffer ) { // we are going to write the image files into SetDestPathURL( LO_GetBaseURL( pBuffer->m_pContext ) ); } CEditBackgroundImageSaveObject::~CEditBackgroundImageSaveObject(){ m_pBuffer->m_pSaveObject = 0; } ED_FileError CEditBackgroundImageSaveObject::FileFetchComplete(){ // Note: If user canceled in overwrite dialog, then we don't change the background if( m_status == ED_ERROR_NONE ){ if( m_pBuffer->m_pBackgroundImage ) XP_FREE(m_pBuffer->m_pBackgroundImage); // Get the one and only image file (without local or URL path) m_pBuffer->m_pBackgroundImage = GetDestName(0); // This will set background image m_pBuffer->RefreshLayout(); // LO_SetBackgroundImage( m_pBuffer->m_pContext, m_pBuffer->m_pBackgroundImage ); } return CFileSaveObject::FileFetchComplete(); } #endif // // Sigh, why did the editor invent a new streaming type? This function // allows the preencrypted file standard streamer to point back to the // Editor's stream without turning the C++ virus loose on the security // library. // typedef struct edtPrivStructStr { ITapeFileSystem *tapeFS; IStreamOut *out; } edtPrivStruct; static unsigned int edt_TapeIsReady(NET_StreamClass * /* stream */ ) { return MAX_WRITE_READY; } static void edt_TapeComplete(NET_StreamClass *stream) { edtPrivStruct *obj = (edtPrivStruct *)stream->data_object; obj->tapeFS->CloseStream(0); XP_FREE(obj); return; } static void edt_TapeAbort(NET_StreamClass *stream, int /* status */) { edt_TapeComplete(stream); } static int edt_TapeWrite(NET_StreamClass *stream, char *str, int32 len) { edtPrivStruct *obj= (edtPrivStruct *)stream->data_object; obj->out->Write(str,len); if ( obj->out->Status() != IStreamOut::EOS_NoError ) { return -1; } return 0; } extern "C" NET_StreamClass *EDT_NetToTape(void *data) { NET_StreamClass *stream; edtPrivStruct *obj; stream = (NET_StreamClass *) XP_ALLOC(sizeof(NET_StreamClass)); if (stream == NULL) { return NULL; } obj = (edtPrivStruct *)XP_ALLOC(sizeof(edtPrivStruct)); if (obj == NULL) { XP_FREE(stream); return NULL; } stream->name = "Editor Tape FS stream"; stream->complete = edt_TapeComplete; stream->abort = edt_TapeAbort; stream->is_write_ready = edt_TapeIsReady; stream->data_object = obj; stream->window_id = NULL; // if this were a real stream we would need a // url and a window context. stream->put_block = (MKStreamWriteFunc)edt_TapeWrite; obj->tapeFS = (ITapeFileSystem *)data; obj->out = obj->tapeFS->OpenStream(0); if (obj->out == NULL) { XP_FREE(obj); XP_FREE(stream); return NULL; } return stream; } #endif