gecko-dev/cmd/winfe/gendoc.cpp
1998-07-24 00:48:33 +00:00

1234 lines
40 KiB
C++

/* -*- 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;
}