home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / edtsave.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  59.8 KB  |  1,754 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20. //
  21. // Editor save stuff. LWT 06/01/95
  22. //
  23. // Added all ITapeFileSystem stuff, hardts 10/96
  24.  
  25. #ifdef EDITOR
  26.  
  27. #include "editor.h"
  28. #include "streams.h"
  29. #include "shist.h"
  30. #include "xpgetstr.h"
  31. #include "net.h"
  32. #include "mime.h"
  33. #include "prefapi.h"
  34.  
  35. // Other error values are created from ALLXPSTR.RC during compiling
  36. //extern "C" int MK_UNABLE_TO_LOCATE_FILE;
  37. //extern "C" int MK_MALFORMED_URL_ERROR;
  38.  
  39. //-----------------------------------------------------------------------------
  40. // CEditImageLoader
  41. //-----------------------------------------------------------------------------
  42. CEditImageLoader::CEditImageLoader( CEditBuffer *pBuffer, EDT_ImageData *pImageData, 
  43.                 XP_Bool bReplaceImage ): 
  44.         m_pBuffer( pBuffer ),
  45.         m_pImageData( edt_DupImageData( pImageData ) ),
  46.         m_bReplaceImage( bReplaceImage )
  47.         {
  48.     char *pStr;
  49.     if( !pBuffer->m_pContext->is_new_document ){
  50.         intn retVal = NET_MakeRelativeURL( LO_GetBaseURL( m_pBuffer->m_pContext ), 
  51.                     m_pImageData->pSrc, 
  52.                     &pStr );
  53.         
  54.         if( retVal != NET_URL_FAIL ){
  55.             XP_FREE( m_pImageData->pSrc );
  56.             m_pImageData->pSrc = pStr;
  57.         }
  58.  
  59.         retVal = NET_MakeRelativeURL( LO_GetBaseURL( m_pBuffer->m_pContext ), 
  60.                     m_pImageData->pLowSrc, 
  61.                     &pStr );
  62.         
  63.         if( retVal != NET_URL_FAIL ){
  64.             XP_FREE( m_pImageData->pLowSrc );
  65.             m_pImageData->pLowSrc = pStr;
  66.         }
  67.  
  68.     }
  69. }
  70.  
  71. CEditImageLoader::~CEditImageLoader(){
  72.     m_pBuffer->m_pLoadingImage = 0;
  73.     FE_ImageLoadDialogDestroy( m_pBuffer->m_pContext );
  74.     FE_EnableClicking( m_pBuffer->m_pContext );
  75.     edt_FreeImageData( m_pImageData );
  76. }
  77.  
  78. void CEditImageLoader::LoadImage(){
  79.     char *pStr;
  80.     XP_ObserverList image_obs_list; // List of image observers.
  81.  
  82.     m_pLoImage = LO_NewImageElement( m_pBuffer->m_pContext );
  83.     
  84.     // convert the URL to an absolute path
  85.     pStr = NET_MakeAbsoluteURL( LO_GetBaseURL( m_pBuffer->m_pContext ), 
  86.                 m_pImageData->pSrc );
  87.     m_pLoImage->image_url = PA_strdup( pStr );
  88.     XP_FREE( pStr );
  89.  
  90.     // convert the URL to an absolute path
  91.     pStr = NET_MakeAbsoluteURL( LO_GetBaseURL( m_pBuffer->m_pContext ), 
  92.                 m_pImageData->pLowSrc );
  93.     m_pLoImage->lowres_image_url = PA_strdup( pStr );
  94.     XP_FREE( pStr );
  95.  
  96.     // tell layout to  pass the size info back to us.
  97.     m_pLoImage->ele_id = ED_IMAGE_LOAD_HACK_ID;  
  98.     FE_ImageLoadDialog( m_pBuffer->m_pContext );
  99.  
  100.     image_obs_list = lo_NewImageObserverList(m_pBuffer->m_pContext,
  101.                                              m_pLoImage);
  102.     if (!image_obs_list) {
  103.         XP_ASSERT(FALSE);        // Out of memory.
  104.         return;
  105.     }
  106.     lo_GetImage(m_pBuffer->m_pContext, m_pBuffer->m_pContext->img_cx,
  107.                 m_pLoImage, image_obs_list, NET_DONT_RELOAD);
  108. }
  109.  
  110.  
  111. // The image library is telling us the size of the image.  Actually do the 
  112. //  insert now.
  113. void CEditImageLoader::SetImageInfo(int32 ele_id, int32 width, int32 height){
  114.     if( m_pImageData->iHeight == 0 || m_pImageData->iWidth == 0 ){
  115.         m_pImageData->iHeight = height;
  116.         m_pImageData->iWidth = width;    
  117.         m_pImageData->iOriginalHeight = height;
  118.         m_pImageData->iOriginalWidth = width;    
  119.     }
  120.     if( m_bReplaceImage ){
  121.         // whack this in here...
  122.         m_pBuffer->m_pCurrent->Image()->SetImageData( m_pImageData );
  123.         // we could be doing a better job here.  We probably repaint too much...
  124.         CEditElement *pContainer = m_pBuffer->m_pCurrent->FindContainer();
  125.         m_pBuffer->Relayout( pContainer, 0, pContainer->GetLastMostChild(), 
  126.                     RELAYOUT_NOCARET );
  127.         m_pBuffer->SelectCurrentElement();
  128.     }
  129.     else {
  130.         m_pBuffer->InsertImage( m_pImageData );
  131.     }
  132.     delete this;
  133. }
  134.  
  135. //-----------------------------------------------------------------------------
  136. // CEditSaveObject
  137. //-----------------------------------------------------------------------------
  138. #if defined(XP_OS2)
  139. extern "C"
  140. #else
  141. PRIVATE
  142. #endif
  143. unsigned int edt_file_save_stream_write_ready( NET_StreamClass *stream ){
  144.     return MAX_WRITE_READY;
  145. }
  146.  
  147. #if defined(XP_OS2)
  148. extern "C"
  149. #else
  150. PRIVATE
  151. #endif
  152. int edt_FileSaveStreamWrite (NET_StreamClass *stream, const char *block, int32 length) {
  153.     CFileSaveObject *pSaver = (CFileSaveObject*)stream->data_object;    
  154.     return pSaver->NetStreamWrite( block, length );
  155. }
  156.  
  157. #if defined(XP_OS2)
  158. extern "C"
  159. #else
  160. PRIVATE
  161. #endif
  162. void edt_file_save_stream_complete( NET_StreamClass *stream ){    
  163. }
  164.  
  165. #if defined(XP_OS2)
  166. extern "C"
  167. #else
  168. PRIVATE
  169. #endif
  170. void edt_file_save_stream_abort (NET_StreamClass *stream, int status) {    
  171. }
  172.  
  173.  
  174. #if defined(XP_OS2)
  175. extern "C"
  176. #else
  177. PRIVATE
  178. #endif
  179. void edt_UrlExit( URL_Struct *pURL, int status, MWContext *context )
  180. {
  181.     // hardts, changed from CEditSaveObject to CFileSaveObject
  182.     ((CFileSaveObject*)(pURL->fe_data))->NetFetchDone(pURL, status, context );
  183.  
  184.     NET_FreeURLStruct(pURL);
  185. }
  186.  
  187. #if defined(XP_OS2)
  188. extern "C"
  189. #else
  190. PRIVATE
  191. #endif
  192. NET_StreamClass * edt_MakeFileSaveStream (int format_out, void *closure,
  193.     URL_Struct *url, MWContext *context ) {
  194.  
  195.     NET_StreamClass *stream;
  196.     CFileSaveObject *pSaver = (CFileSaveObject*)url->fe_data;
  197.     
  198.     // OpenOutputFile will return 0 if OK
  199.     if( !pSaver || pSaver->OpenOutputFile() != 0  ){
  200.         return (NULL);
  201.     }
  202.  
  203.      // ### mwelch Relay the information stored in this URL. We do it here instead of
  204.      //            in the url exit routine because edt_MakeFileSaveStream could
  205.      //            have been invoked by an inferior URL which represents a parsed
  206.      //            piece of a mail or news message. If this URL is what was originally
  207.      //            invoked, the correct mime type should still be present here.    
  208.     if (url)
  209.          pSaver->CopyURLInfo(url);
  210.    
  211.     TRACEMSG(("Setting up attachment stream. Have URL: %s\n", url->address));
  212.  
  213.     stream = XP_NEW (NET_StreamClass);
  214.     if (stream == NULL) 
  215.         return (NULL);
  216.  
  217.     XP_MEMSET (stream, 0, sizeof (NET_StreamClass));
  218.  
  219.     stream->name           = "editor file save";
  220.     stream->complete       = edt_file_save_stream_complete;
  221.     stream->abort          = edt_file_save_stream_abort;
  222.     stream->put_block      = edt_FileSaveStreamWrite;
  223.     stream->is_write_ready = edt_file_save_stream_write_ready;
  224.     stream->data_object    = url->fe_data;
  225.     stream->window_id      = context;
  226.  
  227.     return stream;
  228. }
  229.  
  230. static XP_Bool bNetRegistered = FALSE;
  231.  
  232. #ifdef MOZ_MAIL_NEWS
  233. extern "C" 
  234. {
  235.     NET_StreamClass *MIME_MessageConverter (int format_out, void *closure,
  236.                                                URL_Struct *url,
  237.                                                MWContext *context);
  238. }
  239. #endif
  240.  
  241. //-----------------------------------------------------------------------------
  242. // CFileSaveObject
  243. //-----------------------------------------------------------------------------
  244.  
  245. CFileSaveObject::CFileSaveObject( MWContext *pContext, char *pSrcURL, 
  246.                                   ITapeFileSystem *tapeFS, XP_Bool bAutoSave,
  247.                                   CEditSaveToTempData *pSaveToTempData) : 
  248.         m_pContext( pContext ) ,
  249.         m_bOverwriteAll( FALSE ),
  250.         m_bDontOverwriteAll( FALSE ),
  251.         m_bDontOverwrite( FALSE ),
  252.         m_iCurFile(0),
  253.         m_pOutStream(0),
  254.         m_bOpenOutputHandledError(FALSE),
  255.         m_tapeFS(tapeFS),
  256.         m_status( ED_ERROR_NONE ),
  257.         m_bFromAutoSave(bAutoSave),
  258.         m_pSaveToTempData(pSaveToTempData)
  259. {
  260.     if( !bNetRegistered ){
  261. #ifdef MOZ_MAIL_NEWS
  262.         // ### mwelch Need to pass mail/news messages through
  263.         //     MIME_MessageConverter since we're likely to be
  264.         //     extracting a part of the message.
  265.         NET_RegisterContentTypeConverter( MESSAGE_RFC822, FO_EDT_SAVE_IMAGE,
  266.                                             NULL, MIME_MessageConverter);
  267.         NET_RegisterContentTypeConverter( MESSAGE_NEWS, FO_EDT_SAVE_IMAGE,
  268.                                             NULL, MIME_MessageConverter);
  269. #endif
  270.  
  271.         // For everything else, just write to the tapefs.
  272.         NET_RegisterContentTypeConverter( "*", FO_EDT_SAVE_IMAGE,
  273.                         NULL, edt_MakeFileSaveStream );
  274. //// Why is this duplicated? hardts
  275. #if defined(XP_WIN) || defined(XP_OS2)
  276.         NET_RegisterContentTypeConverter( "*", FO_EDT_SAVE_IMAGE,
  277.                         NULL, edt_MakeFileSaveStream );
  278. #else
  279. NET_RegisterContentTypeConverter( "*", FO_EDT_SAVE_IMAGE,
  280.                 NULL, edt_MakeFileSaveStream );
  281. #endif /* XP_WIN */
  282.         bNetRegistered = TRUE;
  283.     }
  284.  
  285.     // MUST DISABLE WINDOW INTERACTION HERE!
  286.     // (It will always be cleared in the GetURLExitRoutine)
  287.     // (This is normally set TRUE by front end before calling NET_GetURL)
  288.     m_pContext->waitingMode = TRUE;
  289.  
  290.     // Set a flag so we know when we are saving a file
  291.     // (also set in CEditBuffer::SaveFile() - may not be needed here, but its safe to set again)
  292.     m_pContext->edit_saving_url = TRUE;
  293.  
  294.     // set base URL for the tape file system.
  295.     char *pBaseURL = edt_GetDocRelativeBaseURL(m_pContext);
  296.     if (pBaseURL) {
  297.       m_tapeFS->SetSourceBaseURL(pBaseURL);
  298.       XP_FREE(pBaseURL);
  299.     }
  300. }
  301.  
  302. CFileSaveObject::~CFileSaveObject(){
  303.     // Success message for publishing.  Moved from WinFE to back end.
  304.     if (m_tapeFS->GetType() == ITapeFileSystem::Publish && 
  305.         m_status == ED_ERROR_NONE) {
  306.       char *msg = NULL;
  307.       // Different message for 1 or multiple files.
  308.       if (m_tapeFS->GetNumFiles() > 1) {
  309.         char *tmplate = XP_GetString(XP_EDT_PUBLISH_REPORT_MSG);
  310.         if (tmplate) {
  311.           msg = PR_smprintf(tmplate,m_tapeFS->GetNumFiles());
  312.         }
  313.       }
  314.       else {
  315.         msg = XP_STRDUP(XP_GetString(XP_EDT_PUBLISH_REPORT_ONE_FILE));
  316.       }
  317.       if (msg) {
  318.         char * pBrowse = NULL, *pPub = NULL, *pLastPub = NULL;
  319.         PREF_CopyCharPref("editor.publish_browse_location",&pBrowse);
  320.         PREF_CopyCharPref("editor.publish_location",&pPub);
  321.         PREF_CopyCharPref("editor.publish_last_loc",&pLastPub);
  322.  
  323. #if defined(XP_WIN) || defined(XP_OS2)
  324.         /* FE_LoadUrl needs to be implemented in X */
  325.     
  326.         if( EDT_IsSameURL(pPub, pLastPub,0,0) && pBrowse && *pBrowse ){
  327.           // We just saved to the default home location
  328.           StrAllocCat(msg, XP_GetString(XP_EDT_BROWSE_TO_DEFAULT));
  329.           if( FE_Confirm(m_pContext,msg) ){
  330.             // User said Yes - load location into a BROWSER window (FALSE param)
  331.             FE_LoadUrl(pBrowse, FALSE );
  332.           }
  333.         } else {
  334.           FE_Alert(m_pContext,msg);
  335.         }
  336.         XP_FREEIF(pBrowse);
  337.         XP_FREEIF(pPub);
  338. #else
  339.         // mac and unix
  340.         FE_Message(m_pContext,msg);
  341. #endif
  342.         XP_FREE(msg);
  343.       }
  344.     }
  345.  
  346.     // LOU: What should I do here.  There appear to be connections on the 
  347.     //  window that are still open....
  348.     if( NET_AreThereActiveConnectionsForWindow( m_pContext ) ){
  349.         //XP_ASSERT(FALSE);
  350.         //NET_SilentInterruptWindow( m_pContext );
  351.         XP_TRACE(("NET_AreThereActiveConnectionsForWindow is TRUE in ~CFileSaveObject"));
  352.     }
  353.  
  354.     ////XP_FREE( m_pDestPathURL );
  355.  
  356.     // Allow front end interaction
  357.     m_pContext->waitingMode = FALSE;
  358.     // Clear flag (also done in CEditBuffer::FinishedLoad2)
  359.     m_pContext->edit_saving_url = FALSE;
  360.     
  361. #ifdef XP_WIN    
  362.     // Tell front end we finished autosave
  363.     //   so it can gray the Save toolbar button
  364.     if( m_bFromAutoSave )
  365.         FE_UpdateEnableStates(m_pContext);
  366. #endif
  367.  
  368.     // Tape File system should not be deleted yet.
  369.     if (m_tapeFS && 
  370.         m_tapeFS->GetType() == ITapeFileSystem::Publish &&
  371.         m_status == ED_ERROR_NONE) {
  372.       // Call publishComplete event.
  373.       char *pDestURL = m_tapeFS->GetDestAbsURL();
  374.       EDT_PerformEvent(m_pContext,"publishComplete",pDestURL,TRUE,FALSE,0,0);
  375.       XP_FREEIF(pDestURL);
  376.     }
  377.  
  378.     ///// This code will have to change if we implement a "saveComplete" event for
  379.     ///// composer plugins.
  380.     // Call callback for saving file to temporary location.
  381.     if (m_tapeFS &&
  382.         m_tapeFS->GetType() == ITapeFileSystem::File &&
  383.         m_pSaveToTempData && m_pSaveToTempData->doneFn) {
  384.       if (m_status == ED_ERROR_NONE) {
  385.         (*m_pSaveToTempData->doneFn)(m_pSaveToTempData->pFileURL,m_pSaveToTempData->hook);
  386.       }
  387.       else {
  388.         // Failed, pass in NULL as the temporary file name.
  389.         (*m_pSaveToTempData->doneFn)(NULL,m_pSaveToTempData->hook);
  390.       }
  391.     }
  392.     delete m_pSaveToTempData;
  393. }
  394.  
  395. // Callback from tape file system.
  396. //
  397. //** Don't make this function static because it is a friend of CFileSaveObject
  398. //** so it must be extern.  (at least on MAC)
  399. void edt_CFileSaveObjectDone(XP_Bool success,void *arg) {
  400.     CFileSaveObject *saveObject = (CFileSaveObject *)arg;
  401.     if (!saveObject) {
  402.         XP_ASSERT(0);
  403.         return;
  404.     }        
  405.     
  406.     if (!success) {
  407.         saveObject->m_status = ED_ERROR_TAPEFS_COMPLETION;
  408.     }
  409.     // Tape File system should not be deleted yet.
  410.     delete saveObject;
  411. }
  412.  
  413. void
  414. CFileSaveObject::CopyURLInfo(const URL_Struct *pURL)
  415. {
  416.     XP_ASSERT((m_tapeFS) && (m_iCurFile <= m_tapeFS->GetNumFiles()));
  417.     
  418.     if ((m_tapeFS) && (m_iCurFile <= m_tapeFS->GetNumFiles()))
  419.         m_tapeFS->CopyURLInfo(m_iCurFile-1, pURL);
  420. }
  421.  
  422. ED_FileError CFileSaveObject::FileFetchComplete() {
  423.     XP_ASSERT(m_tapeFS->GetNumFiles());
  424.  
  425.     if( !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878
  426.         m_tapeFS->GetType() != ITapeFileSystem::MailSend ){  // Suppress dialog for mail send.
  427.         FE_SaveDialogDestroy( m_pContext, m_status, 0);
  428.     }
  429.     if (!m_tapeFS) {
  430.         XP_ASSERT(0);
  431.         return ED_ERROR_BLOCKED;  // is this the right thing to return?
  432.     }
  433.  
  434.     ED_FileError statusResult = m_status;
  435.  
  436.     m_tapeFS->Complete( m_status == ED_ERROR_NONE, 
  437.                                         edt_CFileSaveObjectDone, (void *)this );
  438.  
  439.     // "this" may already be deleted at this point, so we can't just return m_status.
  440.     return statusResult;
  441. }
  442.  
  443. ED_FileError CFileSaveObject::SaveFiles(){
  444.     XP_ASSERT(m_tapeFS && m_tapeFS->GetNumFiles());
  445.  
  446.     //     Next param indicates if we are downloading (FALSE)
  447.     //     or uploading (TRUE), so this can be used for publishing
  448.     if( !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878
  449.         m_tapeFS->GetType() != ITapeFileSystem::MailSend ){ // Suppress dialog for mail send.
  450.       ED_SaveDialogType saveType;
  451.       switch (m_tapeFS->GetType()) {
  452.         case ITapeFileSystem::File:
  453.           saveType = ED_SAVE_DLG_SAVE_LOCAL;
  454.           break;
  455.         case ITapeFileSystem::Publish:
  456.           saveType = ED_SAVE_DLG_PREPARE_PUBLISH;
  457.           break;
  458.         default:
  459.           XP_ASSERT(0);
  460.       }
  461.       FE_SaveDialogCreate( m_pContext, m_tapeFS->GetNumFiles(), saveType );
  462.     }
  463.  
  464.     if (SaveFirstFile()) {
  465.         // Child of CFileSaveObject took care of the first file.
  466.         m_iCurFile++;
  467.     }
  468.  
  469.     // loop through the tree getting all the image elements...
  470.     return FetchNextFile();
  471. }
  472.  
  473. // Just a wrapper around the Tape File System.
  474. intn CFileSaveObject::AddFile( char *pSrcURL, char *pMIMEType, int16 iDocCharSetID){
  475.     XP_ASSERT(m_tapeFS);
  476.     return m_tapeFS->AddFile(pSrcURL,pMIMEType,iDocCharSetID);
  477. }
  478.  
  479. //
  480. // open the file and get the Netlib to fetch it from the cache.
  481. //
  482. ED_FileError CFileSaveObject::FetchNextFile(){
  483.     XP_ASSERT(m_tapeFS);    
  484.  
  485.     if( m_status != ED_ERROR_NONE || m_iCurFile >= m_tapeFS->GetNumFiles() ){
  486.         return FileFetchComplete();
  487.     }    
  488.     // m_iCurFile is 1-based, so i is 0-based.
  489.     int i = m_iCurFile++;
  490.     
  491.     char *srcURL = m_tapeFS->GetSourceURL(i);
  492.     if (!srcURL) {
  493.         XP_ASSERT(0);
  494.         return ED_ERROR_FILE_READ;
  495.     }
  496.     URL_Struct *pUrl;
  497.     pUrl= NET_CreateURLStruct( srcURL, NET_DONT_RELOAD );
  498.     if (!pUrl) {
  499.         XP_ASSERT(0);
  500.         XP_FREE(srcURL);
  501.         return ED_ERROR_FILE_READ;    
  502.     }    
  503.     XP_FREE(srcURL);    
  504.     // Store file save object so we can access OpenOutputFile etc.
  505.     pUrl->fe_data = this;
  506.     NET_GetURL( pUrl, FO_EDT_SAVE_IMAGE, m_pContext, edt_UrlExit );
  507.     return m_status;
  508. }
  509.  
  510. void CFileSaveObject::CheckFinishedSave(intn oneBased,ED_FileError iError) // one-based index.
  511. {
  512.   if (m_tapeFS->IsLocalPersistentFile(oneBased - 1)) {
  513.     char *pAbsURL = GetDestAbsoluteURL(oneBased - 1);
  514.     if (pAbsURL) {
  515.       // Tell the front end that a new local file has been created.
  516.       // We use number to tell between Main image (1) and Low-Res image (2)
  517.       FE_FinishedSave( m_pContext, iError, pAbsURL, oneBased );
  518.       XP_FREE(pAbsURL);
  519.     }
  520.   }
  521. }
  522.  
  523. void CFileSaveObject::NetFetchDone( URL_Struct *pUrl, int status, MWContext *pContext ){
  524.     // close the file in any case.
  525.     if( m_pOutStream ){
  526.         m_tapeFS->CloseStream(m_iCurFile-1);
  527.         m_pOutStream = 0;
  528.     }
  529.     // Default: No error, show destination name for most errors
  530.     ED_FileError iError = ED_ERROR_NONE;
  531.  
  532.     // NOTE: This is the NETLIB error code: (see merrors.h) 
  533.     //   < 0 is an error
  534.     if( status < 0 ){
  535.         // As we become familiar with typical errors,
  536.         //  add specialized error types here
  537.         //  Use " extern MK_..." to access the error value
  538.         if( status == MK_UNABLE_TO_LOCATE_FILE ||
  539.             status == MK_MALFORMED_URL_ERROR ) {
  540.             // Couldn't find source -- use source filename
  541.             //   for error report
  542.             iError = ED_ERROR_SRC_NOT_FOUND;
  543.         } else if( m_bDontOverwrite ) {
  544.             // We get status = MK_UNABLE_TO_CONVERT 
  545.             //  if user said NO to overwriting an existing file:
  546.             //  edt_MakeFileSaveStream returns 0,
  547.             //  making NET_GetURL think it failed,
  548.             //   but its not really an error
  549.             iError = ED_ERROR_NONE;
  550.         } else if( m_status == ED_ERROR_CANCEL ) {
  551.             // User selected CANCEL at overwrite warning dialog,
  552.             //  thus stream was not created and returns error status,
  553.             //  but its not really an error
  554.             iError = ED_ERROR_CANCEL;
  555.         } else {
  556.             // "Unknown" error default
  557.             iError = ED_ERROR_FILE_READ;
  558.         }
  559.     }
  560.  
  561.     // ED_ERROR_FILE_OPEN was already handled in CFileSaveObject::OpenOutputFile()
  562.     if (!m_bOpenOutputHandledError) {
  563.       // No longer uses FE_SaveErrorContinueDialog()
  564.       if (!SaveErrorContinueDialog(iError) ) {
  565.         Cancel();
  566.       }
  567.       CheckFinishedSave(m_iCurFile,iError);
  568.     }
  569.  
  570.     FetchNextFile();
  571. }
  572.  
  573. intn CFileSaveObject::OpenOutputFile(){
  574.     m_bOpenOutputHandledError = FALSE;
  575.  
  576.     // Return if we already have an open stream
  577.     if( m_pOutStream != NULL){
  578.         return 0;
  579.     }
  580.     // Current number incremented before comming here,
  581.     //  or think of it as 1-based counting
  582.     int i = m_iCurFile-1;
  583.     
  584.     XP_Bool bWriteThis = TRUE;
  585.     char *pHumanName = m_tapeFS->GetHumanName(i);
  586.  
  587.     // Tell front end the filename even if we are not writing the file
  588.     //
  589.     if (!m_bDontOverwriteAll && 
  590.         !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878
  591.         m_tapeFS->GetType() != ITapeFileSystem::MailSend) { // Suppress dialog for mail send.
  592.         FE_SaveDialogSetFilename( m_pContext, pHumanName );
  593.     } 
  594.  
  595.     m_bDontOverwrite = m_bDontOverwriteAll;
  596.  
  597.     if( !m_bOverwriteAll){
  598.         if( m_tapeFS->FileExists(i) ){
  599.             // file exists.  
  600.             if( m_bDontOverwriteAll ){
  601.                 bWriteThis = FALSE;
  602.                 CheckFinishedSave(m_iCurFile,ED_ERROR_FILE_EXISTS);
  603.             }
  604.             else {
  605.                 ED_SaveOption e;
  606.                 e = FE_SaveFileExistsDialog( m_pContext, pHumanName);
  607.  
  608.                 switch( e ){
  609.                 case ED_SAVE_OVERWRITE_THIS:
  610.                     break;
  611.                 case ED_SAVE_OVERWRITE_ALL:
  612.                     m_bOverwriteAll = TRUE;
  613.                     break;
  614.                 case ED_SAVE_DONT_OVERWRITE_ALL:
  615.                     m_bDontOverwriteAll = TRUE;
  616.                     // intentionally fall through
  617.                 case ED_SAVE_DONT_OVERWRITE_THIS:
  618.                     // This flag prevents us from reporting
  619.                     //  "failed" file stream creation as an error
  620.                     m_bDontOverwrite = TRUE;
  621.                     bWriteThis = FALSE;
  622.  
  623.                     CheckFinishedSave(m_iCurFile,ED_ERROR_FILE_EXISTS);
  624.                     break;
  625.                 case ED_SAVE_CANCEL:
  626.                     Cancel();
  627.                     bWriteThis = FALSE;
  628.                 }
  629.             }
  630.         }
  631.     }
  632.  
  633.     if( bWriteThis ){
  634.        m_pOutStream = m_tapeFS->OpenStream( i );
  635.     }
  636.     else {
  637.         // We are returning, not doing the save, but not an error condition
  638.         XP_FREE(pHumanName);
  639.         return -1;
  640.     }
  641.  
  642.     XP_TRACE(("Opening stream number %d gives %d.", i, (int)m_pOutStream));
  643.     
  644.     if ( m_pOutStream == NULL ) {
  645.         // No longer uses FE_SaveErrorContinueDialog()
  646.         if(!SaveErrorContinueDialog( ED_ERROR_FILE_OPEN ) ){
  647.             Cancel();
  648.         }
  649.         XP_FREE(pHumanName);
  650.  
  651.         CheckFinishedSave(m_iCurFile,ED_ERROR_FILE_OPEN);
  652.  
  653.         // So we don't report error twice, once on opening file and once
  654.         // when netlib is unable to write to it.
  655.         m_bOpenOutputHandledError = TRUE;
  656.  
  657.         return -1;
  658.     }
  659.     XP_FREE(pHumanName);
  660.     return 0; // success
  661. }
  662.  
  663. int CFileSaveObject::NetStreamWrite( const char *block, int32 length ){
  664.     XP_TRACE(("NetStreamWrite: length = %lu", length));
  665.     XP_ASSERT(m_pOutStream);
  666.     m_pOutStream->Write((char *)block,length);
  667.     return m_pOutStream->Status() == IStreamOut::EOS_NoError;
  668. }
  669.  
  670. void CFileSaveObject::Cancel(){
  671.     // tell net URL to stop...
  672.     m_status = ED_ERROR_CANCEL;
  673.     // Above alone does seem to work! Don't we need this?
  674.     // NET_InterruptWindow causes a "reentrant interrupt" error message now!
  675.     NET_SilentInterruptWindow( m_pContext );
  676. }
  677.  
  678. char *CFileSaveObject::GetDestName( intn index ){ 
  679.     if (!m_tapeFS) {
  680.         XP_ASSERT(0);
  681.         return NULL;
  682.     }    
  683.     return m_tapeFS->GetDestURL(index);
  684. }
  685.  
  686. char *CFileSaveObject::GetDestAbsoluteURL( intn index ){ 
  687.     if (!m_tapeFS) {
  688.         XP_ASSERT(0);
  689.         return NULL;
  690.     }    
  691.     char *pDestPathURL = m_tapeFS->GetDestPathURL();
  692.     char *pDestRelativeURL = m_tapeFS->GetDestURL(index);
  693.     if (!pDestRelativeURL) {
  694.       XP_ASSERT(0);
  695.       XP_FREEIF(pDestPathURL);
  696.       return NULL;
  697.     }
  698.  
  699.     // Maybe should use NET_MakeAbsolute() instead of concatenation.
  700.     char *pDestAbsURL = pDestPathURL ? 
  701.         PR_smprintf( "%s%s", pDestPathURL,  pDestRelativeURL ) :
  702.         XP_STRDUP(pDestRelativeURL);
  703.     XP_FREEIF(pDestPathURL);
  704.     XP_FREE(pDestRelativeURL);
  705.  
  706.     return pDestAbsURL;
  707. }
  708.  
  709. char *CFileSaveObject::GetSrcName( intn index ){ 
  710.     if (!m_tapeFS) {
  711.         XP_ASSERT(0);
  712.         return NULL;
  713.     }    
  714.     return m_tapeFS->GetSourceURL(index);
  715.  
  716.     /*if( index >= m_srcImageURLs.Size() ){
  717.         return 0;
  718.     }
  719.     else {
  720.         return XP_STRDUP( m_srcImageURLs[index] );
  721.     } */
  722. }
  723.  
  724. // Convert file:// URLs into local file names for display to user.
  725. PRIVATE void edt_url_make_human(char **pURL) {
  726.   if (NET_IsLocalFileURL(*pURL)) {
  727.     char *pTemp;
  728.     XP_ConvertUrlToLocalFile(*pURL,&pTemp);
  729.     XP_FREEIF(*pURL);
  730.     *pURL = pTemp;
  731.   }
  732. }
  733.  
  734. XP_Bool CFileSaveObject::SaveErrorContinueDialog(ED_FileError iError) {
  735.   // No user interaction for these conditions.
  736.   if (iError == ED_ERROR_NONE ||
  737.       iError == ED_ERROR_CANCEL ||
  738.       iError == ED_ERROR_FILE_EXISTS) {
  739.     return TRUE;
  740.   }
  741.  
  742.   char *tmplate = NULL;
  743.   char *msg = NULL;
  744.   // m_iCurFile is 1-based.
  745.   char *pAbsDest = GetDestAbsoluteURL(m_iCurFile-1);
  746.   char *pRelDest = m_tapeFS->GetHumanName(m_iCurFile-1);
  747.   char *pAbsSrc = GetSrcName(m_iCurFile-1);
  748.   char *pRootAbsDest = GetDestAbsoluteURL(0);
  749.  
  750.   edt_url_make_human(&pAbsDest);
  751.   edt_url_make_human(&pAbsSrc);
  752.   edt_url_make_human(&pRootAbsDest);
  753.  
  754.   // Two level switch, first on different types of file systems,
  755.   // second on different classes of errors.
  756.   switch (m_tapeFS->GetType()) {
  757.     case ITapeFileSystem::File:
  758.       switch (iError) {
  759.         case ED_ERROR_FILE_OPEN:
  760.         case ED_ERROR_FILE_WRITE:
  761.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_SAVE_FILE_WRITE));
  762.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_SAVE_CONTINUE));
  763.           msg = PR_smprintf(tmplate,pRelDest,pRootAbsDest,pRelDest);
  764.           break;
  765.         case ED_ERROR_SRC_NOT_FOUND:
  766.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_SAVE_SRC_NOT_FOUND));
  767.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_SAVE_CONTINUE));
  768.           msg = PR_smprintf(tmplate,pAbsSrc,pAbsSrc);
  769.           break;
  770.         case ED_ERROR_FILE_READ:
  771.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_SAVE_FILE_READ));
  772.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_SAVE_CONTINUE));
  773.           msg = PR_smprintf(tmplate,pRelDest,pRelDest);
  774.           break;
  775.       }
  776.       break;
  777.  
  778.     case ITapeFileSystem::Publish:
  779.       switch (iError) {
  780.         case ED_ERROR_FILE_OPEN:
  781.         case ED_ERROR_FILE_WRITE:
  782.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_PUBLISH_FILE_WRITE));
  783.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_PUBLISH_CONTINUE));
  784.           msg = PR_smprintf(tmplate,pRelDest,pRelDest);
  785.           break;
  786.         case ED_ERROR_SRC_NOT_FOUND:
  787.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_PUBLISH_SRC_NOT_FOUND));
  788.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_PUBLISH_CONTINUE));
  789.           msg = PR_smprintf(tmplate,pAbsSrc,pAbsSrc);
  790.           break;
  791.         case ED_ERROR_FILE_READ:
  792.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_PUBLISH_FILE_READ));
  793.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_PUBLISH_CONTINUE));
  794.           msg = PR_smprintf(tmplate,pRelDest,pRelDest);
  795.           break;
  796.       }
  797.       break;
  798.  
  799.     case ITapeFileSystem::MailSend:
  800.       switch (iError) {
  801.         case ED_ERROR_FILE_OPEN:
  802.         case ED_ERROR_FILE_WRITE:
  803.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_FILE_WRITE));
  804.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_MAIL_CONTINUE));
  805.           msg = PR_smprintf(tmplate,pRelDest,pRelDest);
  806.           break;
  807.         case ED_ERROR_SRC_NOT_FOUND:
  808.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_SRC_NOT_FOUND));
  809.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_MAIL_CONTINUE));
  810.           msg = PR_smprintf(tmplate,pAbsSrc,pAbsSrc);
  811.           break;
  812.         case ED_ERROR_FILE_READ:
  813.           tmplate = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_FILE_READ));
  814.           StrAllocCat(tmplate,XP_GetString(XP_EDT_ERR_MAIL_CONTINUE));
  815.           msg = PR_smprintf(tmplate,pRelDest,pRelDest);
  816.           break;
  817.       }
  818.       break;
  819.   }
  820.  
  821.   XP_Bool retVal = FALSE;
  822.   if (msg) {
  823.     retVal = FE_Confirm(m_pContext,msg);
  824.   }
  825.   else {
  826.     XP_ASSERT(0);
  827.   }
  828.  
  829.   XP_FREEIF(tmplate);
  830.   XP_FREEIF(msg);
  831.   XP_FREEIF(pAbsDest);
  832.   XP_FREEIF(pRelDest);
  833.   XP_FREEIF(pAbsSrc);
  834.   XP_FREEIF(pRootAbsDest);
  835.  
  836.   return retVal;
  837. }
  838.  
  839.  
  840. char * CEditSaveObject::m_pFailedPublishUrl = NULL;
  841.  
  842. //-----------------------------------------------------------------------------
  843. // CEditSaveObject
  844. //-----------------------------------------------------------------------------
  845.  
  846. CEditSaveObject::CEditSaveObject( CEditBuffer *pBuffer, 
  847.                                     ED_SaveFinishedOption finishedOpt,
  848.                                     char *pSrcURL, 
  849.                                     ITapeFileSystem *tapeFS,
  850.                                     XP_Bool, // bSaveAs is ignored. 
  851.                                     XP_Bool bKeepImagesWithDoc, 
  852.                                     XP_Bool bAutoAdjustLinks,
  853.                                     XP_Bool bAutoSave,
  854.                                     CEditSaveToTempData *pSaveToTempData ) :
  855.         CFileSaveObject( pBuffer->m_pContext,pSrcURL,tapeFS,bAutoSave, pSaveToTempData ),
  856.         m_pBuffer( pBuffer ),
  857.         m_pDocStateSave( 0 ),
  858.         m_pSrcURL( XP_STRDUP( pSrcURL ) ),
  859.         m_finishedOpt( finishedOpt ),
  860.         m_bKeepImagesWithDoc( bKeepImagesWithDoc ),
  861.         m_bAutoAdjustLinks( bAutoAdjustLinks ),
  862.         m_backgroundIndex(IgnoreImage),
  863.         m_fontDefIndex(IgnoreImage) {
  864. }
  865.  
  866. CEditSaveObject::~CEditSaveObject(){
  867.     XP_Bool bRevertBuffer = FALSE;
  868.     XP_Bool bRelayout = FALSE;
  869.  
  870.     /* 
  871.      * possible values for m_finishedOpt:
  872.      * ED_FINISHED_GOTO_NEW,       Point the editor to the location of the 
  873.      *                             newly saved document if successful, otherwise revert buffer.
  874.      * ED_FINISHED_REVERT_BUFFER,  Revert the buffer to the state before
  875.      *                             the save operation began, regardless.
  876.      * ED_FINISHED_SAVE_DRAFT      Like ED_FINISHED_REVERT_BUFFER, except clears the dirty flag
  877.      *                             on success.
  878.      * ED_FINISHED_MAIL_SEND       If we succeed we're going to throw the buffer
  879.      *                             away, so don't revert it, but don't bother gotoNew.  
  880.      *                             If failure, revert the buffer.
  881.      *                             Used for mail compose, we don't 
  882.      *                             want the editor to start any operation that 
  883.      *                             causes problems when libmsg destroys the editor
  884.      *                             context. 
  885.      */
  886.  
  887.  
  888.     if ( m_finishedOpt == ED_FINISHED_GOTO_NEW && m_status == ED_ERROR_NONE )
  889.     {
  890.         //// Go to new location.
  891.  
  892.         m_pBuffer->m_pContext->is_new_document = FALSE;
  893.  
  894.         // Set the document title and history entry to the new location.
  895.  
  896.         char *pDestFileURL = GetDestAbsoluteURL(0);
  897.         if ( !pDestFileURL )
  898.         {        
  899.             XP_ASSERT( 0 );
  900.             m_status = ED_ERROR_FILE_WRITE; // Not really the right thing to set.
  901.             return;        
  902.         }
  903.  
  904.  
  905.         // We used to do the ftp://username@path here, but it's moved to earlier in
  906.         // the save process, in EDT_PublishFile.
  907.  
  908.         // Set this first - FE_SetDocTitle will get URL from it
  909.         LO_SetBaseURL( m_pBuffer->m_pContext, pDestFileURL );
  910.           History_entry * hist_ent =
  911.             SHIST_GetCurrent(&(m_pBuffer->m_pContext->hist));
  912.         char * pTitle = NULL;
  913.           if(hist_ent)
  914.           {
  915.             if ( hist_ent->title && XP_STRLEN(hist_ent->title) > 0 ){
  916.                 // Note: hist_ent->title should always = m_pContext->title?
  917.                 // Change "file:///Untitled" into URL
  918.                 // Use the new address if old title == old address
  919.                 if( 0 != XP_STRCMP(hist_ent->title, XP_GetString(XP_EDIT_NEW_DOC_NAME) ) && 
  920.                     0 != XP_STRCMP(hist_ent->title, hist_ent->address) )
  921.                 {
  922.                     pTitle = XP_STRDUP(hist_ent->title);
  923.                 } else {
  924.                     pTitle = XP_STRDUP(pDestFileURL);
  925.                     XP_FREE(hist_ent->title);
  926.                     hist_ent->title = XP_STRDUP(pDestFileURL);
  927.                 }
  928.  
  929.             } else {
  930.                 // Use the URL if no Document title
  931.                 pTitle = XP_STRDUP(pDestFileURL);
  932.                 XP_FREEIF(hist_ent->title);
  933.                 hist_ent->title = XP_STRDUP(pDestFileURL);
  934.             }
  935.             // Test if new URL is same as current before we replace the latter
  936.             XP_Bool bSameURL = EDT_IsSameURL( hist_ent->address, pDestFileURL, NULL, NULL);
  937.             
  938.             // Set history entry address to new URL
  939.             XP_FREEIF(hist_ent->address);
  940.             hist_ent->address = XP_STRDUP(pDestFileURL);
  941.  
  942.             // We may inherit this URL (used for faster reloading in browser)
  943.             //  when saving a remote URL to local disk,
  944.             //  so always clear it since Editor never uses it.
  945.             XP_FREEIF(hist_ent->wysiwyg_url);
  946.  
  947.             // This changes doc title, window caption, history title,
  948.             ///  but not history address
  949.             if (pTitle)
  950.             {
  951.               FE_SetDocTitle(m_pBuffer->m_pContext, pTitle);
  952.               XP_FREE(pTitle);
  953.             }  
  954.             
  955.             if( !bSameURL )
  956.             {
  957.                 // Make the new URL (now in current history entry) 
  958.                 //   as the most-recently-used URL in prefs. list
  959.                 //   (Note: We should never be here in mail message composer)
  960.                 EDT_SyncEditHistory(m_pBuffer->m_pContext);
  961.             }
  962.  
  963.         } // if (hist_ent)
  964.         XP_FREE(pDestFileURL);
  965.  
  966.         // Saved successfully.
  967.         m_pBuffer->DocumentStored();
  968.  
  969.         // This flag must be cleared before GetFileWriteTime,
  970.         //  else the write time will not be recorded
  971.         m_pContext->edit_saving_url = FALSE;
  972.         // Get the current file time
  973.         m_pBuffer->GetFileWriteTime();
  974.         // Some of the links may have changed, so relayout
  975.         bRelayout = TRUE;
  976.     } 
  977.     else if ( m_finishedOpt == ED_FINISHED_MAIL_SEND && m_status == ED_ERROR_NONE )
  978.     {
  979.       //// Do nothing.
  980.       // Don't relayout, we're throwing the buffer away.
  981.     }
  982.     else if (m_pDocStateSave) {
  983.       //// Don't go to the new document, revert to m_pDocStateSave.
  984.       bRevertBuffer = TRUE;        
  985.       // Don't relayout, reverting the buffer will take care of it.
  986.     }
  987.  
  988.     if ( m_finishedOpt == ED_FINISHED_SAVE_DRAFT && m_status == ED_ERROR_NONE )
  989.     {
  990.         // Note that we are still reverting the buffer.
  991.         
  992.         // Saved successfully.
  993.         m_pBuffer->DocumentStored();
  994.     }
  995.  
  996.  
  997.     // Originally, this was only called if m_status == ED_ERROR_NONE.
  998.     m_pBuffer->FileFetchComplete(m_status);
  999.     if (bRelayout)
  1000.     {
  1001.       // Don't always relayout because we may have "cid:" links which cause an error.
  1002.       // reverting the buffer will take care of it.
  1003.       m_pBuffer->RefreshLayout();
  1004.     }
  1005.     
  1006.     
  1007.     // Already cleared above if ED_FINISHED_GOTO_NEW and success,
  1008.     // but it must be cleared eventually in all cases.
  1009.     m_pContext->edit_saving_url = FALSE;
  1010.  
  1011.     m_pBuffer->m_pSaveObject = 0;
  1012.  
  1013.     if (bRevertBuffer) {
  1014.         // Must be after CEditBuffer::FileFetchComplete(), because it deletes CEditBuffer.
  1015.         m_pBuffer->RestoreState(m_pDocStateSave);
  1016.     }
  1017.     
  1018.     if( m_tapeFS->GetType() == ITapeFileSystem::Publish ){
  1019.         XP_FREEIF(m_pFailedPublishUrl);
  1020.     
  1021.         if( m_status == ED_ERROR_NONE ){
  1022.             // Save the last-used location into the history list
  1023.             EDT_SyncPublishingHistory();
  1024.         } else {
  1025.             // Save this URL so we can supply the "bad" location
  1026.             //   to user if they try to publish that URL again
  1027.             m_pFailedPublishUrl = XP_STRDUP(m_pSrcURL);
  1028.         }
  1029.     }
  1030.     XP_FREE( m_pSrcURL );
  1031.     
  1032.     delete m_pDocStateSave;
  1033. }
  1034.  
  1035. // Add all images in document that are also in ppIncludedFiles.
  1036. //
  1037. // If ppIncludedFiles is NULL, this should add exactly those files that would be returned by
  1038. // CEditBuffer::GetAllDocumentFiles() with the selected flag set TRUE.
  1039. // i.e. Regular Saving should save exactly those files that are published by default in remote publishing.
  1040. XP_Bool CEditSaveObject::AddAllFiles(char **ppIncludedFiles){
  1041. //////// WARNING: if you change this function, fix CEditBuffer::GetAllDocumentFiles and CEditSaveObject::FixupLinks() also.
  1042.     EDT_ImageData *pData;
  1043.     EDT_PageData *pPageData;
  1044.  
  1045.     // Make sure all URLs in ppIncludedFiles are absolute
  1046.     if (ppIncludedFiles) {
  1047.       char **ppFile = ppIncludedFiles;
  1048.       while (*ppFile) {
  1049.         char *pAbs = NET_MakeAbsoluteURL(m_pSrcURL,*ppFile);
  1050.         if (pAbs) {
  1051.           // If making *ppFile absolute changed it, replace it with absolute URL.
  1052.           if (XP_STRCMP(pAbs,*ppFile)) {
  1053.             XP_FREE(*ppFile);
  1054.             *ppFile = pAbs;
  1055.           }
  1056.           else {
  1057.             XP_FREE(pAbs);
  1058.           }
  1059.         }
  1060.         
  1061.         ppFile++;
  1062.       }
  1063.     }
  1064.  
  1065.     // Add actual HTML document.    
  1066.     intn firstIndex = AddFile(m_pSrcURL,TEXT_HTML,m_pBuffer->GetDocCharSetID());
  1067.     if (firstIndex != 0) {
  1068.         m_status = ED_ERROR_BAD_URL;
  1069.         FreeList(ppIncludedFiles);
  1070.         return FALSE;
  1071.     }
  1072.  
  1073.  
  1074.     CEditElement *pImage = m_pBuffer->m_pRoot->FindNextElement( 
  1075.                                              &CEditElement::FindImage, 0 );
  1076.  
  1077.     // If there is a background Image, make it the first image.
  1078.     pPageData = m_pBuffer->GetPageData();
  1079.     if( pPageData ) {
  1080.         if ( pPageData->pBackgroundImage && *pPageData->pBackgroundImage){
  1081.             m_backgroundIndex = CheckAddFile( pPageData->pBackgroundImage, NULL, ppIncludedFiles, 
  1082.                 m_bKeepImagesWithDoc && !pPageData->bBackgroundNoSave );
  1083.         }
  1084.  
  1085.         if ( pPageData->pFontDefURL && *pPageData->pFontDefURL ){
  1086.             m_fontDefIndex = CheckAddFile( pPageData->pFontDefURL, NULL, ppIncludedFiles, 
  1087.                 m_bKeepImagesWithDoc && !pPageData->bFontDefNoSave );
  1088.         }
  1089.     }
  1090.     m_pBuffer->FreePageData( pPageData );
  1091.  
  1092.     while( pImage ){
  1093.         // If this assert fails, it is probably because FinishedLoad
  1094.         // didn't get called on the image. And that's probably because
  1095.         // of some bug in the recursive FinishedLoad code.
  1096.         // If the size isn't known, the relayout after the save will block,
  1097.         // and the fourth .. nth images in the document will get zero size.
  1098.         // XP_ASSERT(pImage->Image()->SizeIsKnown());
  1099.         pImage->Image()->m_iSaveIndex = IgnoreImage;
  1100.         pImage->Image()->m_iSaveLowIndex = IgnoreImage;
  1101.         pData = pImage->Image()->GetImageData();
  1102.         if( pData && pData->pSrc && *pData->pSrc){
  1103.             pImage->Image()->m_iSaveIndex = CheckAddFile( pData->pSrc, NULL, ppIncludedFiles, 
  1104.                 m_bKeepImagesWithDoc && !pData->bNoSave );
  1105.         }
  1106.         if( pData && pData->pLowSrc && *pData->pLowSrc){
  1107.             pImage->Image()->m_iSaveLowIndex = CheckAddFile( pData->pLowSrc, NULL,ppIncludedFiles, 
  1108.                 m_bKeepImagesWithDoc && !pData->bNoSave );
  1109.         }
  1110.         edt_FreeImageData( pData );
  1111.         pImage = pImage->FindNextElement( &CEditElement::FindImage, 0 );
  1112.     }
  1113.  
  1114.  
  1115.     //// Sure would be nice to abstract all the different types of table
  1116.     //// backgrounds.
  1117.     // table backgrounds <table>
  1118.     CEditElement *pNext = m_pBuffer->m_pRoot;
  1119.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTable, 0 )) ){
  1120.       EDT_TableData *pData = ((CEditTableElement *)pNext)->GetData();
  1121.       if (pData) {
  1122.         if ( pData->pBackgroundImage && *pData->pBackgroundImage) {
  1123.           ((CEditTableElement *)pNext)->m_iBackgroundSaveIndex = CheckAddFile( pData->pBackgroundImage, NULL, ppIncludedFiles, 
  1124.               m_bKeepImagesWithDoc && !pData->bBackgroundNoSave);
  1125.         }
  1126.         CEditTableElement::FreeData(pData);
  1127.       }
  1128.     }
  1129.     // table row backgrounds <tr>
  1130.     pNext = m_pBuffer->m_pRoot;
  1131.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableRow, 0 )) ){
  1132.       EDT_TableRowData *pData = ((CEditTableRowElement *)pNext)->GetData();
  1133.       if (pData) {
  1134.         if ( pData->pBackgroundImage && *pData->pBackgroundImage) {
  1135.           ((CEditTableRowElement *)pNext)->m_iBackgroundSaveIndex = CheckAddFile( pData->pBackgroundImage, NULL, ppIncludedFiles, 
  1136.               m_bKeepImagesWithDoc && !pData->bBackgroundNoSave);
  1137.         }
  1138.         CEditTableRowElement::FreeData(pData);
  1139.       }
  1140.     }
  1141.     // table cell backgrounds <td> <th>
  1142.     pNext = m_pBuffer->m_pRoot;
  1143.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableCell, 0 )) ){
  1144.       EDT_TableCellData *pData = ((CEditTableCellElement *)pNext)->GetData(0);
  1145.       if (pData) {
  1146.         if ( pData->pBackgroundImage && *pData->pBackgroundImage) {
  1147.           ((CEditTableCellElement *)pNext)->m_iBackgroundSaveIndex = CheckAddFile( pData->pBackgroundImage, NULL, ppIncludedFiles, 
  1148.               m_bKeepImagesWithDoc && !pData->bBackgroundNoSave);
  1149.         }
  1150.         CEditTableCellElement::FreeData(pData);
  1151.       }
  1152.     }
  1153.  
  1154.  
  1155.     // UnknownHTML tags with LOCALDATA attribute.
  1156.     pNext = m_pBuffer->m_pRoot;
  1157.     if ( pNext ) {
  1158.         while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindUnknownHTML, 0 )) ){
  1159.           CEditIconElement *pIcon = CEditIconElement::Cast(pNext);
  1160.           if (pIcon) {
  1161.             char **pMimeTypes;
  1162.             char **pURLs;
  1163.             int count = pIcon->ParseLocalData(&pMimeTypes,&pURLs);
  1164.             
  1165.             // Remember indices into save object.
  1166.             if (count) {
  1167.               XP_FREEIF(pIcon->m_piSaveIndices);
  1168.               // allocate memory.            
  1169.               pIcon->m_piSaveIndices = new intn[count];
  1170.             }
  1171.  
  1172.             // Maybe should make a check that pURLs[n] is a relative URL
  1173.             // in current directory.
  1174.             for (int n = 0; n < count; n++) {
  1175.               pIcon->m_piSaveIndices[n] = CheckAddFile(pURLs[n],pMimeTypes[n],ppIncludedFiles,TRUE);
  1176.             }
  1177.             CEditIconElement::FreeLocalDataLists(pMimeTypes,pURLs,count);
  1178.           }
  1179.         } // while
  1180.     }
  1181.  
  1182.  
  1183.     // Go through ppIncludedFiles once more to add anything we missed.
  1184.     //
  1185.     // This is for the "add all files in directory" button in publishing,
  1186.     // here ppIncludedFiles may contain files that CEditSaveObject knows
  1187.     // nothing about.
  1188.     //
  1189.     // Doesn't hurt to add files twice to tape file system, it'll just 
  1190.     // return the previous index.
  1191.     if (ppIncludedFiles) {
  1192.       char **pFile = ppIncludedFiles;
  1193.       while (*pFile) {
  1194.         // Ignore returned index, we're not going to update any links here.
  1195.         AddFile(*pFile,NULL,m_pBuffer->GetDocCharSetID());
  1196.         pFile++;
  1197.       }
  1198.     }
  1199.  
  1200.  
  1201.     FreeList(ppIncludedFiles);
  1202.     return TRUE;
  1203. }
  1204.  
  1205. // Returns the URL that should be used as the base for relative URLs in 
  1206. // the document.  Similar to just calling LO_GetBaseURL(), except returns
  1207. // the document temp directory for untitled documents.
  1208. // Must free returned memory.
  1209. char *edt_GetDocRelativeBaseURL(MWContext *pContext) {
  1210.   char *pDocURL = NULL;
  1211.  
  1212.   // Interpret URLs relative to temp directory if a new document.
  1213.   if (EDT_IS_NEW_DOCUMENT(pContext)) {
  1214.     char *pxpURL = EDT_GetDocTempDir(pContext);
  1215.     if (!pxpURL) {
  1216.       return pDocURL;
  1217.     }
  1218.     // prepend "file://" to make a URL.
  1219.     StrAllocCat(pDocURL,"file://");
  1220.     StrAllocCat(pDocURL,pxpURL);
  1221.     XP_FREE(pxpURL);
  1222.   }
  1223.   else {
  1224.     pDocURL = edt_StrDup(LO_GetBaseURL(pContext));
  1225.   }
  1226.   return pDocURL;
  1227. }
  1228.  
  1229.  
  1230. // Mostly a wrapper for CFileSaveObject::AddFile, except deals with adjusting links.
  1231. //
  1232. // Analogous to AddToBufferUnique() in edtbuf.cpp
  1233. intn  CEditSaveObject::CheckAddFile( char *pSrc, char *pMIMEType, 
  1234.                       char **ppIncludedFiles, XP_Bool bSaveDefault ){
  1235.     intn retVal = IgnoreImage;
  1236.     if( EDT_IS_LIVEWIRE_MACRO( pSrc ) ){
  1237.         return IgnoreImage;  
  1238.     }
  1239.  
  1240.     char *pDocURL = edt_GetDocRelativeBaseURL(m_pContext);
  1241.     if (!pDocURL) {
  1242.       return IgnoreImage;
  1243.     }
  1244.  
  1245.     char *pAbsoluteSrc = NET_MakeAbsoluteURL(pDocURL,pSrc);
  1246.     XP_FREE(pDocURL);
  1247.     if (!pAbsoluteSrc) {
  1248.       return IgnoreImage;
  1249.     }
  1250.  
  1251.     // If ppIncludedFiles is passed in, use it to decide whether to add file.
  1252.     // Otherwise, use bSaveDefault to decide.
  1253.     XP_Bool saveMe;
  1254.     if (ppIncludedFiles) {
  1255.       saveMe = URLInList(ppIncludedFiles,pAbsoluteSrc);
  1256.     }
  1257.     else {
  1258.       saveMe = bSaveDefault;
  1259.     }
  1260.     
  1261.     if (saveMe) {
  1262.       retVal = AddFile(pSrc,pMIMEType,m_pBuffer->GetDocCharSetID());        
  1263.       if (retVal == ITapeFileSystem::Error || retVal == ITapeFileSystem::SourceDestSame) {
  1264.           // couldn't make a good local name, so try to fix it.
  1265.           retVal = AttemptAdjust;
  1266.       }
  1267.     }
  1268.     else if ( m_bAutoAdjustLinks ) { 
  1269.         retVal = AttemptAdjust;
  1270.     }
  1271.  
  1272.     XP_FREE(pAbsoluteSrc);
  1273.     return retVal;
  1274. }
  1275.  
  1276. void CEditSaveObject::FreeList(char **ppList) {
  1277.   if (!ppList) 
  1278.     return;
  1279.  
  1280.   int n = 0;
  1281.   while (ppList[n]) {
  1282.     XP_FREE(ppList[n]);
  1283.     n++;
  1284.   }
  1285.   
  1286.   XP_FREE(ppList);  
  1287. }
  1288.  
  1289. // ppList and pURL must be absolute URLs.
  1290. XP_Bool CEditSaveObject::URLInList(char **ppList, char *pURL ) {
  1291.   for (int  n = 0; ppList[n]; n++) {
  1292.     if (EDT_IsSameURL(ppList[n],pURL,NULL,NULL))
  1293.       return TRUE;
  1294.   }
  1295.   return FALSE;
  1296. }
  1297.  
  1298. // Should be called no matter what, even if error or cancel occurs.
  1299. ED_FileError CEditSaveObject::FileFetchComplete(){
  1300.     XP_ASSERT(XP_IsContextInList(m_pBuffer->m_pContext));
  1301.  
  1302.     return CFileSaveObject::FileFetchComplete();
  1303. }
  1304.  
  1305. // Always return TRUE.
  1306. XP_Bool CEditSaveObject::SaveFirstFile() {
  1307.     if ( m_status != ED_ERROR_NONE ) {
  1308.         XP_ASSERT(0);
  1309.         return TRUE;    
  1310.     }
  1311.  
  1312.     XP_ASSERT(!m_pDocStateSave);
  1313.     m_pDocStateSave = m_pBuffer->RecordState();
  1314.  
  1315.     char *pBadLinks = FixupLinks();
  1316.     PRBool needEncrypt = EDT_EncryptState( m_pContext );
  1317.  
  1318.     // Tell user some links may be broken by this operation, give choice
  1319.     // of continuing or not.
  1320.     if (pBadLinks) {
  1321.       XP_Bool bCancel = FALSE;
  1322.       char *tmplate = XP_GetString(XP_EDT_BREAKING_LINKS);
  1323.       char *msg = NULL;
  1324.       if (tmplate) {
  1325.         msg = PR_smprintf(tmplate,pBadLinks);
  1326.         bCancel = !FE_Confirm(m_pContext,msg);
  1327.       }
  1328.  
  1329.       XP_FREEIF(msg);
  1330.       XP_FREEIF(pBadLinks);
  1331.       if (bCancel) {
  1332.           Cancel();
  1333.           return TRUE;
  1334.       }
  1335.     }
  1336.  
  1337.     // Write root HTML document
  1338.     char *pHumanName = m_tapeFS->GetHumanName(0);
  1339.     if (pHumanName == NULL) {
  1340.         XP_ASSERT(0);
  1341.         return TRUE;
  1342.     }    
  1343.     
  1344.     // Tell front end the filename.
  1345.     if( !m_bFromAutoSave && // Don't want dialogs from autosave, bug 43878
  1346.         m_tapeFS->GetType() != ITapeFileSystem::MailSend ){// Suppress dialog for mail send.
  1347.       FE_SaveDialogSetFilename( m_pContext, pHumanName );
  1348.     } 
  1349.  
  1350.     IStreamOut *out = NULL;
  1351.     URL_Struct *URL_s = NULL;
  1352.  
  1353.     if (needEncrypt) { 
  1354.         // by the time the file gets to the tapeFS, it will be binary.
  1355.         m_tapeFS->SetFirstBinary();
  1356.         out = new CNetStreamToTapeFS(m_pContext,m_tapeFS);
  1357.     }
  1358.     else {
  1359.         out = m_tapeFS->OpenStream(0);
  1360.     }
  1361.     
  1362.     if (out == NULL) {
  1363.         m_status = ED_ERROR_FILE_OPEN;
  1364.     }
  1365.     else {
  1366.       m_pBuffer->WriteToStream( out );
  1367.       if ( out->Status() != IStreamOut::EOS_NoError ) {
  1368.           m_status = ED_ERROR_FILE_WRITE;
  1369.       }
  1370.       if (!needEncrypt) {
  1371.           m_tapeFS->CloseStream(0);    
  1372.       }
  1373.       else {
  1374.          delete out;
  1375.       }
  1376.     }
  1377.     
  1378.     if( m_status != ED_ERROR_NONE ){
  1379.       char *tmplate = NULL;
  1380.       char *msg = NULL;
  1381.       char *pAbsDest = GetDestAbsoluteURL(0);
  1382.       char *pAbsSrc = GetSrcName(0);
  1383.       edt_url_make_human(&pAbsDest);
  1384.       edt_url_make_human(&pAbsSrc);
  1385.  
  1386.       switch (m_tapeFS->GetType()) {
  1387.         case ITapeFileSystem::File:
  1388.           tmplate = XP_GetString(XP_EDT_ERR_SAVE_WRITING_ROOT);
  1389.           if (tmplate) {
  1390.             char *pRelDest = m_tapeFS->GetHumanName(0);
  1391.             msg = PR_smprintf(tmplate,pRelDest,pAbsDest);
  1392.             XP_FREEIF(pRelDest);
  1393.           }
  1394.           break;
  1395.         case ITapeFileSystem::Publish:
  1396.           tmplate = XP_GetString(XP_EDT_ERR_PUBLISH_PREPARING_ROOT);
  1397.           if (tmplate) {
  1398.             msg = PR_smprintf(tmplate,pAbsSrc);
  1399.             StrAllocCat(msg,XP_GetString(XP_EDT_ERR_CHECK_DISK));
  1400.           }
  1401.           break;
  1402.         case ITapeFileSystem::MailSend:
  1403.           msg = XP_STRDUP(XP_GetString(XP_EDT_ERR_MAIL_PREPARING_ROOT));
  1404.           break;
  1405.       }
  1406.       if (msg) {
  1407.         FE_Alert(m_pContext,msg);
  1408.       }
  1409.  
  1410.       XP_FREEIF(msg);
  1411.       XP_FREEIF(pAbsDest);
  1412.       XP_FREEIF(pAbsSrc);
  1413.     }
  1414.  
  1415.     // We know this is file number 1,  CFileSaveObject::m_iCurFile is 1-based.
  1416.     CheckFinishedSave(1,m_status);
  1417.  
  1418.     XP_FREE(pHumanName);
  1419.     return TRUE;
  1420. }
  1421.  
  1422. void CEditSaveObject::FixupLink(intn iIndex, // index to tape file system
  1423.                                 char **ppImageURL,   // Value to fixup.
  1424.                                 char *pDestPathURL,
  1425.                                 ED_HREFList *badLinks) {
  1426.   if( iIndex == IgnoreImage ){
  1427.     return;
  1428.   }
  1429.  
  1430.   if( iIndex == AttemptAdjust ){
  1431.     CEditLinkManager::AdjustLink(ppImageURL,m_pSrcURL,pDestPathURL,badLinks);
  1432.   }
  1433.   else {
  1434.     // Look it up from tape file system.
  1435.     char *pNewURL = GetDestName(iIndex);
  1436.     if (pNewURL) {
  1437.       XP_FREEIF(*ppImageURL);
  1438.       *ppImageURL = pNewURL;
  1439.     }
  1440.   }          
  1441. }
  1442.  
  1443. char *CEditSaveObject::FixupLinks(){
  1444. //////// WARNING: if you change this function, fix CEditSaveObject::AddAllFiles() and CEditBuffer::GetAllDocumentFiles() also.
  1445.     ED_HREFList badLinks;
  1446.  
  1447.     EDT_ImageData *pData;
  1448.  
  1449.     char *pDestPathURL = m_tapeFS->GetDestPathURL();
  1450.     // Note: pDestPathURL may legally be NULL.
  1451.  
  1452.     // If there is a background Image, make it the first one.
  1453.     FixupLink(m_backgroundIndex,&m_pBuffer->m_pBackgroundImage,pDestPathURL,&badLinks);
  1454.  
  1455.     FixupLink(m_fontDefIndex, &m_pBuffer->m_pFontDefURL, pDestPathURL,&badLinks);
  1456.  
  1457.     // regular images.
  1458.     CEditElement *pImage = m_pBuffer->m_pRoot->FindNextElement( 
  1459.                                    &CEditElement::FindImage, 0 );
  1460.     while( pImage ){
  1461.         pData = pImage->Image()->GetImageData();
  1462.  
  1463.         // do the normal image
  1464.         FixupLink(pImage->Image()->m_iSaveIndex,&pData->pSrc,pDestPathURL,&badLinks);
  1465.         // do the lowres image
  1466.         FixupLink(pImage->Image()->m_iSaveLowIndex,&pData->pLowSrc,pDestPathURL,&badLinks);
  1467.  
  1468.         pImage->Image()->SetImageData( pData );
  1469.         edt_FreeImageData( pData );
  1470.         pImage = pImage->FindNextElement( &CEditElement::FindImage, 0 );
  1471.     }
  1472.  
  1473.  
  1474.     //// Sure would be nice to abstract all the different types of table
  1475.     //// backgrounds.
  1476.     // table backgrounds <table>
  1477.     CEditElement *pNext = m_pBuffer->m_pRoot;
  1478.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTable, 0 )) ){
  1479.       CEditTableElement *pTable = (CEditTableElement *)pNext;
  1480.       EDT_TableData *pData = pTable->GetData();
  1481.       if (pData) {
  1482.         if ( pData->pBackgroundImage && *pData->pBackgroundImage) {
  1483.           FixupLink(pTable->m_iBackgroundSaveIndex,&pData->pBackgroundImage,pDestPathURL,&badLinks);
  1484.           pTable->SetData(pData);
  1485.         }
  1486.         CEditTableElement::FreeData(pData);
  1487.       }
  1488.     }
  1489.     // table row backgrounds <tr>
  1490.     pNext = m_pBuffer->m_pRoot;
  1491.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableRow, 0 )) ){
  1492.       CEditTableRowElement *pTable = (CEditTableRowElement *)pNext;
  1493.       EDT_TableRowData *pData = pTable->GetData();
  1494.       if (pData) {
  1495.         if ( pData->pBackgroundImage && *pData->pBackgroundImage) {
  1496.           FixupLink(pTable->m_iBackgroundSaveIndex,&pData->pBackgroundImage,pDestPathURL,&badLinks);
  1497.           pTable->SetData(pData);
  1498.         }
  1499.         CEditTableRowElement::FreeData(pData);
  1500.       }
  1501.     }
  1502.     // table cell backgrounds <td> <th>
  1503.     pNext = m_pBuffer->m_pRoot;
  1504.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableCell, 0 )) ){
  1505.       CEditTableCellElement *pTable = (CEditTableCellElement *)pNext;
  1506.       EDT_TableCellData *pData = pTable->GetData(0);
  1507.       if (pData) {
  1508.         if ( pData->pBackgroundImage && *pData->pBackgroundImage) {
  1509.           FixupLink(pTable->m_iBackgroundSaveIndex,&pData->pBackgroundImage,pDestPathURL,&badLinks);
  1510.           pTable->SetData(pData);
  1511.         }
  1512.         CEditTableCellElement::FreeData(pData);
  1513.       }
  1514.     }
  1515.  
  1516.     // UnknownHTML tags with LOCALDATA attribute.
  1517.     pNext = m_pBuffer->m_pRoot;
  1518.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindUnknownHTML, 0 )) ){
  1519.       CEditIconElement *pIcon = CEditIconElement::Cast(pNext);
  1520.       if (pIcon) {
  1521.         char **pMimeTypes;
  1522.         char **pURLs;
  1523.         int count = pIcon->ParseLocalData(&pMimeTypes,&pURLs);
  1524.  
  1525.         if (count) {
  1526.           if (pIcon->m_piSaveIndices) {
  1527.             // Change all occurances of pURLs[n] in the unknown tag to
  1528.             // take account of the new location.
  1529.             for (int n = 0; n < count; n++) {
  1530.               char *pPrev = XP_STRDUP(pURLs[n]); // Remember, so we can se if pURLs[n] changes.
  1531.               FixupLink(pIcon->m_piSaveIndices[n],&pURLs[n],pDestPathURL,&badLinks);
  1532.               // Note: ReplaceParamValue checks if the two are the same.
  1533.               // Also note: the LOCALDATA parameter itself will be updated.
  1534.               pIcon->ReplaceParamValues(pPrev,pURLs[n]);
  1535.               XP_FREEIF(pPrev);
  1536.             }
  1537.           } 
  1538.           else {
  1539.             XP_ASSERT(0);
  1540.           }
  1541.         }
  1542.  
  1543.         // kill and zero memory for saved indices.
  1544.         XP_FREEIF(pIcon->m_piSaveIndices);
  1545.         CEditIconElement::FreeLocalDataLists(pMimeTypes,pURLs,count);
  1546.       }
  1547.     } // while
  1548.  
  1549.  
  1550.     
  1551.     // Adjust the HREFs.
  1552.     if( m_bAutoAdjustLinks && pDestPathURL ){
  1553.        // No longer use CEditLinkManager::AdjustAllLinks because its ref counting is 
  1554.        // messed up and has more links than are currently in document.  It adds too 
  1555.        // many lines to badLinks (which is actually displayed to the user.)
  1556.        // m_pBuffer->linkManager.AdjustAllLinks( m_pSrcURL, pDestPathURL, &badLinks );
  1557.  
  1558.         // Walk the tree and find all HREFs.
  1559.         CEditElement *pLeaf = m_pBuffer->m_pRoot->FindNextElement( 
  1560.                                       &CEditElement::FindLeafAll,0 );
  1561.         // First sweep, mark all HREFs as not adjusted.
  1562.         while (pLeaf) {
  1563.           m_pBuffer->linkManager.SetAdjusted(pLeaf->Leaf()->GetHREF(),FALSE);
  1564.           pLeaf = pLeaf->FindNextElement(&CEditElement::FindLeafAll,0 );
  1565.         }
  1566.         // Second sweep, actually adjust the HREFs.
  1567.         pLeaf = m_pBuffer->m_pRoot->FindNextElement( 
  1568.                 &CEditElement::FindLeafAll,0 );
  1569.         while (pLeaf) {
  1570.           ED_LinkId linkId = pLeaf->Leaf()->GetHREF();
  1571.           if (linkId && !m_pBuffer->linkManager.GetAdjusted(linkId)) {
  1572.             // linkManager can deal with NULL HREF.
  1573.             m_pBuffer->linkManager.AdjustLink(linkId,m_pSrcURL, pDestPathURL, &badLinks );          
  1574.             m_pBuffer->linkManager.SetAdjusted(linkId,TRUE);
  1575.           }
  1576.           pLeaf = pLeaf->FindNextElement(&CEditElement::FindLeafAll,0 );
  1577.         }
  1578.     }
  1579.     
  1580.     XP_FREEIF(pDestPathURL);
  1581.  
  1582.     // Package up a string of all the bad links found.
  1583.     char *pRet = NULL;
  1584.     if (badLinks.Size() > 0) {
  1585.       int n;
  1586.       for (n = 0; n < badLinks.Size(); n++) {
  1587.         StrAllocCat(pRet,badLinks[n]);
  1588.         StrAllocCat(pRet,"\n");
  1589.         XP_FREEIF(badLinks[n]);
  1590.       }
  1591.     }
  1592.  
  1593.     return pRet;
  1594. }
  1595.  
  1596.  
  1597. #if 0 //// Now obsolete
  1598. //-----------------------------------------------------------------------------
  1599. // CEditImageSaveObject
  1600. //-----------------------------------------------------------------------------
  1601.  
  1602. CEditImageSaveObject::CEditImageSaveObject( CEditBuffer *pBuffer, 
  1603.                 EDT_ImageData *pData, XP_Bool bReplaceImage ) 
  1604.     :
  1605.         CFileSaveObject( pBuffer->m_pContext ),
  1606.         m_pBuffer( pBuffer ),
  1607.         m_pData( edt_DupImageData( pData ) ),
  1608.         m_srcIndex(-1),
  1609.         m_lowSrcIndex(-1),
  1610.         m_bReplaceImage( bReplaceImage )
  1611. {
  1612.     // we are going to write the image files into
  1613.     SetDestPathURL( LO_GetBaseURL( pBuffer->m_pContext ) );
  1614. }
  1615.  
  1616. CEditImageSaveObject::~CEditImageSaveObject(){
  1617.     edt_FreeImageData( m_pData );
  1618.     m_pBuffer->m_pSaveObject = 0;
  1619. }
  1620.  
  1621. ED_FileError CEditImageSaveObject::FileFetchComplete(){
  1622.  
  1623.     if( m_status == ED_ERROR_NONE ){
  1624.         char *pName = 0;
  1625.         if( m_srcIndex != -1 && (pName = GetDestName(m_srcIndex)) != 0 ){
  1626.             if( m_pData->pSrc) XP_FREE( m_pData->pSrc );
  1627.             m_pData->pSrc = pName;
  1628.         }
  1629.  
  1630.         if( m_lowSrcIndex != -1 && (pName = GetDestName(m_lowSrcIndex)) != 0 ){
  1631.             XP_FREE( m_pData->pLowSrc );
  1632.             m_pData->pLowSrc = pName;
  1633.         }
  1634.  
  1635.         m_pBuffer->m_pLoadingImage = new CEditImageLoader( m_pBuffer, 
  1636.                     m_pData, m_bReplaceImage );
  1637.         m_pBuffer->m_pLoadingImage->LoadImage();
  1638.     }
  1639.     return CFileSaveObject::FileFetchComplete();
  1640. }
  1641. #endif
  1642.  
  1643. #if 0 // now obsolete
  1644. //-----------------------------------------------------------------------------
  1645. // CEditBackgroundImageSaveObject
  1646. //-----------------------------------------------------------------------------
  1647.  
  1648. CEditBackgroundImageSaveObject::CEditBackgroundImageSaveObject( CEditBuffer *pBuffer)
  1649.     :
  1650.         CFileSaveObject( pBuffer->m_pContext ),
  1651.         m_pBuffer( pBuffer )
  1652. {
  1653.     // we are going to write the image files into
  1654.     SetDestPathURL( LO_GetBaseURL( pBuffer->m_pContext ) );
  1655. }
  1656.  
  1657. CEditBackgroundImageSaveObject::~CEditBackgroundImageSaveObject(){
  1658.     m_pBuffer->m_pSaveObject = 0;
  1659. }
  1660.  
  1661. ED_FileError CEditBackgroundImageSaveObject::FileFetchComplete(){
  1662.     // Note: If user canceled in overwrite dialog, then we don't change the background
  1663.     if( m_status == ED_ERROR_NONE ){
  1664.         if( m_pBuffer->m_pBackgroundImage ) XP_FREE(m_pBuffer->m_pBackgroundImage);
  1665.  
  1666.         // Get the one and only image file (without local or URL path)
  1667.         m_pBuffer->m_pBackgroundImage = GetDestName(0);
  1668.  
  1669.         // This will set background image
  1670.         m_pBuffer->RefreshLayout();
  1671.         // LO_SetBackgroundImage( m_pBuffer->m_pContext, m_pBuffer->m_pBackgroundImage );
  1672.     }
  1673.     return CFileSaveObject::FileFetchComplete();
  1674. }
  1675. #endif
  1676.  
  1677. //
  1678. // Sigh, why did the editor invent a new streaming type? This function
  1679. // allows the preencrypted file standard streamer to point back to the
  1680. // Editor's stream without turning the C++ virus loose on the security
  1681. // library.
  1682. //
  1683.  
  1684. typedef struct edtPrivStructStr {
  1685.      ITapeFileSystem *tapeFS;
  1686.      IStreamOut *out;
  1687. } edtPrivStruct;
  1688.  
  1689. static unsigned int
  1690. edt_TapeIsReady(NET_StreamClass *stream) {    
  1691.     return MAX_WRITE_READY;
  1692. }
  1693.  
  1694. static void
  1695. edt_TapeComplete(NET_StreamClass *stream) {
  1696.     edtPrivStruct *obj = (edtPrivStruct *)stream->data_object;    
  1697.     obj->tapeFS->CloseStream(0);
  1698.     XP_FREE(obj);
  1699.     return;
  1700. }
  1701.  
  1702. static void
  1703. edt_TapeAbort(NET_StreamClass *stream, int status) {    
  1704.     edt_TapeComplete(stream);
  1705. }
  1706.  
  1707. static int
  1708. edt_TapeWrite(NET_StreamClass *stream, char *str, int32 len) {
  1709.     edtPrivStruct *obj= (edtPrivStruct *)stream->data_object;    
  1710.     obj->out->Write(str,len);
  1711.     if ( obj->out->Status() != IStreamOut::EOS_NoError ) {
  1712.         return -1;
  1713.     }
  1714.     return 0;
  1715. }
  1716.  
  1717. extern "C" NET_StreamClass *EDT_NetToTape(void *data) {
  1718.     NET_StreamClass *stream;
  1719.     edtPrivStruct *obj;
  1720.  
  1721.     stream = (NET_StreamClass *) XP_ALLOC(sizeof(NET_StreamClass));
  1722.     if (stream == NULL) {
  1723.         return NULL;
  1724.     }
  1725.  
  1726.     obj = (edtPrivStruct *)XP_ALLOC(sizeof(edtPrivStruct));
  1727.  
  1728.     if (obj == NULL) {
  1729.         XP_FREE(stream);
  1730.         return NULL;
  1731.     }
  1732.  
  1733.     stream->name = "Editor Tape FS stream";
  1734.     stream->complete = edt_TapeComplete;
  1735.     stream->abort = edt_TapeAbort;
  1736.     stream->is_write_ready = edt_TapeIsReady;
  1737.     stream->data_object = obj;
  1738.     stream->window_id = NULL; // if this were a real stream we would need a 
  1739.                               // url and a window context.
  1740.     stream->put_block  = (MKStreamWriteFunc)edt_TapeWrite;
  1741.  
  1742.     obj->tapeFS = (ITapeFileSystem *)data;
  1743.     obj->out = obj->tapeFS->OpenStream(0);
  1744.     
  1745.     if (obj->out == NULL) {
  1746.         XP_FREE(obj);
  1747.         XP_FREE(stream);
  1748.         return NULL;
  1749.     }
  1750.  
  1751.     return stream;
  1752. }
  1753. #endif
  1754.