/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * 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. */ // gendoc.cpp : implementation file // #include "stdafx.h" #include "cntritem.h" #include "srvritem.h" #include "feembed.h" #include "np.h" #include "nppg.h" #include "plginvw.h" #include "edt.h" #include "il_icons.h" #include "edframe.h" #include "ipframe.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif #ifdef XP_WIN16 #define T2COLE(str) str #define LPCOLESTR LPCTSTR #endif ///////////////////////////////////////////////////////////////////////////// // CGenericDoc #ifndef _AFXDLL #undef new #endif IMPLEMENT_DYNCREATE(CGenericDoc, COleServerDoc) #ifndef _AFXDLL #define new DEBUG_NEW #endif // Default extent values. #define HIY 7000 #define HIX 7000 LPUNKNOWN AFXAPI _myQueryInterface(LPUNKNOWN lpUnknown, REFIID iid) { ASSERT(lpUnknown != NULL); LPUNKNOWN lpW = NULL; if (lpUnknown->QueryInterface(iid, (void**)&lpW) != S_OK) return NULL; return lpW; } #define QUERYINTERFACE(lpUnknown, iface) \ (iface*)_myQueryInterface(lpUnknown, IID_##iface) CGenericDoc::CGenericDoc() { // In case someone forgets to tell us. m_pContext = NULL; // Haven't set a document file yet. m_bOpenDocumentFileSet = FALSE; // We can close the document. m_bCanClose = TRUE; // Default extents. m_csViewExtent = CSize(HIX, HIY); m_csDocumentExtent = CSize(HIX, HIY); } CGenericDoc::~CGenericDoc() { // When the document goes down, it's a good idea to let the context // know it's no longer there. if(GetContext() != NULL) { GetContext()->ClearDoc(); } // Get rid of the context if around. if(GetContext() && GetContext()->IsDestroyed() == FALSE) { GetContext()->DestroyContext(); } } BEGIN_MESSAGE_MAP(CGenericDoc, COleServerDoc) //{{AFX_MSG_MAP(CGenericDoc) ON_COMMAND(ID_EDIT_COPY, OnEditCopy) ON_COMMAND(ID_OLE_UPDATE, OnOleUpdate) ON_UPDATE_COMMAND_UI(ID_OLE_UPDATE, OnUpdateOleUpdate) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CGenericDoc diagnostics #ifdef _DEBUG void CGenericDoc::AssertValid() const { COleServerDoc::AssertValid(); } void CGenericDoc::Dump(CDumpContext& dc) const { COleServerDoc::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CGenericDoc serialization void CGenericDoc::Serialize(CArchive& ar) { TRACE("CGenericDoc::Serialize called\n"); BOOL bCanSerialize = TRUE; if(ar.IsLoading()) { #ifdef XP_WIN32 // Determine name of actual document. CFile *pFile = ar.GetFile(); CString csFile; if(pFile) { csFile = pFile->GetFilePath(); } // Determine if this is the same file that was passed into the // OnOpenDocument function. // If so, then we can not read our OLE format. bCanSerialize = (csFile.CompareNoCase(m_csOpenDocumentFile) != 0); // However, if they're both empty, then we need to allow this. if(csFile.IsEmpty() && m_csOpenDocumentFile.IsEmpty()) { bCanSerialize = TRUE; } TRACE("%d = !(%s==%s)\n", bCanSerialize, (const char *)csFile, (const char *)m_csOpenDocumentFile); #else // 16 bit can not turn a file handle into a file name. // If our document name is set, then say we can't serialize. bCanSerialize = !(m_bOpenDocumentFileSet && !m_csOpenDocumentFile.IsEmpty()); TRACE("%d = !(%d && !%d)\n", m_bOpenDocumentFileSet, !m_csOpenDocumentFile.IsEmpty()); #endif } // We can only serialize if we're not looking at a local file which we've opened up. if(bCanSerialize) { // Fleshed out for OLE server work. // The below is the common implementation that should work across the // board for all versions of the navigator. // All it does is either read in or write out a URL. if(GetContext() && GetContext()->IsDestroyed() == FALSE) { if (ar.IsStoring()) { TRACE("Storing\n"); // Just shove our current URL into the file. // Get the current history entry. History_entry *pHist = SHIST_GetCurrent(&(GetContext()->GetContext()->hist)); if(pHist != NULL && pHist->address != NULL) { CString csAddress = pHist->address; ar << csAddress; TRACE("URL is %s\n", (const char *)csAddress); } else { TRACE("no history!\n"); CString csEmpty; ar << csEmpty; } } else { TRACE("Reading\n"); // Pretty much just read this in as internet shortcut // format and load it. CString csLoadMe; ar >> csLoadMe; // Do it. TRACE("URL is %s\n", (const char *)csLoadMe); GetContext()->NormalGetUrl(csLoadMe); } } else { TRACE("no context!\n"); if (ar.IsStoring()) { TRACE("Storing\n"); // Hope that ephemeral data were cached, otherwise, no real // harm done. ar << m_csEphemeralHistoryAddress; } else { TRACE("Reading\n"); CString csDontcare; ar >> csDontcare; } } // Next in line should be a string identifying the client which wrote // out the information. // ALL NUMBERS TO BE CONVERTED TO NETWORK BYTE ORDER, SO WORKS ACROSS PLATFORMS. CString csNetscapeVersion; if(ar.IsStoring()) { // Write out our document version number. // This is initially 2.0 ar << theApp.ResolveAppVersion(); TRACE("App version is %s\n", (const char *)theApp.ResolveAppVersion()); // Write out any other information for a particular version here, // prepended by the amount of total bytes to be written. // Hold all integer values (all XP values) to a size that will // translate between win16 and win32. TRACE("Writing version specific information.\n"); // Figure up the size of extra info we'll be writing out. u_long arExtraBytes = 0; // 3.0 beta 6 has some extra information arExtraBytes += sizeof(u_long); // extent.cx arExtraBytes += sizeof(u_long); // extent.cy arExtraBytes += sizeof(u_long); // extent.cx arExtraBytes += sizeof(u_long); // extent.cy // For version 2.0, there was no extra information. ar << htonl(arExtraBytes); // Now, begin writing out extra information. // 3.0 beta 6 TRACE("3.0 beta 6 and later information being written\n"); ar << htonl((u_long)m_csViewExtent.cx); ar << htonl((u_long)m_csViewExtent.cy); TRACE("Write cx=%lu cy=%lu\n", (uint32)m_csViewExtent.cx, (uint32)m_csViewExtent.cy); ar << htonl((u_long)m_csDocumentExtent.cx); ar << htonl((u_long)m_csDocumentExtent.cy); TRACE("Write cx=%lu cy=%lu\n", (uint32)m_csDocumentExtent.cx, (uint32)m_csDocumentExtent.cy); } else { // Read in our document version number. ar >> csNetscapeVersion; TRACE("App version is %s\n", (const char *)csNetscapeVersion); // Next, read in the amount of bytes that are stored. u_long lBytes; ar >> lBytes; lBytes = ntohl(lBytes); if(lBytes != 0) { // Now, depending on which version we're reading from, // figure out the information in the file if we understand // that particular version's file format. // 2.0 won't understand anything, so just read in the number of // extra bytes and continue. // Let the caller of serialize handle thrown exceptions. TRACE("Reading version specific information of %lu bytes.\n", lBytes); char *pBuf = new char[lBytes]; if(pBuf == NULL) { AfxThrowMemoryException(); } ar.Read((void *)pBuf, CASTUINT(lBytes)); // Use and increment this pointer appropriately to read in // extra information. char *va_start = pBuf; // If and only if there are extra bytes, decide what was written out. // In 3.0 beta 6, we wrote out two uint32's first. u_long arVersion30b6 = 0; arVersion30b6 += sizeof(u_long); arVersion30b6 += sizeof(u_long); arVersion30b6 += sizeof(u_long); arVersion30b6 += sizeof(u_long); // Read in this information, otherwise, use a default. if(lBytes >= arVersion30b6) { TRACE("3.0 beta 6 information being retrieved\n"); m_csViewExtent.cx = CASTINT(ntohl(*((u_long *)va_start))); va_start += sizeof(u_long); m_csViewExtent.cy = CASTINT(ntohl(*((u_long *)va_start))); va_start += sizeof(u_long); TRACE("Read cx=%lu cy=%lu\n", (uint32)m_csViewExtent.cx, (uint32)m_csViewExtent.cy); m_csDocumentExtent.cx = CASTINT(ntohl(*((u_long *)va_start))); va_start += sizeof(u_long); m_csDocumentExtent.cy = CASTINT(ntohl(*((u_long *)va_start))); va_start += sizeof(u_long); TRACE("Read cx=%lu cy=%lu\n", (uint32)m_csDocumentExtent.cx, (uint32)m_csDocumentExtent.cy); } else { TRACE("Using default 3.0 beta 6 information\n"); m_csViewExtent = CSize(HIX, HIY); m_csDocumentExtent = CSize(HIX, HIY); } delete[] pBuf; } } // Calling the base class CGenericDoc enables serialization // of the conainer document's COleclientItem objects. TRACE("Calling base serialize\n"); COleServerDoc::Serialize(ar); } } UINT GetChildID() { static UINT s_uChildID; return s_uChildID++; } #ifndef MOZ_NGLAYOUT // Get the size of an embedded item. void CGenericDoc::GetEmbedSize(MWContext *pContext, LO_EmbedStruct *pLayoutData, NET_ReloadMethod Reload) { // First, see if we've already got what Layout is asking for. POSITION rIndex = GetStartPosition(); char *pLayoutAddress = (char *)pLayoutData->embed_src; CNetscapeCntrItem *pItem = NULL; CString csLoad = pLayoutAddress; while(rIndex != NULL) { pItem = (CNetscapeCntrItem *)GetNextClientItem(rIndex); if(pItem == NULL) { break; } // Compare the address to the one layout has. if(pItem->m_csAddress == csLoad) { // One in the same. If this is an OLE container item, then just // reuse the pItem. This allows us to use the existing temp file // and to avoid downloading the file multiple times // // Plugins work differently. A new plugin instance will be created // for each embed, and each plugin instance will have a stream associated // with it. Because the plugin data is cached, we won't be reloading // multiple times // // Only reuse this pItem if it corresponds to an OLE object. We can // tell by looking at the first NPEmbeddedApp. Unfortunately this isn't // very reliable, because the previous embed may still be untyped POSITION rIndex = pItem->m_cplElements.GetHeadPosition(); ASSERT(rIndex != NULL); if (rIndex) { LO_EmbedStruct* pLayoutData = (LO_EmbedStruct *)pItem->m_cplElements.GetNext(rIndex); ASSERT(pLayoutData && pLayoutData->objTag.type == LO_EMBED); if (pLayoutData) { NPEmbeddedApp* pEmbeddedApp = (NPEmbeddedApp*)pLayoutData->objTag.FE_Data; ASSERT(pEmbeddedApp); if (pEmbeddedApp && pEmbeddedApp->type == NP_OLE) break; // it's okay to reuse this pItem } } } // Not a match, reset the value. pItem = NULL; } // See if we have a match. Note that this should never happen with plugins // (OLE only) if(pItem != NULL) { // We have a match. // Add this layout item to the number of layout items accessing us. pItem->m_cplElements.AddTail(pLayoutData); // create and init new structures for managing an embedded plugin NPEmbeddedApp *pEmbeddedApp = NULL; NPWindow *pAppWin = XP_NEW_ZAP(NPWindow); if(pAppWin == NULL) { return; } if(!(pEmbeddedApp = XP_NEW_ZAP(NPEmbeddedApp))) { XP_FREE(pAppWin); return; } pEmbeddedApp->next = pContext->pluginList; pContext->pluginList = pEmbeddedApp; pEmbeddedApp->type = NP_Untyped; // figure it out in EmbedStream pEmbeddedApp->fe_data = (void *)pItem; pEmbeddedApp->wdata = pAppWin; // Go ahead and assign it into the layout data for use in other functions. pLayoutData->objTag.FE_Data = (void *)pEmbeddedApp; pItem->m_iLock++; // Is layout blocking for this information? if(pLayoutData->objTag.width == 0 || pLayoutData->objTag.height == 0) { if(pItem->m_bLoading == TRUE) { // Add this layout element to those that will be unblocked once loaded. pItem->m_cplUnblock.AddTail(pLayoutData); return; } // We have to switch here to handle the old way of doing things, and the new. // Base class which handles embeds is CDCCX. CDCCX *pCX = VOID2CX(pContext->fe.cx, CDCCX); // Go ahead and set the size of the item. if(pItem->m_bDelayed == TRUE) { LTRB Rect; pCX->DisplayIcon(Rect.left, Rect.top, IL_IMAGE_DELAYED); } else if(pItem->m_bBroken == TRUE) { LTRB Rect; pCX->DisplayIcon(Rect.left, Rect.top, IL_IMAGE_BAD_DATA); } else { CSize csExtents; pItem->GetExtent(&csExtents); csExtents.cx = pCX->Metric2TwipsX(csExtents.cx); csExtents.cy = pCX->Metric2TwipsY(csExtents.cy); if ( pLayoutData->objTag.width ) csExtents.cx = pLayoutData->objTag.width; if ( pLayoutData->objTag.height ) csExtents.cy = pLayoutData->objTag.height; pLayoutData->objTag.width = csExtents.cx; pLayoutData->objTag.height = csExtents.cy; } return; } else { if(pItem->m_bLoading == TRUE) { // We'll need to update this once the load is completed. pItem->m_cplDisplay.AddTail(pLayoutData); return; } } } else { BOOL bPrinting = GetContext()->IsPrintContext(); NPEmbeddedApp* pEmbeddedApp; CString theURL; const char * tempURL; PA_LOCK(tempURL, const char*, pLayoutData->embed_src); theURL = tempURL; BOOL fullPageOLE = FALSE; int index; if ((index = theURL.Find( "internal-ole-viewer:")) > 0) { PA_UNLOCK(pLayoutData->embed_src); index += strlen("internal-ole-viewer:"); PA_FREE(pLayoutData->embed_src); int newEmbed_len = (theURL.GetLength() - index) + 1; pLayoutData->embed_src = (PA_Block)PA_ALLOC(newEmbed_len); char* str; PA_LOCK(str, char *, pLayoutData->embed_src); tempURL = theURL; strncpy(str, tempURL+index, newEmbed_len - 1); str[newEmbed_len - 1] = 0; // null terminated. pLayoutAddress = (char *)pLayoutData->embed_src; PA_UNLOCK(pLayoutData->embed_src); fullPageOLE = TRUE; } else PA_UNLOCK(pLayoutData->embed_src); // Create and init new structures for managing an embedded plugin pEmbeddedApp = NPL_EmbedCreate(pContext, pLayoutData); if(pEmbeddedApp == NULL) return; // We're going to have to load. pItem = new CNetscapeCntrItem(this); if (fullPageOLE) pItem->m_isFullPage = TRUE; pItem->m_iLock++; // Add this layout item to the number of layout items accessing us. pItem->m_cplElements.AddTail(pLayoutData); // Mark what actions to take with layout, depending on blocking or non blocking. // Note that hidden plugins never block layout and they don't require displaying // // If we're printing we will use a cached app from the session data so we don't // need to do either of these if ((pLayoutData->objTag.ele_attrmask & LO_ELE_HIDDEN) == 0 && !bPrinting) { if(pLayoutData->objTag.width == 0 || pLayoutData->objTag.height == 0) { // Layout is blocking, be sure to unblock once loaded. pItem->m_cplUnblock.AddTail(pLayoutData); } else { // Layout isn't blocking, but we'll need to manually display this once it is loaded. pItem->m_cplDisplay.AddTail(pLayoutData); } } // If we are printing a plugin then NPL_EmbedCreate() just returns the // cached app from the session data if (bPrinting) { // Because we are using a cached app from the session data, save // the existing pItem so we can restore it when we free the embed item pItem->m_pOriginalItem = (CNetscapeCntrItem*)pEmbeddedApp->fe_data; ASSERT(pItem->m_pOriginalItem); if (pEmbeddedApp->type == NP_OLE && pItem->m_pOriginalItem && pItem->m_pOriginalItem->m_bDelayed == FALSE && pItem->m_pOriginalItem->m_bBroken == FALSE && pItem->m_pOriginalItem->m_bLoading == FALSE) { // Have the OLE object load itself from the same temp file if (pItem->m_pOriginalItem) { pItem->m_csFileName = pItem->m_pOriginalItem->m_csFileName; pItem->CreateCloneFrom( pItem->m_pOriginalItem); } CSize csExtents; CDCCX* pCX = VOID2CX(pContext->fe.cx, CDCCX); pItem->m_pOriginalItem->GetExtent(&csExtents); csExtents.cx = pCX->Metric2TwipsX(csExtents.cx); csExtents.cy = pCX->Metric2TwipsY(csExtents.cy); if ( pLayoutData->objTag.width ) csExtents.cx = pLayoutData->objTag.width; if ( pLayoutData->objTag.height ) csExtents.cy = pLayoutData->objTag.height; pLayoutData->objTag.width = csExtents.cx; pLayoutData->objTag.height = csExtents.cy; // In the printing case, an OLE container is not windowed LO_SetEmbedType(pLayoutData, PR_FALSE); } else { // so layout will not block on us. Since this embed element is missing. pLayoutData->objTag.width = 1; pLayoutData->objTag.height = 1; } } else { if(pEmbeddedApp->wdata == NULL) { // Just created pEmbeddedApp->wdata = XP_NEW_ZAP(NPWindow); if(pEmbeddedApp->wdata == NULL) return; } else { // Check if there's a child window and if we're in a frame cell. Note: // there may not be a window, especially if the plugin is in a nested table if (pContext->is_grid_cell && pEmbeddedApp->wdata->window && (pEmbeddedApp->wdata->type == NPWindowTypeWindow)) { // Reparent the window onto the current window in case it got // moved from a frame cell (e.g. during a resize) ::SetParent((HWND)pEmbeddedApp->wdata->window, WINCX(pContext)->GetView()->m_hWnd); ::ShowWindow((HWND)pEmbeddedApp->wdata->window, SW_SHOW); // Adobe hack. Turn on clip children CGenericView *pGView = WINCX(pContext)->GetView(); if(pGView) { CFrameGlue *pGlue = pGView->GetFrame(); if(pGlue) { pGlue->ClipChildren(CWnd::FromHandle((HWND)pEmbeddedApp->wdata->window), TRUE); } } } } pItem->m_bLoading = TRUE; } pEmbeddedApp->fe_data = (void *)pItem; pItem->m_csAddress = pLayoutAddress; // Go ahead and assign it into the layout data for use in other functions. pLayoutData->objTag.FE_Data = (void *)pEmbeddedApp; // Now that we've set the NPEmbeddedApp's fe_data and layout's FE_Data we can start the embed if (NPL_EmbedStart(pContext, pLayoutData, pEmbeddedApp) != NPERR_NO_ERROR) { // Something went wrong. Time to clean up. The XP code has already deleted // the NPEmbeddedApp pLayoutData->objTag.FE_Data = NULL; pItem->m_bLoading = FALSE; delete pItem; } } } #endif /* MOZ_NGLAYOUT */ #ifndef MOZ_NGLAYOUT void CGenericDoc::FreeEmbedElement(MWContext *pContext, LO_EmbedStruct *pLayoutData) { NPEmbeddedApp* pEmbeddedApp = (NPEmbeddedApp*)pLayoutData->objTag.FE_Data; CNetscapeCntrItem* pItem = NULL; CNetscapeCntrItem* curItem = NULL; int32 iRefCountIndicator = 0; // It's possible that pEmbeddedApp is NULL if the plugin failed to // initialize if (! pEmbeddedApp) return; // this thing is going to be decremented in NPL_EmbedDelete, so iRefCountIndicator = NPL_GetEmbedReferenceCount(pEmbeddedApp); pItem = (CNetscapeCntrItem *)pEmbeddedApp->fe_data; if(iRefCountIndicator > 0) { // Restore the pItem if necessary. This is needed for printing of // embedded apps because we use the cached app from the session data // See comments in GetEmbedSize() if (pItem && pItem->m_pOriginalItem) pEmbeddedApp->fe_data = (void *)pItem->m_pOriginalItem; } // pEmbeddedApp will be freed in the call to NPL_EmbedDelete NPWindow *pAppWin = pEmbeddedApp->wdata; BOOL bFullPage = pEmbeddedApp->pagePluginType == NP_FullPage; // for OLE to clean up the CNetscapeCntrItem. if (pEmbeddedApp->type == NP_OLE) curItem = (CNetscapeCntrItem*)pEmbeddedApp->fe_data; NPL_EmbedDelete(pContext, pLayoutData); pLayoutData->objTag.FE_Data = NULL; // If the item is already gone, bail now. // Also check for npdata->refs being greater or equal 0 // It should not happen but // www.msn.com cashes us on resizing if this is not checked. // but first... Just in case since if pContext were 0 it would've crashed already if(pContext == NULL) iRefCountIndicator++; // because in this case NPL_EmbedDelete does not decrement it if ((pItem == NULL) || (iRefCountIndicator <= 0)) return; // Decrement the lock. pItem->m_iLock--; // Remove this element from the list of items accessing us. pItem->m_cplElements.RemoveAt(pItem->m_cplElements.Find(pLayoutData)); // If the lock is zero'd, then we can free off the embed completely when it expires. // We should probably try saving modified files here.... If I knew how. if(pItem->m_iLock == 0) { CString csRemoveMe = pItem->m_csFileName; // If the app hasn't been deleted, break the reference // from the app to the CNetscapeCntrItem. if (curItem == pItem) { if (pEmbeddedApp != NULL) pEmbeddedApp->fe_data = NULL; if ( pItem->m_lpObject) { LPPERSISTSTORAGE lpPersistStorage = QUERYINTERFACE(pItem->m_lpObject, IPersistStorage); ASSERT(lpPersistStorage != NULL); // removed pItem->IsDirty() when I figure out how to get lpPersistStorage->IsDirty() // to do the right thing. const char* tempPtr = pItem->m_csAddress; if (lpPersistStorage && (pItem->IsDirty())) { lpPersistStorage->Release(); CString destFileName; if (pItem->m_bCanSavedByOLE) { CString buf; int ret; destFileName.Empty(); if (!NET_IsLocalFileURL((char*)tempPtr)) { AfxFormatString1(buf, IDS_SAVE_HTTP_PROMPT, pItem->m_csAddress); ret = AfxMessageBox(buf, MB_YESNO | MB_ICONEXCLAMATION); if (ret == IDYES) { BOOL result = PromptForFileName(pItem, destFileName, destFileName, FALSE); if (!result) // user cancel from the getFileDialog ret = IDNO; } } else { AfxFormatString1(buf, IDS_SAVE_DOC_PROMPT, pItem->m_csDosName); ret = AfxMessageBox(buf, MB_YESNO | MB_ICONEXCLAMATION); destFileName = pItem->m_csDosName; } switch (ret) { case IDYES: { #ifdef XP_WIN32 SCODE sc = S_OK; #else HRESULT sc = S_OK; #endif LPSTORAGE lpStorage = NULL; BOOL result = FALSE; while (!result) { result = DoSaveFile( pItem, destFileName); if (!result) { CString buf; AfxFormatString1(buf, IDS_FILESAVEFAILED, destFileName); AfxMessageBox(buf, MB_OK | MB_ICONEXCLAMATION); if (!PromptForFileName(pItem, destFileName, destFileName, FALSE)) break; } } break; } case IDNO: break; } } } } // If we've a file to remove, do so now. if(csRemoveMe.IsEmpty() == FALSE) { TRY { CFile::Remove(csRemoveMe); } CATCH(CFileException, e) { // Well, we couldn't remove the file on our own. // Some locking must still be in place. // Add it to the list of files to remove on exit. FE_DeleteFileOnExit(csRemoveMe, (const char *)pLayoutData->embed_src); } END_CATCH } } delete pItem; } } #endif /* MOZ_NGLAYOUT */ ///////////////////////////////////////////////////////////////////////////// // CGenericDoc commands BOOL CGenericDoc::OnOpenDocument(const char *pszPathName) { TRACE("CGenericDoc::OnOpenDocument(%s)\n", pszPathName); // Check if we need to set our ability to serialize. // Simply save or empty out the name if available. if(!m_bOpenDocumentFileSet) { m_bOpenDocumentFileSet = TRUE; if(pszPathName) { m_csOpenDocumentFile = pszPathName; } } // Regardless, call the base. // It ends up calling serialize and we handle correctly by // setting the above serialize flag. BOOL bRetval = COleServerDoc::OnOpenDocument(pszPathName); // We only handle loads on real file names passed in. // This causes the actual load. if(pszPathName && m_pContext) { bRetval = m_pContext->OnOpenDocumentCX(pszPathName); } return(bRetval); } // Don't allow the frame to close if we've been told not to let it close. BOOL CGenericDoc::CanCloseFrame(CFrameWnd *pFrame) { if (pFrame && pFrame->m_hWnd) { HWND hPopup; // Check if the frame is displaying a dialog box. If so, don't quit // that window hPopup = ::GetLastActivePopup(pFrame->m_hWnd); if (hPopup != pFrame->m_hWnd) { // Bring the window to the top of the z-order so the user can get a // visual clue as to why we aren't closing ::BringWindowToTop(hPopup); return FALSE; } #ifdef EDITOR // Check for changes to edit document and prompt to save: CFrameGlue *pGlue = CFrameGlue::GetFrameGlue(pFrame); if(pGlue && pGlue->GetMainContext() && !pGlue->GetMainContext()->IsDestroyed() ){ MWContext *pMWContext = pGlue->GetMainContext()->GetContext(); if( pMWContext ){ // Don't close if something is in progess, like drag and drop // (Browser or Editor) // Stop any active Editor plugin if (EDT_IS_EDITOR(pMWContext) && !CheckAndCloseEditorPlugin(pMWContext)) return FALSE; // do NOT return false when in waitingMode. // pMWContext->waitingMode // waitingMode is the mode we are in when a user can not click on // another link, not for whatever bastardized purpose you have // come up with. // Blocking this on criteria in general is confusing as hell to // the user, because when they select exit, and we're looking // up a host, they can't exit. Whey they say exit, we exit. // End of story. // Don't close if we are in the process of saving // a file or user cancels when prompted to save, if( pMWContext && EDT_IS_EDITOR(pMWContext) && pMWContext->edit_saving_url || (LO_GetEDBuffer( ABSTRACTCX(pMWContext)->GetDocumentContext() ) && !FE_CheckAndSaveDocument(pMWContext)) ) { return FALSE; } } } #endif /* EDITOR */ } // Don't ask the base, it will ask the user to save a file. return(CanClose()); } BOOL CGenericDoc::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // Don't handle ID_FILE_CLOSE, let someone else deal with it. if(nID == ID_FILE_CLOSE) { return(FALSE); } else if(nID == ID_EDIT_COPY) { // If we have an embedded item, we'll want to copy it. // Otherwise, don't handle this message. // By not returning false, we decide to handle it. if(m_pEmbeddedItem == NULL) { // Don't handle it. // This let's the frame deal with it in whatever manner // it needs to. return(FALSE); } } // Otherwise, let the base class handle it. return(COleServerDoc::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)); } COleServerItem *CGenericDoc::OnGetEmbeddedItem() { TRACE("CGenericDoc::OnGetEmbeddedItem() %p\n", this); // Called by the framework only when needed to get a COleServerItem // that is associated with the document. CNetscapeSrvrItem *pItem = new CNetscapeSrvrItem(this); ASSERT_VALID(pItem); return(pItem); } void CGenericDoc::OnEditCopy() { // See OnCmdMsg to see exactly when this is called (special cased). // If there's an embedded item, copy it to the clipboard. // Disable this functionality, and you disable OLE linked documents. // Work around it. Forcing me to implement a solution here is the wrong // thing to do. CNetscapeSrvrItem *pItem = GetEmbeddedItem(); if(pItem != NULL) { pItem->CopyToClipboard(TRUE); } } void CGenericDoc::OnOleUpdate() { TRACE("Updating OLE Embedded Server.\n"); // Have the document update in whatever terms are normally // handled in MFC. // The call to update all items causes the redraw in the container. // The OnUpdateDocument call causes serialization fo the current state. // Also, call SetModifiedFlag to force the implementations to think // that the document is dirty. UpdateAllItems(NULL); SetModifiedFlag(); if(IsInPlaceActive() == FALSE) { // Only update the document if embedded, not in place. OnUpdateDocument(); } } void CGenericDoc::OnUpdateOleUpdate(CCmdUI* pCmdUI) { // Always leave it enabled, don't know exactly when to turn it off or // how to detect. pCmdUI->Enable(TRUE); } void CGenericDoc::OnCloseDocument() { // set the flag so we will not realize the Palette. when closing. // Call the base. COleServerDoc::OnCloseDocument(); } void CGenericDoc::OnDeactivateUI( BOOL bUndoable ) { CInPlaceFrame* pFrameWnd = (CInPlaceFrame*)m_pInPlaceFrame; COleServerDoc::OnDeactivateUI(bUndoable); if(pFrameWnd) pFrameWnd->ReparentControlBars(); } void CGenericDoc::OnShowControlBars( CFrameWnd *pFrameWnd, BOOL bShow ) { CInPlaceFrame* pInPlace = (CInPlaceFrame*)m_pInPlaceFrame; if(pInPlace) pInPlace->ShowControlBars(pFrameWnd, bShow); } // OLE server extension to allow document and server item // to minimally function without a context being // present. void CGenericDoc::CacheEphemeralData() { ASSERT(GetContext()); ASSERT(GetContext()->IsDestroyed() == FALSE); // Basically take any data that we'll need in order to serialize // the document. // That is only in the 2.0 timeline the address of the current // history entry! Whew.... // Do we have a history entry from which to create // the address? History_entry *pHistoryEntry = SHIST_GetCurrent(&(GetContext()->GetContext()->hist)); if(pHistoryEntry == NULL) { // Nothing to do really. } else { // Save anything. // If in the future, we attempt to access the context, and // are unable, we will fall back to this information. m_csEphemeralHistoryAddress = pHistoryEntry->address; } } // Resolve between actual context and ephemeral data. void CGenericDoc::GetContextHistoryAddress(CString &csRetval) { if(GetContext() && GetContext()->IsDestroyed() == FALSE) { History_entry *pHistoryEntry = SHIST_GetCurrent(&(GetContext()->GetContext()->hist)); if(pHistoryEntry == NULL) { csRetval.Empty(); } else { csRetval = pHistoryEntry->address; } } else { // Return whatever epemeral data we have. csRetval = m_csEphemeralHistoryAddress; } } // Enable or disable closing of the frame window. void CGenericDoc::EnableClose(BOOL bEnable) { // Just remember the value. m_bCanClose = bEnable; // We want to recusively walk down all child contexts to // set their values also. MWContext *pChild; XP_List *pTraverse = GetContext()->GetContext()->grid_children; while((pChild = (MWContext*)XP_ListNextObject(pTraverse))) { if(ABSTRACTCX(pChild) && ABSTRACTCX(pChild)->IsDCContext() && VOID2CX(pChild->fe.cx, CDCCX)->GetDocument()) { VOID2CX(pChild->fe.cx, CDCCX)->GetDocument()->EnableClose(bEnable); } } } BOOL CGenericDoc::CanClose() { // Assume our retval is initially what we have set. // We want to recursively walk down all child contexts to // see if they are not allowing close. BOOL bRetval = m_bCanClose; if(GetContext()) { MWContext *pChild; XP_List *pTraverse = GetContext()->GetContext()->grid_children; while(bRetval && (pChild = (MWContext*)XP_ListNextObject(pTraverse))) { if(ABSTRACTCX(pChild) && ABSTRACTCX(pChild)->IsDCContext() && VOID2CX(pChild->fe.cx, CDCCX)->GetDocument()) { bRetval = VOID2CX(pChild->fe.cx, CDCCX)->GetDocument()->CanClose(); } } } return(bRetval); } // This OnSaveDocument should take care of 3 types of saving, when this routine is // changed should test on the followin. // 1. saving an HTML file from the File|SaveAs menu. // 2. Save a OLE container item, opening up a .doc file from the directory listing, // after the page is loaded, double click on the item, start in-place edit, type in // some changes, then do File |Save As. // 3. Test as an OLE inplace server. Opening up wordPad, do insert object with // create from file. BOOL CGenericDoc::OnSaveDocument( LPCTSTR lpszPathName ) { // Since this will only happen with File | Save as, so it is safe to do this. CWinCX* pwincx = (CWinCX*)m_pContext; CNetscapeCntrItem *pItem = (CNetscapeCntrItem *)GetInPlaceActiveItem(CWnd::FromHandle(pwincx->GetPane())); if (pItem) { // we had an inplace edited item. CString fileName; if (lpszPathName == NULL) {// need to bring up the saveAs dialog box // here. BOOL result = PromptForFileName(pItem, fileName, pItem->m_csAddress, FALSE); if (!result) return TRUE; // user cancel here. } else {// using the default control item name. fileName = pItem->m_csDosName; } BOOL result = DoSaveFile(pItem, fileName); if (!result) { CString buf; AfxFormatString1(buf, IDS_FILESAVEFAILED, fileName); AfxMessageBox(buf, MB_OK | MB_ICONEXCLAMATION); } return TRUE; // return true here, so the regular navigator save as dialog box // will not shows up. This is to handle File | Save As ... // when we are in inplace edited. } POSITION pos = GetStartPosition( ); if (GetNextServerItem(pos)) // OLE contain as us to save the document. return COleServerDoc::OnSaveDocument(lpszPathName); else return FALSE; // let navigator does the normal saveAs operation. } BOOL CGenericDoc::PromptForFileName(CNetscapeCntrItem* pItem, CString& lpszPathName, CString &orgFileName, BOOL useDefaultDocName) { CString filter; CString ext; int dot_index = orgFileName.ReverseFind('.'); if (dot_index > 0) { ext = orgFileName.Right(3); } char name[255]; pItem->GetUserType(USERCLASSTYPE_FULL, filter); OPENFILENAME fname; filter += '\0'; filter += "*."; filter += ext; filter += '\0'; CString s; s.LoadString(IDS_FILTER_ALLFILES); filter += s; filter += '\0'; filter += "*.*"; filter += '\0'; filter += '\0'; memset(&fname, 0, sizeof(fname)); fname.lStructSize = sizeof(OPENFILENAME); fname.hwndOwner = pItem->GetActiveView()->GetSafeHwnd(); fname.lpstrFilter = filter; fname.lpstrCustomFilter = NULL; fname.nFilterIndex = 1; char FullName[255]; FullName[0] = 0; strcpy(&FullName[1], ext); fname.lpstrFile = FullName; fname.nMaxFile = _MAX_PATH; fname.lpstrFileTitle = name; fname.nMaxFileTitle = _MAX_FNAME; fname.lpstrInitialDir = NULL; CString title; CString temp; title.LoadString(IDS_SAVE_AS); pItem->GetUserType(USERCLASSTYPE_FULL, temp); title += temp; pItem->GetUserType(USERCLASSTYPE_FULL, title); fname.lpstrTitle = title; fname.lpstrDefExt = ext; fname.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; BOOL bResult; bResult = FEU_GetSaveFileName(&fname); // prompt, filter, index, CWnd::FromHandle(m_hWnd)); if (bResult) { // user choose OK lpszPathName = FullName; } return bResult; } // If there is an error, an should ask user for futher info to save to the file, then return FALSE, // otherwise return TRUE. meaning this file can not be saved at all. BOOL CGenericDoc::DoSaveFile( CNetscapeCntrItem* pItem, LPCTSTR lpszPathName) { COleDispatchDriver pDispDrv; #if _MSC_VER < 1200 int _convert; #else USES_CONVERSION; #endif BOOL cont = TRUE; LPCOLESTR lpsz =T2COLE(lpszPathName); #ifdef XP_WIN32 LPDISPATCH pdisp = QUERYINTERFACE(pItem->m_lpObject, IDispatch); WIN32_FIND_DATA fileHandle; HANDLE fHandle = FindFirstFile(lpszPathName, &fileHandle); // only do this for existing file. if ((fHandle != INVALID_HANDLE_VALUE) && pdisp && (pItem->m_idSavedAs != DISPID_UNKNOWN)){ pDispDrv.AttachDispatch( pdisp, FALSE); if (pItem->m_idSavedAs) { VARIANT v; TRY { static BYTE parms[] = VTS_VARIANT; V_VT(&v) = VT_BSTR; V_BSTR(&v) = SysAllocString(lpsz); pDispDrv.InvokeHelper(pItem->m_idSavedAs, DISPATCH_METHOD, VT_EMPTY, (void*)NULL, parms, &v); cont = FALSE; } CATCH( COleException, e) { if (StgIsStorageFile(lpsz) != S_OK) { // this doc object does not support compound file, // should abort saving operation here. if (!pItem->ReportError(e->m_sc)) { AfxMessageBox(AFX_IDP_FAILED_TO_SAVE_DOC); } cont = FALSE; } } AND_CATCH( COleDispatchException, e) { if (StgIsStorageFile(lpsz) != S_OK) { // this doc object does not support compound file, // should abort saving operation here. // Display message box for dispatch exceptions. AfxMessageBox((LPCTSTR)e->m_strDescription, MB_ICONEXCLAMATION | MB_OK); cont = FALSE; } } END_CATCH } } if (!cont) { pDispDrv.DetachDispatch(); pdisp->Release(); return TRUE; } #endif HRESULT sc; lpsz =T2COLE(lpszPathName); IStorage * pStg; sc = StgOpenStorage(lpsz, NULL, STGM_READWRITE|STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE, 0, 0, &pStg); if (sc != S_OK) { // convert existing storage file sc = StgCreateDocfile(lpsz, STGM_READWRITE| STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE, 0, &pStg); } if (sc == S_OK) { LPPERSISTSTORAGE lpPersistStorage = QUERYINTERFACE(pItem->m_lpObject, IPersistStorage); ASSERT(lpPersistStorage != NULL); if (lpPersistStorage) { sc = lpPersistStorage->HandsOffStorage(); if (sc == S_OK) { sc = ::OleSave(lpPersistStorage, pStg, FALSE); if (sc == S_OK) { lpPersistStorage->SaveCompleted(NULL); pStg->Commit(STGC_DEFAULT); } pStg->Release(); } lpPersistStorage->Release(); if (sc != S_OK) { // error handling here. return FALSE; } else return TRUE; } } return FALSE; }