|
|
/*************************************************************************
** ** OLE 2 Container Sample Code ** ** cntrline.c ** ** This file contains ContainerLine methods. ** ** (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved ** *************************************************************************/
#include "outline.h"
OLEDBGDATA
extern LPOUTLINEAPP g_lpApp; extern IUnknownVtbl g_CntrLine_UnknownVtbl; extern IOleClientSiteVtbl g_CntrLine_OleClientSiteVtbl; extern IAdviseSinkVtbl g_CntrLine_AdviseSinkVtbl;
#if defined( INPLACE_CNTR )
extern IOleInPlaceSiteVtbl g_CntrLine_OleInPlaceSiteVtbl; extern BOOL g_fInsideOutContainer; #endif // INPLACE_CNTR
// REVIEW: should use string resource for messages
char ErrMsgDoVerb[] = "OLE object action failed!";
/* prototype for static functions */ static void InvertDiffRect(LPRECT lprcPix, LPRECT lprcObj, HDC hDC);
/*************************************************************************
** ContainerLine ** This object represents the location within the container where ** the embedded/linked object lives. It exposes interfaces to the ** object that allow the object to get information about its ** embedding site and to announce notifications of important events ** (changed, closed, saved) ** ** The ContainerLine exposes the following interfaces: ** IUnknown ** IOleClientSite ** IAdviseSink *************************************************************************/
/*************************************************************************
** ContainerLine::IUnknown interface implementation *************************************************************************/
// IUnknown::QueryInterface
STDMETHODIMP CntrLine_Unk_QueryInterface( LPUNKNOWN lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj); }
// IUnknown::AddRef
STDMETHODIMP_(ULONG) CntrLine_Unk_AddRef(LPUNKNOWN lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgAddRefMethod(lpThis, "IUnknown");
return ContainerLine_AddRef(lpContainerLine); }
// IUnknown::Release
STDMETHODIMP_(ULONG) CntrLine_Unk_Release(LPUNKNOWN lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgReleaseMethod(lpThis, "IUnknown");
return ContainerLine_Release(lpContainerLine); }
/*************************************************************************
** ContainerLine::IOleClientSite interface implementation *************************************************************************/
// IOleClientSite::QueryInterface
STDMETHODIMP CntrLine_CliSite_QueryInterface( LPOLECLIENTSITE lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj); }
// IOleClientSite::AddRef
STDMETHODIMP_(ULONG) CntrLine_CliSite_AddRef(LPOLECLIENTSITE lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgAddRefMethod(lpThis, "IOleClientSite");
return ContainerLine_AddRef(lpContainerLine); }
// IOleClientSite::Release
STDMETHODIMP_(ULONG) CntrLine_CliSite_Release(LPOLECLIENTSITE lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgReleaseMethod(lpThis, "IOleClientSite");
return ContainerLine_Release(lpContainerLine); }
// IOleClientSite::SaveObject
STDMETHODIMP CntrLine_CliSite_SaveObject(LPOLECLIENTSITE lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine; LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg; SCODE sc = S_OK; HRESULT hrErr;
OLEDBG_BEGIN2("CntrLine_CliSite_SaveObject\r\n")
if (! lpPersistStg) { /* OLE2NOTE: The object is NOT loaded. a container must be
** prepared for the fact that an object that it thinks it ** has unloaded may still call IOleClientSite::SaveObject. ** in this case the container should fail the save call and ** NOT try to reload the object. this scenario arises if you ** have a in-process server (DLL object) which has a ** connected linking client. even after the embedding ** container unloads the DLL object, the link connection ** keeps the object alive. it may then as part of its ** shutdown try to call IOCS::SaveObject, but then it is too ** late. */ OLEDBG_END2 return ResultFromScode(E_FAIL); }
// mark ContainerDoc as now dirty
OutlineDoc_SetModified( (LPOUTLINEDOC)lpContainerLine->m_lpDoc, TRUE, TRUE, FALSE);
/* Tell OLE object to save itself
** OLE2NOTE: it is NOT sufficient to ONLY call ** IPersistStorage::Save method. it is also necessary to call ** WriteClassStg. the helper API OleSave does this automatically. */ OLEDBG_BEGIN2("OleSave called\r\n") hrErr=OleSave(lpPersistStg,lpContainerLine->m_lpStg, TRUE/*fSameAsLoad*/); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: OleSave returned", hrErr); sc = GetScode(hrErr); }
// OLE2NOTE: even if OleSave fails, SaveCompleted must be called.
OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n") hrErr = lpPersistStg->lpVtbl->SaveCompleted(lpPersistStg, NULL); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr); if (sc == S_OK) sc = GetScode(hrErr); }
OLEDBG_END2 return ResultFromScode(sc); }
// IOleClientSite::GetMoniker
STDMETHODIMP CntrLine_CliSite_GetMoniker( LPOLECLIENTSITE lpThis, DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER FAR* lplpmk ) { LPCONTAINERLINE lpContainerLine;
lpContainerLine=((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OLEDBG_BEGIN2("CntrLine_CliSite_GetMoniker\r\n")
// OLE2NOTE: we must make sure to set output pointer parameters to NULL
*lplpmk = NULL;
switch (dwWhichMoniker) {
case OLEWHICHMK_CONTAINER: /* OLE2NOTE: create a FileMoniker which identifies the
** entire container document. */ *lplpmk = OleDoc_GetFullMoniker( (LPOLEDOC)lpContainerLine->m_lpDoc, dwAssign ); break;
case OLEWHICHMK_OBJREL:
/* OLE2NOTE: create an ItemMoniker which identifies the
** OLE object relative to the container document. */ *lplpmk = ContainerLine_GetRelMoniker(lpContainerLine, dwAssign); break;
case OLEWHICHMK_OBJFULL: { /* OLE2NOTE: create an absolute moniker which identifies the
** OLE object in the container document. this moniker is ** created as a composite of the absolute moniker for the ** entire document appended with an item moniker which ** identifies the OLE object relative to the document. */
*lplpmk = ContainerLine_GetFullMoniker(lpContainerLine, dwAssign); break; } }
OLEDBG_END2
if (*lplpmk != NULL) return NOERROR; else return ResultFromScode(E_FAIL); }
// IOleClientSite::GetContainer
STDMETHODIMP CntrLine_CliSite_GetContainer( LPOLECLIENTSITE lpThis, LPOLECONTAINER FAR* lplpContainer ) { LPCONTAINERLINE lpContainerLine; HRESULT hrErr;
OLEDBG_BEGIN2("CntrLine_CliSite_GetContainer\r\n")
lpContainerLine=((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
hrErr = OleDoc_QueryInterface( (LPOLEDOC)lpContainerLine->m_lpDoc, &IID_IOleContainer, (LPVOID FAR*)lplpContainer );
OLEDBG_END2 return hrErr; }
// IOleClientSite::ShowObject
STDMETHODIMP CntrLine_CliSite_ShowObject(LPOLECLIENTSITE lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; LPLINELIST lpLL = OutlineDoc_GetLineList(lpOutlineDoc); int nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine); HWND hWndFrame = OutlineApp_GetFrameWindow(g_lpApp);
OLEDBG_BEGIN2("CntrLine_CliSite_ShowObject\r\n")
/* make sure our doc window is visible and not minimized.
** the OutlineDoc_ShowWindow function will cause the app window ** to show itself SW_SHOWNORMAL. */ if (! IsWindowVisible(hWndFrame) || IsIconic(hWndFrame)) OutlineDoc_ShowWindow(lpOutlineDoc);
BringWindowToTop(hWndFrame);
/* make sure that the OLE object is currently in view. if necessary
** scroll the document in order to bring it into view. */ LineList_ScrollLineIntoView(lpLL, nIndex);
#if defined( INPLACE_CNTR )
/* after the in-place object is scrolled into view, we need to ask
** it to update its rect for the new clip rect coordinates */ ContainerDoc_UpdateInPlaceObjectRects((LPCONTAINERDOC)lpOutlineDoc, 0); #endif
OLEDBG_END2 return NOERROR; }
// IOleClientSite::OnShowWindow
STDMETHODIMP CntrLine_CliSite_OnShowWindow(LPOLECLIENTSITE lpThis, BOOL fShow) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; LPLINELIST lpLL = OutlineDoc_GetLineList(lpOutlineDoc); int nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);
if (fShow) { OLEDBG_BEGIN2("CntrLine_CliSite_OnShowWindow(TRUE)\r\n")
/* OLE2NOTE: we need to hatch out the OLE object now; it has
** just been opened in a window elsewhere (open editing as ** opposed to in-place activation). ** force the line to re-draw with the hatch. */ lpContainerLine->m_fObjWinOpen = TRUE; LineList_ForceLineRedraw(lpLL, nIndex, FALSE /*fErase*/);
} else { OLEDBG_BEGIN2("CntrLine_CliSite_OnShowWindow(FALSE)\r\n")
/* OLE2NOTE: the object associated with this container site has
** just closed its server window. we should now remove the ** hatching that indicates that the object is open ** elsewhere. also our window should now come to the top. ** force the line to re-draw without the hatch. */ lpContainerLine->m_fObjWinOpen = FALSE; LineList_ForceLineRedraw(lpLL, nIndex, TRUE /*fErase*/);
BringWindowToTop(lpOutlineDoc->m_hWndDoc); SetFocus(lpOutlineDoc->m_hWndDoc); }
OLEDBG_END2 return NOERROR; }
// IOleClientSite::RequestNewObjectLayout
STDMETHODIMP CntrLine_CliSite_RequestNewObjectLayout(LPOLECLIENTSITE lpThis) { OleDbgOut2("CntrLine_CliSite_RequestNewObjectLayout\r\n");
/* OLE2NOTE: this method is NOT yet used. it is for future layout
** negotiation support. */ return ResultFromScode(E_NOTIMPL); }
/*************************************************************************
** ContainerLine::IAdviseSink interface implementation *************************************************************************/
// IAdviseSink::QueryInterface
STDMETHODIMP CntrLine_AdvSink_QueryInterface( LPADVISESINK lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj); }
// IAdviseSink::AddRef
STDMETHODIMP_(ULONG) CntrLine_AdvSink_AddRef(LPADVISESINK lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgAddRefMethod(lpThis, "IAdviseSink");
return ContainerLine_AddRef(lpContainerLine); }
// IAdviseSink::Release
STDMETHODIMP_(ULONG) CntrLine_AdvSink_Release (LPADVISESINK lpThis) { LPCONTAINERLINE lpContainerLine = ((struct COleClientSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgReleaseMethod(lpThis, "IAdviseSink");
return ContainerLine_Release(lpContainerLine); }
// IAdviseSink::OnDataChange
STDMETHODIMP_(void) CntrLine_AdvSink_OnDataChange( LPADVISESINK lpThis, FORMATETC FAR* lpFormatetc, STGMEDIUM FAR* lpStgmed ) { OleDbgOut2("CntrLine_AdvSink_OnDataChange\r\n"); // We are not interested in data changes (only view changes)
// (ie. nothing to do)
}
STDMETHODIMP_(void) CntrLine_AdvSink_OnViewChange( LPADVISESINK lpThis, DWORD aspects, LONG lindex ) { LPCONTAINERLINE lpContainerLine; LPOUTLINEDOC lpOutlineDoc; HWND hWndDoc; LPLINELIST lpLL; MSG msg; int nIndex;
OLEDBG_BEGIN2("CntrLine_AdvSink_OnViewChange\r\n")
lpContainerLine = ((struct CAdviseSinkImpl FAR*)lpThis)->lpContainerLine; lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
/* OLE2NOTE: at this point we simply invalidate the rectangle of
** the object to force a repaint in the future, we mark ** that the extents of the object may have changed ** (m_fDoGetExtent=TRUE), and we post a message ** (WM_U_UPDATEOBJECTEXTENT) to our document that one or more ** OLE objects may need to have their extents updated. later ** when this message is processed, the document loops through ** all lines to see if any are marked as needing an extent update. ** if infact the extents did change. this can be done by calling ** IViewObject2::GetExtent to retreive the object's current ** extents and comparing with the last known extents for the ** object. if the extents changed, then relayout space for the ** object before drawing. we postpone the check to get ** the extents now because OnViewChange is an asyncronis method, ** and we have to careful not to call any syncronis methods back ** to the object. while it WOULD be OK to call the ** IViewObject2::GetExtent method within the asyncronis ** OnViewChange method (because this method is handled by the ** object handler and never remoted), it is good practise to not ** call any object methods from within an asyncronis ** notification method. ** if there is already WM_U_UPDATEOBJECTEXTENT message waiting ** in our message queue, there is no need to post another one. ** in this way, if the server is updating quicker than we can ** keep up, we do not make unneccsary GetExtent calls. also if ** drawing is disabled, we postpone updating the extents of any ** objects until drawing is re-enabled. */ lpContainerLine->m_fDoGetExtent = TRUE; hWndDoc = OutlineDoc_GetWindow((LPOUTLINEDOC)lpContainerLine->m_lpDoc);
if (lpOutlineDoc->m_nDisableDraw == 0 && ! PeekMessage(&msg, hWndDoc, WM_U_UPDATEOBJECTEXTENT, WM_U_UPDATEOBJECTEXTENT, PM_NOREMOVE | PM_NOYIELD)) { PostMessage(hWndDoc, WM_U_UPDATEOBJECTEXTENT, 0, 0L); }
// force the modified line to redraw.
lpLL = OutlineDoc_GetLineList(lpOutlineDoc); nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine); LineList_ForceLineRedraw(lpLL, nIndex, TRUE /*fErase*/);
OLEDBG_END2 }
// IAdviseSink::OnRename
STDMETHODIMP_(void) CntrLine_AdvSink_OnRename( LPADVISESINK lpThis, LPMONIKER lpmk ) { OleDbgOut2("CntrLine_AdvSink_OnRename\r\n"); /* OLE2NOTE: the Embedding Container has nothing to do here. this
** notification is important for linking situations. it tells ** the OleLink objects to update their moniker because the ** source object has been renamed (track the link source). */ }
// IAdviseSink::OnSave
STDMETHODIMP_(void) CntrLine_AdvSink_OnSave(LPADVISESINK lpThis) { OleDbgOut2("CntrLine_AdvSink_OnSave\r\n"); /* OLE2NOTE: the Embedding Container has nothing to do here. this
** notification is only useful to clients which have set up a ** data cache with the ADVFCACHE_ONSAVE flag. */ }
// IAdviseSink::OnClose
STDMETHODIMP_(void) CntrLine_AdvSink_OnClose(LPADVISESINK lpThis) { OleDbgOut2("CntrLine_AdvSink_OnClose\r\n"); /* OLE2NOTE: the Embedding Container has nothing to do here. this
** notification is important for the OLE's default object handler ** and the OleLink object. it tells them the remote object is ** shutting down. */ }
/*************************************************************************
** ContainerLine Support Functions *************************************************************************/
/* ContainerLine_Init
** ------------------ ** Initialize fields in a newly constructed ContainerLine line object. ** NOTE: ref cnt of ContainerLine initialized to 0 */ void ContainerLine_Init(LPCONTAINERLINE lpContainerLine, int nTab, HDC hDC) { Line_Init((LPLINE)lpContainerLine, nTab, hDC); // init base class fields
((LPLINE)lpContainerLine)->m_lineType = CONTAINERLINETYPE; ((LPLINE)lpContainerLine)->m_nWidthInHimetric = DEFOBJWIDTH; ((LPLINE)lpContainerLine)->m_nHeightInHimetric = DEFOBJHEIGHT; lpContainerLine->m_cRef = 0; lpContainerLine->m_szStgName[0] = '\0'; lpContainerLine->m_fObjWinOpen = FALSE; lpContainerLine->m_fMonikerAssigned = FALSE; lpContainerLine->m_dwDrawAspect = DVASPECT_CONTENT;
lpContainerLine->m_fGuardObj = FALSE; lpContainerLine->m_fDoGetExtent = FALSE; lpContainerLine->m_fDoSetExtent = FALSE; lpContainerLine->m_sizeInHimetric.cx = -1; lpContainerLine->m_sizeInHimetric.cy = -1;
lpContainerLine->m_lpStg = NULL; lpContainerLine->m_lpDoc = NULL; lpContainerLine->m_lpOleObj = NULL; lpContainerLine->m_lpViewObj2 = NULL; lpContainerLine->m_lpPersistStg = NULL; lpContainerLine->m_lpOleLink = NULL; lpContainerLine->m_dwLinkType = 0; lpContainerLine->m_fLinkUnavailable = FALSE; lpContainerLine->m_lpszShortType = NULL;
#if defined( INPLACE_CNTR )
lpContainerLine->m_fIpActive = FALSE; lpContainerLine->m_fUIActive = FALSE; lpContainerLine->m_fIpVisible = FALSE; lpContainerLine->m_lpOleIPObj = NULL; lpContainerLine->m_fInsideOutObj = FALSE; lpContainerLine->m_fIpChangesUndoable = FALSE; lpContainerLine->m_fIpServerRunning = FALSE; lpContainerLine->m_hWndIpObject = NULL; lpContainerLine->m_nHorizScrollShift = 0; #endif // INPLACE_CNTR
INIT_INTERFACEIMPL( &lpContainerLine->m_Unknown, &g_CntrLine_UnknownVtbl, lpContainerLine );
INIT_INTERFACEIMPL( &lpContainerLine->m_OleClientSite, &g_CntrLine_OleClientSiteVtbl, lpContainerLine );
INIT_INTERFACEIMPL( &lpContainerLine->m_AdviseSink, &g_CntrLine_AdviseSinkVtbl, lpContainerLine );
#if defined( INPLACE_CNTR )
INIT_INTERFACEIMPL( &lpContainerLine->m_OleInPlaceSite, &g_CntrLine_OleInPlaceSiteVtbl, lpContainerLine ); #endif // INPLACE_CNTR
}
/* Setup the OLE object associated with the ContainerLine */ BOOL ContainerLine_SetupOleObject( LPCONTAINERLINE lpContainerLine, BOOL fDisplayAsIcon, HGLOBAL hMetaPict ) { DWORD dwDrawAspect = (fDisplayAsIcon ? DVASPECT_ICON : DVASPECT_CONTENT); LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
/* Cache a pointer to the IViewObject2* interface. *Required*
** we need this everytime we draw the object. ** ** OLE2NOTE: We require the object to support IViewObject2 ** interface. this is an extension to the IViewObject interface ** that was added with the OLE 2.01 release. This interface must ** be supported by all object handlers and DLL-based objects. */ lpContainerLine->m_lpViewObj2 = (LPVIEWOBJECT2)OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IViewObject2); if (! lpContainerLine->m_lpViewObj2) { #if defined( _DEBUG )
OleDbgAssertSz( lpContainerLine->m_lpViewObj2,"IViewObject2 NOT supported\r\n"); #endif
return FALSE; }
// Cache a pointer to the IPersistStorage* interface. *Required*
// we need this everytime we save the object.
lpContainerLine->m_lpPersistStg = (LPPERSISTSTORAGE)OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IPersistStorage); if (! lpContainerLine->m_lpPersistStg) { OleDbgAssert(lpContainerLine->m_lpPersistStg); return FALSE; }
// Cache a pointer to the IOleLink* interface if supported. *Optional*
// if supported the object is a link. we need this to manage the link
lpContainerLine->m_lpOleLink = (LPOLELINK)OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IOleLink); if (lpContainerLine->m_lpOleLink) { OLEDBG_BEGIN2("IOleLink::GetUpdateOptions called\r\n") lpContainerLine->m_lpOleLink->lpVtbl->GetUpdateOptions( lpContainerLine->m_lpOleLink, &lpContainerLine->m_dwLinkType); OLEDBG_END2 } else lpContainerLine->m_dwLinkType = 0; // NOT a link
/* get the short user type name of the object. this
** is used all the time when we have to build the object ** verb menu. we will cache this information to make it ** quicker to build the verb menu. */ OleDbgAssert(lpContainerLine->m_lpszShortType == NULL); OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
CallIOleObjectGetUserTypeA( lpContainerLine->m_lpOleObj, USERCLASSTYPE_SHORT, &lpContainerLine->m_lpszShortType );
OLEDBG_END2
/* Perform that standard setup for the OLE object. this includes:
** setup View advise ** Call IOleObject::SetHostNames ** Call OleSetContainedObject */ OleStdSetupAdvises( lpContainerLine->m_lpOleObj, dwDrawAspect, (LPSTR)APPNAME, lpOutlineDoc->m_lpszDocTitle, (LPADVISESINK)&lpContainerLine->m_AdviseSink, TRUE /*fCreate*/ );
#if defined( INPLACE_CNTR )
/* OLE2NOTE: (INSIDE-OUT CONTAINER) An inside-out container should
** check if the object is an inside-out and prefers to be ** activated when visible type of object. if not the object ** should not be allowed to keep its window up after it gets ** UIDeactivated. */ if (g_fInsideOutContainer) { DWORD mstat; OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n") lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus( lpContainerLine->m_lpOleObj, DVASPECT_CONTENT, (DWORD FAR*)&mstat ); OLEDBG_END2
lpContainerLine->m_fInsideOutObj = (BOOL) (mstat & (OLEMISC_INSIDEOUT|OLEMISC_ACTIVATEWHENVISIBLE)); } #endif // INPLACE_CNTR
if (fDisplayAsIcon) { /* user has requested to display icon aspect instead of content
** aspect. ** NOTE: we do not have to delete the previous aspect cache ** because one did not get set up. */ OleStdSwitchDisplayAspect( lpContainerLine->m_lpOleObj, &lpContainerLine->m_dwDrawAspect, dwDrawAspect, hMetaPict, FALSE, /* fDeleteOldAspect */ TRUE, /* fSetupViewAdvise */ (LPADVISESINK)&lpContainerLine->m_AdviseSink, NULL /*fMustUpdate*/ // this can be ignored; update
// for switch to icon not req'd
); } return TRUE; }
/* Create an ContainerLine object and return the pointer */ LPCONTAINERLINE ContainerLine_Create( DWORD dwOleCreateType, HDC hDC, UINT nTab, LPCONTAINERDOC lpContainerDoc, LPCLSID lpclsid, LPSTR lpszFileName, BOOL fDisplayAsIcon, HGLOBAL hMetaPict, LPSTR lpszStgName ) { LPCONTAINERLINE lpContainerLine = NULL; LPOLEOBJECT lpObj = NULL; LPSTORAGE lpDocStg = ContainerDoc_GetStg(lpContainerDoc); DWORD dwDrawAspect = (fDisplayAsIcon ? DVASPECT_ICON : DVASPECT_CONTENT); DWORD dwOleRenderOpt = (fDisplayAsIcon ? OLERENDER_NONE : OLERENDER_DRAW); HRESULT hrErr;
OLEDBG_BEGIN3("ContainerLine_Create\r\n")
if (lpDocStg == NULL) { OleDbgAssertSz(lpDocStg != NULL, "Doc storage is NULL"); goto error; }
lpContainerLine=(LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE)); if (lpContainerLine == NULL) { OleDbgAssertSz(lpContainerLine!=NULL,"Error allocating ContainerLine"); goto error; }
ContainerLine_Init(lpContainerLine, nTab, hDC);
/* OLE2NOTE: in order to avoid re-entrancy we will set a flag to
** guard our object. if this guard is set, then the object is ** not ready to have any OLE interface methods called. it is ** necessary to guard the object this way while it is being ** created or loaded. */ lpContainerLine->m_fGuardObj = TRUE;
/* OLE2NOTE: In order to have a stable ContainerLine object we must
** AddRef the object's refcnt. this will be later released when ** the ContainerLine is deleted. */ ContainerLine_AddRef(lpContainerLine);
lstrcpy(lpContainerLine->m_szStgName, lpszStgName); lpContainerLine->m_lpDoc = lpContainerDoc;
/* Create a new storage for the object inside the doc's storage */ lpContainerLine->m_lpStg = OleStdCreateChildStorage(lpDocStg,lpszStgName); if (lpContainerLine->m_lpStg == NULL) { OleDbgAssert(lpContainerLine->m_lpStg != NULL); goto error; }
lpContainerLine->m_dwLinkType = 0;
switch (dwOleCreateType) {
case IOF_SELECTCREATENEW:
OLEDBG_BEGIN2("OleCreate called\r\n") hrErr = OleCreate ( lpclsid, &IID_IOleObject, dwOleRenderOpt, NULL, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj ); OLEDBG_END2
#if defined( _DEBUG )
if (hrErr != NOERROR) OleDbgOutHResult("OleCreate returned", hrErr); #endif
break;
case IOF_SELECTCREATEFROMFILE:
OLEDBG_BEGIN2("OleCreateFromFile called\r\n")
hrErr = OleCreateFromFileA( &CLSID_NULL, lpszFileName, &IID_IOleObject, dwOleRenderOpt, NULL, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj );
OLEDBG_END2
#if defined( _DEBUG )
if (hrErr != NOERROR) OleDbgOutHResult("OleCreateFromFile returned", hrErr); #endif
break;
case IOF_CHECKLINK:
OLEDBG_BEGIN2("OleCreateLinkToFile called\r\n")
hrErr = OleCreateLinkToFileA( lpszFileName, &IID_IOleObject, dwOleRenderOpt, NULL, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj );
OLEDBG_END2
#if defined( _DEBUG )
if (hrErr != NOERROR) OleDbgOutHResult("OleCreateLinkToFile returned", hrErr); #endif
break; } if (hrErr != NOERROR) goto error;
if (! ContainerLine_SetupOleObject( lpContainerLine, fDisplayAsIcon, hMetaPict)) { goto error; }
/* OLE2NOTE: clear our re-entrancy guard. the object is now ready
** to have interface methods called. */ lpContainerLine->m_fGuardObj = FALSE;
OLEDBG_END3 return lpContainerLine;
error: OutlineApp_ErrorMessage(g_lpApp, "Could not create object!");
// Destroy partially created OLE object
if (lpContainerLine) ContainerLine_Delete(lpContainerLine); OLEDBG_END3 return NULL; }
LPCONTAINERLINE ContainerLine_CreateFromData( HDC hDC, UINT nTab, LPCONTAINERDOC lpContainerDoc, LPDATAOBJECT lpSrcDataObj, DWORD dwCreateType, CLIPFORMAT cfFormat, BOOL fDisplayAsIcon, HGLOBAL hMetaPict, LPSTR lpszStgName ) { HGLOBAL hData = NULL; LPCONTAINERLINE lpContainerLine = NULL; LPOLEOBJECT lpObj = NULL; LPSTORAGE lpDocStg = ContainerDoc_GetStg(lpContainerDoc); DWORD dwDrawAspect = (fDisplayAsIcon ? DVASPECT_ICON : DVASPECT_CONTENT); DWORD dwOleRenderOpt; FORMATETC renderFmtEtc; LPFORMATETC lpRenderFmtEtc = NULL; HRESULT hrErr; LPUNKNOWN lpUnk = NULL;
OLEDBG_BEGIN3("ContainerLine_CreateFromData\r\n")
if (dwCreateType == OLECREATEFROMDATA_STATIC && cfFormat != 0) { // a particular type of static object should be created
dwOleRenderOpt = OLERENDER_FORMAT; lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc;
if (cfFormat == CF_METAFILEPICT) SETDEFAULTFORMATETC(renderFmtEtc, cfFormat, TYMED_MFPICT); else if (cfFormat == CF_BITMAP) SETDEFAULTFORMATETC(renderFmtEtc, cfFormat, TYMED_GDI); else SETDEFAULTFORMATETC(renderFmtEtc, cfFormat, TYMED_HGLOBAL);
} else if (dwCreateType == OLECREATEFROMDATA_STATIC && fDisplayAsIcon) { // a link that currently displayed as an icon needs to be
// converted to a STATIC picture object. this case is driven
// from "BreakLink" in the "Links" dialog. because the current
// data in the source object's cache is DVASPECT_ICON we need
// to tell the OleCreateStaticFromData API to look for
// DVASPECT_ICON data. the static object that results however,
// is considered to be displayed in the DVASPECT_CONTENT view.
dwOleRenderOpt = OLERENDER_DRAW; lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc; SETFORMATETC(renderFmtEtc,0,DVASPECT_ICON,NULL,TYMED_NULL,-1); dwDrawAspect = DVASPECT_CONTENT; // static obj displays only CONTENT
} else if (fDisplayAsIcon && hMetaPict) { // a special icon should be used. first we create the object
// OLERENDER_NONE and then we stuff the special icon into the cache.
dwOleRenderOpt = OLERENDER_NONE;
} else if (fDisplayAsIcon && hMetaPict == NULL) { // the object's default icon should be used
dwOleRenderOpt = OLERENDER_DRAW; lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc; SETFORMATETC(renderFmtEtc,0,DVASPECT_ICON,NULL,TYMED_NULL,-1);
} else { // create standard DVASPECT_CONTENT/OLERENDER_DRAW object
dwOleRenderOpt = OLERENDER_DRAW; }
if (lpDocStg == NULL) { OleDbgAssertSz(lpDocStg != NULL, "Doc storage is NULL"); goto error; }
lpContainerLine=(LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE)); if (lpContainerLine == NULL) { OleDbgAssertSz(lpContainerLine!=NULL,"Error allocating ContainerLine"); goto error; }
ContainerLine_Init(lpContainerLine, nTab, hDC);
/* OLE2NOTE: in order to avoid re-entrancy we will set a flag to
** guard our object. if this guard is set, then the object is ** not ready to have any OLE interface methods called. it is ** necessary to guard the object this way while it is being ** created or loaded. */ lpContainerLine->m_fGuardObj = TRUE;
/* OLE2NOTE: In order to have a stable ContainerLine object we must
** AddRef the object's refcnt. this will be later released when ** the ContainerLine is deleted. */ ContainerLine_AddRef(lpContainerLine);
lstrcpy(lpContainerLine->m_szStgName, lpszStgName); lpContainerLine->m_lpDoc = lpContainerDoc;
/* Create a new storage for the object inside the doc's storage */ lpContainerLine->m_lpStg = OleStdCreateChildStorage(lpDocStg,lpszStgName); if (lpContainerLine->m_lpStg == NULL) { OleDbgAssert(lpContainerLine->m_lpStg != NULL); goto error; }
switch (dwCreateType) {
case OLECREATEFROMDATA_LINK:
OLEDBG_BEGIN2("OleCreateLinkFromData called\r\n") hrErr = OleCreateLinkFromData ( lpSrcDataObj, &IID_IOleObject, dwOleRenderOpt, lpRenderFmtEtc, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj ); OLEDBG_END2
#if defined( _DEBUG )
if (hrErr != NOERROR) OleDbgOutHResult("OleCreateLinkFromData returned", hrErr); #endif
break;
case OLECREATEFROMDATA_OBJECT:
OLEDBG_BEGIN2("OleCreateFromData called\r\n") hrErr = OleCreateFromData ( lpSrcDataObj, &IID_IOleObject, dwOleRenderOpt, lpRenderFmtEtc, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj ); OLEDBG_END2
#if defined( _DEBUG )
if (hrErr != NOERROR) OleDbgOutHResult("OleCreateFromData returned", hrErr); #endif
break;
case OLECREATEFROMDATA_STATIC:
OLEDBG_BEGIN2("OleCreateStaticFromData called\r\n") hrErr = OleCreateStaticFromData ( lpSrcDataObj, &IID_IOleObject, dwOleRenderOpt, lpRenderFmtEtc, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj ); OLEDBG_END2
#if defined( _DEBUG )
if (hrErr != NOERROR) OleDbgOutHResult("OleCreateStaticFromData returned", hrErr); #endif
break; }
if (hrErr != NOERROR) goto error;
if (! ContainerLine_SetupOleObject( lpContainerLine, fDisplayAsIcon, hMetaPict)) { goto error; }
/* OLE2NOTE: clear our re-entrancy guard. the object is now ready
** to have interface methods called. */ lpContainerLine->m_fGuardObj = FALSE;
OLEDBG_END3 return lpContainerLine;
error: OutlineApp_ErrorMessage(g_lpApp, "Could not create object!"); // Destroy partially created OLE object
if (lpContainerLine) ContainerLine_Delete(lpContainerLine); OLEDBG_END3 return NULL; }
/* ContainerLine_AddRef
** -------------------- ** ** increment the ref count of the line object. ** ** Returns the new ref count on the object */ ULONG ContainerLine_AddRef(LPCONTAINERLINE lpContainerLine) { ++lpContainerLine->m_cRef;
#if defined( _DEBUG )
OleDbgOutRefCnt4( "ContainerLine_AddRef: cRef++\r\n", lpContainerLine, lpContainerLine->m_cRef ); #endif
return lpContainerLine->m_cRef; }
/* ContainerLine_Release
** --------------------- ** ** decrement the ref count of the line object. ** if the ref count goes to 0, then the line is destroyed. ** ** Returns the remaining ref count on the object */ ULONG ContainerLine_Release(LPCONTAINERLINE lpContainerLine) { ULONG cRef;
/*********************************************************************
** OLE2NOTE: when the obj refcnt == 0, then destroy the object. ** ** otherwise the object is still in use. ** *********************************************************************/
cRef = --lpContainerLine->m_cRef;
#if defined( _DEBUG )
OleDbgAssertSz( lpContainerLine->m_cRef >= 0,"Release called with cRef == 0");
OleDbgOutRefCnt4( "ContainerLine_Release: cRef--\r\n", lpContainerLine, cRef ); #endif
if (cRef == 0) ContainerLine_Destroy(lpContainerLine);
return cRef; }
/* ContainerLine_QueryInterface
** ---------------------------- ** ** Retrieve a pointer to an interface on the ContainerLine object. ** ** Returns NOERROR if interface is successfully retrieved. ** E_NOINTERFACE if the interface is not supported */ HRESULT ContainerLine_QueryInterface( LPCONTAINERLINE lpContainerLine, REFIID riid, LPVOID FAR* lplpvObj ) { SCODE sc = E_NOINTERFACE;
/* OLE2NOTE: we must make sure to set all out ptr parameters to NULL. */ *lplpvObj = NULL;
if (IsEqualIID(riid, &IID_IUnknown)) { OleDbgOut4("ContainerLine_QueryInterface: IUnknown* RETURNED\r\n");
*lplpvObj = (LPVOID) &lpContainerLine->m_Unknown; ContainerLine_AddRef(lpContainerLine); sc = S_OK; } else if (IsEqualIID(riid, &IID_IOleClientSite)) { OleDbgOut4("ContainerLine_QueryInterface: IOleClientSite* RETURNED\r\n");
*lplpvObj = (LPVOID) &lpContainerLine->m_OleClientSite; ContainerLine_AddRef(lpContainerLine); sc = S_OK; } else if (IsEqualIID(riid, &IID_IAdviseSink)) { OleDbgOut4("ContainerLine_QueryInterface: IAdviseSink* RETURNED\r\n");
*lplpvObj = (LPVOID) &lpContainerLine->m_AdviseSink; ContainerLine_AddRef(lpContainerLine); sc = S_OK; } #if defined( INPLACE_CNTR )
else if (IsEqualIID(riid, &IID_IOleWindow) || IsEqualIID(riid, &IID_IOleInPlaceSite)) { OleDbgOut4("ContainerLine_QueryInterface: IOleInPlaceSite* RETURNED\r\n");
*lplpvObj = (LPVOID) &lpContainerLine->m_OleInPlaceSite; ContainerLine_AddRef(lpContainerLine); sc = S_OK; } #endif // INPLACE_CNTR
OleDbgQueryInterfaceMethod(*lplpvObj);
return ResultFromScode(sc); }
BOOL ContainerLine_LoadOleObject(LPCONTAINERLINE lpContainerLine) { LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; LPSTORAGE lpDocStg = ContainerDoc_GetStg(lpContainerLine->m_lpDoc); LPOLECLIENTSITE lpOleClientSite; LPMONIKER lpmkObj; LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; BOOL fPrevEnable1; BOOL fPrevEnable2; HRESULT hrErr;
if (lpContainerLine->m_fGuardObj) return FALSE; // object in process of creation
if (lpContainerLine->m_lpOleObj) return TRUE; // object already loaded
OLEDBG_BEGIN3("ContainerLine_LoadOleObject\r\n")
/* OLE2NOTE: in order to avoid re-entrancy we will set a flag to
** guard our object. if this guard is set, then the object is ** not ready to have any OLE interface methods called. it is ** necessary to guard the object this way while it is being ** created or loaded. */ lpContainerLine->m_fGuardObj = TRUE;
/* if object storage is not already open, then open it */ if (! lpContainerLine->m_lpStg) { lpContainerLine->m_lpStg = OleStdOpenChildStorage( lpDocStg, lpContainerLine->m_szStgName, STGM_READWRITE ); if (lpContainerLine->m_lpStg == NULL) { OleDbgAssert(lpContainerLine->m_lpStg != NULL); goto error; } }
/* OLE2NOTE: if the OLE object being loaded is in a data transfer
** document, then we should NOT pass a IOleClientSite* pointer ** to the OleLoad call. This particularly critical if the OLE ** object is an OleLink object. If a non-NULL client site is ** passed to the OleLoad function, then the link will bind to ** the source if its is running. in the situation that we are ** loading the object as part of a data transfer document we do ** not want this connection to be established. even worse, if ** the link source is currently blocked or busy, then this could ** hang the system. it is simplest to never pass a ** IOleClientSite* when loading an object in a data transfer ** document. */ lpOleClientSite = (lpOutlineDoc->m_fDataTransferDoc ? NULL : (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite);
/* OLE2NOTE: we do not want to ever give the Busy/NotResponding
** dialogs when we are loading an object. if the object is a ** link, it will attempt to BindIfRunning to the link source. if ** the link source is currently busy, this could cause the Busy ** dialog to come up. even if the link source is busy, ** we do not want put up the busy dialog. thus we will disable ** the dialog and later re-enable them */ OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2);
OLEDBG_BEGIN2("OleLoad called\r\n") hrErr = OleLoad ( lpContainerLine->m_lpStg, &IID_IOleObject, lpOleClientSite, (LPVOID FAR*)&lpContainerLine->m_lpOleObj ); OLEDBG_END2
// re-enable the Busy/NotResponding dialogs
OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2);
if (hrErr != NOERROR) { OleDbgAssertSz(hrErr == NOERROR, "Could not load object!"); OleDbgOutHResult("OleLoad returned", hrErr); goto error; }
/* Cache a pointer to the IViewObject2* interface. *Required*
** we need this everytime we draw the object. ** ** OLE2NOTE: We require the object to support IViewObject2 ** interface. this is an extension to the IViewObject interface ** that was added with the OLE 2.01 release. This interface must ** be supported by all object handlers and DLL-based objects. */ lpContainerLine->m_lpViewObj2 = (LPVIEWOBJECT2)OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IViewObject2); if (! lpContainerLine->m_lpViewObj2) { #if defined( _DEBUG )
OleDbgAssertSz( lpContainerLine->m_lpViewObj2,"IViewObject2 NOT supported\r\n"); #endif
goto error; }
// Cache a pointer to the IPersistStorage* interface. *Required*
// we need this everytime we save the object.
lpContainerLine->m_lpPersistStg = (LPPERSISTSTORAGE)OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IPersistStorage); if (! lpContainerLine->m_lpPersistStg) { OleDbgAssert(lpContainerLine->m_lpPersistStg); goto error; }
// Cache a pointer to the IOleLink* interface if supported. *Optional*
// if supported the object is a link. we need this to manage the link
if (lpContainerLine->m_dwLinkType != 0) { lpContainerLine->m_lpOleLink = (LPOLELINK)OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, &IID_IOleLink); if (! lpContainerLine->m_lpOleLink) { OleDbgAssert(lpContainerLine->m_lpOleLink); goto error; } }
/* OLE2NOTE: clear our re-entrancy guard. the object is now ready
** to have interface methods called. */ lpContainerLine->m_fGuardObj = FALSE;
/* OLE2NOTE: similarly, if the OLE object being loaded is in a data
** transfer document, then we do NOT need to setup any advises, ** call SetHostNames, SetMoniker, etc. */ if (lpOleClientSite) { /* Setup the Advises (OLE notifications) that we are interested
** in receiving. */ OleStdSetupAdvises( lpContainerLine->m_lpOleObj, lpContainerLine->m_dwDrawAspect, (LPSTR)APPNAME, lpOutlineDoc->m_lpszDocTitle, (LPADVISESINK)&lpContainerLine->m_AdviseSink, FALSE /*fCreate*/ );
/* OLE2NOTE: if the OLE object has a moniker assigned, we need to
** inform the object by calling IOleObject::SetMoniker. this ** will force the OLE object to register in the ** RunningObjectTable when it enters the running state. */ if (lpContainerLine->m_fMonikerAssigned) { lpmkObj = ContainerLine_GetRelMoniker( lpContainerLine, GETMONIKER_ONLYIFTHERE );
if (lpmkObj) { OLEDBG_BEGIN2("IOleObject::SetMoniker called\r\n") lpContainerLine->m_lpOleObj->lpVtbl->SetMoniker( lpContainerLine->m_lpOleObj, OLEWHICHMK_OBJREL, lpmkObj ); OLEDBG_END2 OleStdRelease((LPUNKNOWN)lpmkObj); } }
/* get the Short form of the user type name of the object. this
** is used all the time when we have to build the object ** verb menu. we will cache this information to make it ** quicker to build the verb menu. */ OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n") CallIOleObjectGetUserTypeA( lpContainerLine->m_lpOleObj, USERCLASSTYPE_SHORT, &lpContainerLine->m_lpszShortType );
OLEDBG_END2
#if defined( INPLACE_CNTR )
/* OLE2NOTE: an inside-out container should check if the object
** is an inside-out and prefers to be activated when visible ** type of object. if so, the object should be immediately ** activated in-place, BUT NOT UIActived. */ if (g_fInsideOutContainer && lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT) { DWORD mstat; OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n") lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus( lpContainerLine->m_lpOleObj, DVASPECT_CONTENT, (DWORD FAR*)&mstat ); OLEDBG_END2
lpContainerLine->m_fInsideOutObj = (BOOL) (mstat & (OLEMISC_INSIDEOUT|OLEMISC_ACTIVATEWHENVISIBLE));
if ( lpContainerLine->m_fInsideOutObj ) { HWND hWndDoc = OutlineDoc_GetWindow(lpOutlineDoc);
ContainerLine_DoVerb( lpContainerLine, OLEIVERB_INPLACEACTIVATE, NULL, FALSE, FALSE );
/* OLE2NOTE: following this DoVerb(INPLACEACTIVATE) the
** object may have taken focus. but because the ** object is NOT UIActive it should NOT have focus. ** we will make sure our document has focus. */ SetFocus(hWndDoc); } } #endif // INPLACE_CNTR
OLEDBG_END2
}
OLEDBG_END2 return TRUE;
error: OLEDBG_END2 return FALSE; }
/* ContainerLine_CloseOleObject
** ---------------------------- ** Close the OLE object associated with the ContainerLine. ** ** Closing the object forces the object to transition from the ** running state to the loaded state. if the object was not running, ** then there is no effect. it is necessary to close the OLE object ** before releasing the pointers to the OLE object. ** ** Returns TRUE if successfully closed, ** FALSE if closing was aborted. */ BOOL ContainerLine_CloseOleObject( LPCONTAINERLINE lpContainerLine, DWORD dwSaveOption ) { HRESULT hrErr; SCODE sc;
if (lpContainerLine->m_fGuardObj) return FALSE; // object in process of creation
if (! lpContainerLine->m_lpOleObj) return TRUE; // object is NOT loaded
OLEDBG_BEGIN2("IOleObject::Close called\r\n") hrErr = lpContainerLine->m_lpOleObj->lpVtbl->Close( lpContainerLine->m_lpOleObj, (dwSaveOption == OLECLOSE_NOSAVE ? OLECLOSE_NOSAVE : OLECLOSE_SAVEIFDIRTY) ); OLEDBG_END2
#if defined( INPLACE_CNTR )
if (lpContainerLine->m_fIpServerRunning) { /* OLE2NOTE: unlock the lock held on the in-place object.
** it is VERY important that an in-place container ** that also support linking to embeddings properly manage ** the running of its in-place objects. in an outside-in ** style in-place container, when the user clicks ** outside of the in-place active object, the object gets ** UIDeactivated and the object hides its window. in order ** to make the object fast to reactivate, the container ** deliberately does not call IOleObject::Close. the object ** stays running in the invisible unlocked state. the idea ** here is if the user simply clicks outside of the object ** and then wants to double click again to re-activate the ** object, we do not want this to be slow. if we want to ** keep the object running, however, we MUST Lock it ** running. otherwise the object will be in an unstable ** state where if a linking client does a "silent-update" ** (eg. UpdateNow from the Links dialog), then the in-place ** server will shut down even before the object has a chance ** to be saved back in its container. this saving normally ** occurs when the in-place container closes the object. also ** keeping the object in the unstable, hidden, running, ** not-locked state can cause problems in some scenarios. ** ICntrOtl keeps only one object running. if the user ** intiates a DoVerb on another object, then that last ** running in-place active object is closed. a more ** sophistocated in-place container may keep more object running. ** (see CntrLine_IPSite_OnInPlaceActivate) */ lpContainerLine->m_fIpServerRunning = FALSE;
OLEDBG_BEGIN2("OleLockRunning(FALSE,TRUE) called\r\n") OleLockRunning((LPUNKNOWN)lpContainerLine->m_lpOleObj, FALSE, TRUE); OLEDBG_END2 } #endif
if (hrErr != NOERROR) { OleDbgOutHResult("IOleObject::Close returned", hrErr); sc = GetScode(hrErr); if (sc == RPC_E_CALL_REJECTED || sc==OLE_E_PROMPTSAVECANCELLED) return FALSE; // object aborted shutdown
} return TRUE; }
/* ContainerLine_UnloadOleObject
** ----------------------------- ** Close the OLE object associated with the ContainerLine and ** release all pointer held to the object. ** ** Closing the object forces the object to transition from the ** running state to the loaded state. if the object was not running, ** then there is no effect. it is necessary to close the OLE object ** before releasing the pointers to the OLE object. releasing all ** pointers to the object allows the object to transition from ** loaded to unloaded (or passive). */ void ContainerLine_UnloadOleObject( LPCONTAINERLINE lpContainerLine, DWORD dwSaveOption ) { if (lpContainerLine->m_lpOleObj) {
OLEDBG_BEGIN2("IOleObject::Close called\r\n") lpContainerLine->m_lpOleObj->lpVtbl->Close( lpContainerLine->m_lpOleObj, dwSaveOption); OLEDBG_END2
/* OLE2NOTE: we will take our IOleClientSite* pointer away from
** the object before we release all pointers to the object. ** in the scenario where the object is implemented as an ** in-proc server (DLL object), then, if there are link ** connections to the DLL object, it is possible that the ** object will not be destroyed when we release our pointers ** to the object. the existance of the remote link ** connections will hold the object alive. later when these ** strong connections are released, then the object may ** attempt to call IOleClientSite::Save if we had not taken ** away the client site pointer. */ OLEDBG_BEGIN2("IOleObject::SetClientSite(NULL) called\r\n") lpContainerLine->m_lpOleObj->lpVtbl->SetClientSite( lpContainerLine->m_lpOleObj, NULL); OLEDBG_END2
OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpOleObj); lpContainerLine->m_lpOleObj = NULL;
if (lpContainerLine->m_lpViewObj2) { OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpViewObj2); lpContainerLine->m_lpViewObj2 = NULL; } if (lpContainerLine->m_lpPersistStg) { OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpPersistStg); lpContainerLine->m_lpPersistStg = NULL; }
if (lpContainerLine->m_lpOleLink) { OleStdRelease((LPUNKNOWN)lpContainerLine->m_lpOleLink); lpContainerLine->m_lpOleLink = NULL; } }
if (lpContainerLine->m_lpszShortType) { OleStdFreeString(lpContainerLine->m_lpszShortType, NULL); lpContainerLine->m_lpszShortType = NULL; } }
/* ContainerLine_Delete
** -------------------- ** Delete the ContainerLine. ** ** NOTE: we can NOT directly destroy the memory for the ** ContainerLine; the ContainerLine maintains a reference count. a ** non-zero reference count indicates that the object is still ** in-use. The OleObject keeps a reference-counted pointer to the ** ClientLine object. we must take the actions necessary so that the ** ContainerLine object receives Releases for outstanding ** references. when the reference count of the ContainerLine reaches ** zero, then the memory for the object will actually be destroyed ** (ContainerLine_Destroy called). ** */ void ContainerLine_Delete(LPCONTAINERLINE lpContainerLine) { OLEDBG_BEGIN2("ContainerLine_Delete\r\n")
#if defined( INPLACE_CNTR )
if (lpContainerLine == lpContainerLine->m_lpDoc->m_lpLastIpActiveLine) lpContainerLine->m_lpDoc->m_lpLastIpActiveLine = NULL; if (lpContainerLine == lpContainerLine->m_lpDoc->m_lpLastUIActiveLine) lpContainerLine->m_lpDoc->m_lpLastUIActiveLine = NULL; #endif
/* OLE2NOTE: in order to have a stable line object during the
** process of deleting, we intially AddRef the line ref cnt and ** later Release it. This initial AddRef is artificial; it is ** simply done to guarantee that our object does not destroy ** itself until the END of this routine. */ ContainerLine_AddRef(lpContainerLine);
// Unload the loaded OLE object
if (lpContainerLine->m_lpOleObj) ContainerLine_UnloadOleObject(lpContainerLine, OLECLOSE_NOSAVE);
/* OLE2NOTE: we can NOT directly free the memory for the ContainerLine
** data structure until everyone holding on to a pointer to our ** ClientSite interface and IAdviseSink interface has released ** their pointers. There is one refcnt on the ContainerLine object ** which is held by the container itself. we will release this ** refcnt here. */ ContainerLine_Release(lpContainerLine);
/* OLE2NOTE: this call forces all external connections to our
** ContainerLine to close down and therefore guarantees that ** we receive all releases associated with those external ** connections. Strictly this call should NOT be necessary, but ** it is defensive coding to make this call. */ OLEDBG_BEGIN2("CoDisconnectObject(lpContainerLine) called\r\n") CoDisconnectObject((LPUNKNOWN)&lpContainerLine->m_Unknown, 0); OLEDBG_END2
#if defined( _DEBUG )
/* at this point the object all references from the OLE object to
** our ContainerLine object should have been released. there ** should only be 1 remaining reference that will be released below. */ if (lpContainerLine->m_cRef != 1) { OleDbgOutRefCnt( "WARNING: ContainerLine_Delete: cRef != 1\r\n", lpContainerLine, lpContainerLine->m_cRef ); } #endif
ContainerLine_Release(lpContainerLine); // release artificial AddRef above
OLEDBG_END2 }
/* ContainerLine_Destroy
** --------------------- ** Destroy (Free) the memory used by a ContainerLine structure. ** This function is called when the ref count of the ContainerLine goes ** to zero. the ref cnt goes to zero after ContainerLine_Delete forces ** the OleObject to unload and release its pointers to the ** ContainerLine IOleClientSite and IAdviseSink interfaces. */
void ContainerLine_Destroy(LPCONTAINERLINE lpContainerLine) { LPUNKNOWN lpTmpObj;
OLEDBG_BEGIN2("ContainerLine_Destroy\r\n")
// Release the storage opened for the OLE object
if (lpContainerLine->m_lpStg) { lpTmpObj = (LPUNKNOWN)lpContainerLine->m_lpStg; lpContainerLine->m_lpStg = NULL;
OleStdRelease(lpTmpObj); }
if (lpContainerLine->m_lpszShortType) { OleStdFreeString(lpContainerLine->m_lpszShortType, NULL); lpContainerLine->m_lpszShortType = NULL; }
Delete(lpContainerLine); // Free the memory for the structure itself
OLEDBG_END2 }
/* ContainerLine_CopyToDoc
* ----------------------- * * Copy a ContainerLine to another Document (usually ClipboardDoc) */ BOOL ContainerLine_CopyToDoc( LPCONTAINERLINE lpSrcLine, LPOUTLINEDOC lpDestDoc, int nIndex ) { LPCONTAINERLINE lpDestLine = NULL; LPLINELIST lpDestLL = &lpDestDoc->m_LineList; HDC hDC; HRESULT hrErr; BOOL fStatus; LPSTORAGE lpDestDocStg = ((LPOLEDOC)lpDestDoc)->m_lpStg; LPSTORAGE lpDestObjStg = NULL;
lpDestLine = (LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE)); if (lpDestLine == NULL) { OleDbgAssertSz(lpDestLine!=NULL, "Error allocating ContainerLine"); return FALSE; }
hDC = LineList_GetDC(lpDestLL); ContainerLine_Init(lpDestLine, ((LPLINE)lpSrcLine)->m_nTabLevel, hDC); LineList_ReleaseDC(lpDestLL, hDC);
/* OLE2NOTE: In order to have a stable ContainerLine object we must
** AddRef the object's refcnt. this will be later released when ** the ContainerLine is deleted. */ ContainerLine_AddRef(lpDestLine);
lpDestLine->m_lpDoc = (LPCONTAINERDOC)lpDestDoc;
// Copy data of the original source ContainerLine.
((LPLINE)lpDestLine)->m_nWidthInHimetric = ((LPLINE)lpSrcLine)->m_nWidthInHimetric; ((LPLINE)lpDestLine)->m_nHeightInHimetric = ((LPLINE)lpSrcLine)->m_nHeightInHimetric; lpDestLine->m_fMonikerAssigned = lpSrcLine->m_fMonikerAssigned; lpDestLine->m_dwDrawAspect = lpSrcLine->m_dwDrawAspect; lpDestLine->m_sizeInHimetric = lpSrcLine->m_sizeInHimetric; lpDestLine->m_dwLinkType = lpSrcLine->m_dwLinkType;
/* We must create a new sub-storage for the embedded object within
** the destination document's storage. We will first attempt to ** use the same storage name as the source line. if this name is ** in use, then we will allocate a new name. in this way we try ** to keep the name associated with the OLE object unchanged ** through a Cut/Paste operation. */ lpDestObjStg = OleStdCreateChildStorage( lpDestDocStg, lpSrcLine->m_szStgName ); if (lpDestObjStg) { lstrcpy(lpDestLine->m_szStgName, lpSrcLine->m_szStgName); } else { /* the original name was in use, make up a new name. */ ContainerDoc_GetNextStgName( (LPCONTAINERDOC)lpDestDoc, lpDestLine->m_szStgName, sizeof(lpDestLine->m_szStgName) ); lpDestObjStg = OleStdCreateChildStorage( lpDestDocStg, lpDestLine->m_szStgName ); } if (lpDestObjStg == NULL) { OleDbgAssertSz(lpDestObjStg != NULL, "Error creating child stg"); goto error; }
// Copy over storage of the embedded object itself
if (! lpSrcLine->m_lpOleObj) {
/*****************************************************************
** CASE 1: object is NOT loaded. ** because the object is not loaded, we can simply copy the ** object's current storage to the new storage. *****************************************************************/
/* if current object storage is not already open, then open it */ if (! lpSrcLine->m_lpStg) { LPSTORAGE lpSrcDocStg = ((LPOLEDOC)lpSrcLine->m_lpDoc)->m_lpStg;
if (! lpSrcDocStg) goto error;
// open object storage.
lpSrcLine->m_lpStg = OleStdOpenChildStorage( lpSrcDocStg, lpSrcLine->m_szStgName, STGM_READWRITE ); if (lpSrcLine->m_lpStg == NULL) { #if defined( _DEBUG )
OleDbgAssertSz( lpSrcLine->m_lpStg != NULL, "Error opening child stg" ); #endif
goto error; } }
hrErr = lpSrcLine->m_lpStg->lpVtbl->CopyTo( lpSrcLine->m_lpStg, 0, NULL, NULL, lpDestObjStg ); if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: lpSrcObjStg->CopyTo returned", hrErr); goto error; }
fStatus = OleStdCommitStorage(lpDestObjStg);
} else {
/*****************************************************************
** CASE 2: object IS loaded. ** we must tell the object to save into the new storage. *****************************************************************/
SCODE sc = S_OK; LPPERSISTSTORAGE lpPersistStg = lpSrcLine->m_lpPersistStg; OleDbgAssert(lpPersistStg);
OLEDBG_BEGIN2("OleSave called\r\n") hrErr = OleSave(lpPersistStg, lpDestObjStg, FALSE /*fSameAsLoad*/); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: OleSave returned", hrErr); sc = GetScode(hrErr); }
// OLE2NOTE: even if OleSave fails, SaveCompleted must be called.
OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n") hrErr=lpPersistStg->lpVtbl->SaveCompleted(lpPersistStg,NULL); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr); if (sc == S_OK) sc = GetScode(hrErr); }
if (sc != S_OK) goto error;
}
OutlineDoc_AddLine(lpDestDoc, (LPLINE)lpDestLine, nIndex); OleStdVerifyRelease( (LPUNKNOWN)lpDestObjStg, "Copied object stg not released" );
return TRUE;
error:
// Delete any partially created storage.
if (lpDestObjStg) {
OleStdVerifyRelease( (LPUNKNOWN)lpDestObjStg, "Copied object stg not released" );
CallIStorageDestroyElementA( lpDestDocStg, lpDestLine->m_szStgName );
lpDestLine->m_szStgName[0] = '\0'; }
// destroy partially created ContainerLine
if (lpDestLine) ContainerLine_Delete(lpDestLine); return FALSE; }
/* ContainerLine_UpdateExtent
** -------------------------- ** Update the size of the ContainerLine because the extents of the ** object may have changed. ** ** NOTE: because we are using a Windows OwnerDraw ListBox, we must ** constrain the maximum possible height of a line. the ListBox has ** a limitation (unfortunately) that no line can become larger than ** 255 pixels. thus we force the object to scale maintaining its ** aspect ratio if this maximum line height limit is reached. the ** actual maximum size for an object at 100% Zoom is ** 255 ** ** RETURNS TRUE -- if the extents of the object changed ** FALSE -- if the extents did NOT change */ BOOL ContainerLine_UpdateExtent( LPCONTAINERLINE lpContainerLine, LPSIZEL lpsizelHim ) { LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; LPLINELIST lpLL = OutlineDoc_GetLineList(lpOutlineDoc); LPLINE lpLine = (LPLINE)lpContainerLine; int nIndex = LineList_GetLineIndex(lpLL, lpLine); UINT nOrgWidthInHimetric = lpLine->m_nWidthInHimetric; UINT nOrgHeightInHimetric = lpLine->m_nHeightInHimetric; BOOL fWidthChanged = FALSE; BOOL fHeightChanged = FALSE; SIZEL sizelHim; HRESULT hrErr;
if (!lpContainerLine || !lpContainerLine->m_lpOleObj) return FALSE;
if (lpContainerLine->m_fGuardObj) return FALSE; // object in process of creation
OLEDBG_BEGIN3("ContainerLine_UpdateExtent\r\n");
lpContainerLine->m_fDoGetExtent = FALSE;
if (! lpsizelHim) { /* OLE2NOTE: We want to call IViewObject2::GetExtent instead of
** IOleObject::GetExtent. IViewObject2::GetExtent method was ** added in OLE 2.01 release. It always retrieves the ** extents of the object corresponding to that which will be ** drawn by calling IViewObject::Draw. Normally, this is ** determined by the data stored in the data cache. This ** call will never result in a remoted (LRPC) call. */ OLEDBG_BEGIN2("IViewObject2::GetExtent called\r\n") hrErr = lpContainerLine->m_lpViewObj2->lpVtbl->GetExtent( lpContainerLine->m_lpViewObj2, lpContainerLine->m_dwDrawAspect, -1, /*lindex*/ NULL, /*ptd*/ (LPSIZEL)&sizelHim ); OLEDBG_END2 if (hrErr != NOERROR) sizelHim.cx = sizelHim.cy = 0;
lpsizelHim = (LPSIZEL)&sizelHim; }
if (lpsizelHim->cx == lpContainerLine->m_sizeInHimetric.cx && lpsizelHim->cy == lpContainerLine->m_sizeInHimetric.cy) { goto noupdate; }
if (lpsizelHim->cx > 0 || lpsizelHim->cy > 0) { lpContainerLine->m_sizeInHimetric = *lpsizelHim; } else { /* object does not have any extents; let's use our container
** chosen arbitrary size for OLE objects. */ lpContainerLine->m_sizeInHimetric.cx = (long)DEFOBJWIDTH; lpContainerLine->m_sizeInHimetric.cy = (long)DEFOBJHEIGHT; }
ContainerLine_SetLineHeightFromObjectExtent( lpContainerLine, (LPSIZEL)&lpContainerLine->m_sizeInHimetric);
// if height of object changed, then reset the height of line in LineList
if (nOrgHeightInHimetric != lpLine->m_nHeightInHimetric) { LineList_SetLineHeight(lpLL, nIndex, lpLine->m_nHeightInHimetric); fHeightChanged = TRUE; }
fWidthChanged = LineList_RecalcMaxLineWidthInHimetric( lpLL, nOrgWidthInHimetric ); fWidthChanged |= (nOrgWidthInHimetric != lpLine->m_nWidthInHimetric);
if (fHeightChanged || fWidthChanged) { OutlineDoc_ForceRedraw(lpOutlineDoc, TRUE);
// mark ContainerDoc as now dirty
OutlineDoc_SetModified(lpOutlineDoc, TRUE, TRUE, TRUE); }
OLEDBG_END3 return TRUE;
noupdate: OLEDBG_END3 return FALSE; // No UPDATE necessary
}
/* ContainerLine_DoVerb
** -------------------- ** Activate the OLE object and perform a specific verb. */ BOOL ContainerLine_DoVerb( LPCONTAINERLINE lpContainerLine, LONG iVerb, LPMSG lpMsg, BOOL fMessage, BOOL fAction ) { HRESULT hrErr; LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; RECT rcPosRect; OLEDBG_BEGIN3("ContainerLine_DoVerb\r\n")
if (lpContainerLine->m_fGuardObj) { // object in process of creation--Fail the DoVerb call
hrErr = ResultFromScode(E_FAIL); goto error; }
/* if object is not already loaded, then load it now. objects are
** loaded lazily in this manner. */ if (! lpContainerLine->m_lpOleObj) ContainerLine_LoadOleObject(lpContainerLine);
if (! lpContainerLine->m_lpOleObj) { #if defined( _DEBUG )
OleDbgAssertSz( lpContainerLine->m_lpOleObj != NULL, "OLE object not loaded" ); #endif
goto error; }
ExecuteDoVerb:
ContainerLine_GetPosRect(lpContainerLine, (LPRECT)&rcPosRect);
// run the object
hrErr = ContainerLine_RunOleObject(lpContainerLine); if (hrErr != NOERROR) goto error;
/* Tell object server to perform a "verb". */ OLEDBG_BEGIN2("IOleObject::DoVerb called\r\n") hrErr = lpContainerLine->m_lpOleObj->lpVtbl->DoVerb ( lpContainerLine->m_lpOleObj, iVerb, lpMsg, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, -1, OutlineDoc_GetWindow(lpOutlineDoc), (LPCRECT)&rcPosRect ); OLEDBG_END2
/* OLE2NOTE: IOleObject::DoVerb may return a success code
** OLE_S_INVALIDVERB. this SCODE should NOT be considered an ** error; thus it is important to use the "FAILED" macro to ** check for an error SCODE. */ if (FAILED(GetScode(hrErr))) { OleDbgOutHResult("WARNING: lpOleObj->DoVerb returned", hrErr); goto error; }
#if defined( INPLACE_CNTR )
/* OLE2NOTE: we want to keep only 1 inplace server active at any
** given time. so when we start to do a DoVerb on another line, ** then we want to shut down the previously activated server. in ** this way we keep at most one inplace server active at a time. ** because it is possible that the object we want to do DoVerb ** on is handled by the same EXE as that of the previously ** activated server, then we do not want the EXE to be shut down ** only to be launched again. in order to avoid this we will do ** the DoVerb BEFORE trying to shutdown the previous object. */ if (!g_fInsideOutContainer) { ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded( lpContainerLine->m_lpDoc, lpContainerLine); } #endif // INPLACE_CNTR
OLEDBG_END3 return TRUE;
error:
if (lpContainerLine->m_dwLinkType != 0) lpContainerLine->m_fLinkUnavailable = TRUE;
#if defined( INPLACE_CNTR )
/* OLE2NOTE: we want to keep only 1 inplace server active at any
** given time. so when we start to do a DoVerb on another line, ** then we want to shut down the previously activated server. in ** this way we keep at most one inplace server active at a time. ** even though the DoVerb failed, we will still shutdown the ** previous server. it is possible that we ran out of memory and ** that the DoVerb will succeed next time after shutting down ** the pervious server. */ if (!g_fInsideOutContainer) { ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded( lpContainerLine->m_lpDoc, lpContainerLine); } #endif // INPLACE_CNTR
/* OLE2NOTE: if an error occurs we must give the appropriate error
** message box. there are many potential errors that can occur. ** the OLE2.0 user model has specific guidelines as to the ** dialogs that should be displayed given the various potential ** errors (eg. server not registered, unavailable link source. ** the OLE2UI library includes support for most of the ** recommended message dialogs. (see OleUIPrompUser function) */ if (fMessage) { BOOL fReDoVerb = ContainerLine_ProcessOleRunError( lpContainerLine, hrErr, fAction, (lpMsg==NULL && iVerb>=0) /* fMenuInvoked */ ); if (fReDoVerb) { goto ExecuteDoVerb; } }
OLEDBG_END3 return FALSE; }
/* ContainerLine_ProcessOleRunError
* -------------------------------- * * Handle the various errors possible when attempting OleRun of an object. * Popup up appropriate message according to the error and/or take action * specified button pressed by the user. * * OLE2NOTE: The OLE 2.0 User Interface Guidelines specify the messages * that should be given for the following situations: * 1. Link Source Unavailable...goto Links dialog * 2. Server Not Registered...goto Convert dialog * 3. Link Type Changed * 4. Server Not Found * * Returns: TRUE -- repeat IOleObject::DoVerb call. * FALSE -- do NOT repeat IOleObject::DoVerb call. * * Comments: * (see LinkTypeChanged case) */ BOOL ContainerLine_ProcessOleRunError( LPCONTAINERLINE lpContainerLine, HRESULT hrErr, BOOL fAction, BOOL fMenuInvoked ) { LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; HWND hwndParent = OutlineDoc_GetWindow(lpOutlineDoc); SCODE sc = GetScode(hrErr); BOOL fReDoVerb = FALSE;
OleDbgOutHResult("ProcessError", hrErr); if ((sc >= MK_E_FIRST) && (sc <= MK_E_LAST)) goto LinkSourceUnavailable; if (sc == OLE_E_CANT_BINDTOSOURCE) goto LinkSourceUnavailable; if (sc == STG_E_PATHNOTFOUND) goto LinkSourceUnavailable; if (sc == REGDB_E_CLASSNOTREG) goto ServerNotReg; if (sc == OLE_E_STATIC) goto ServerNotReg; // user dblclk'ed a static object w/ no svr reg'd
if (sc == OLE_E_CLASSDIFF) goto LinkTypeChanged; if (sc == CO_E_APPDIDNTREG) goto ServerNotFound; if (sc == CO_E_APPNOTFOUND) goto ServerNotFound; if (sc == E_OUTOFMEMORY) goto OutOfMemory;
if (ContainerLine_IsOleLink(lpContainerLine)) goto LinkSourceUnavailable; else goto ServerNotFound;
/*************************************************************************
** Error handling routines ** *************************************************************************/ LinkSourceUnavailable: if (ID_PU_LINKS == OleUIPromptUser( (WORD)IDD_LINKSOURCEUNAVAILABLE, hwndParent, (LPSTR)APPNAME)) { if (fAction) { ContainerDoc_EditLinksCommand(lpContainerLine->m_lpDoc); } } return fReDoVerb;
ServerNotReg: { LPSTR lpszUserType = NULL; CLIPFORMAT cfFormat; // not used
hrErr = ReadFmtUserTypeStgA( lpContainerLine->m_lpStg, &cfFormat, &lpszUserType);
if (ID_PU_CONVERT == OleUIPromptUser( (WORD)IDD_SERVERNOTREG, hwndParent, (LPSTR)APPNAME, (hrErr == NOERROR) ? lpszUserType : (LPSTR)"Unknown Object")) { if (fAction) { ContainerDoc_ConvertCommand( lpContainerLine->m_lpDoc, TRUE // fMustActivate
); } }
if (lpszUserType) OleStdFreeString(lpszUserType, NULL);
return fReDoVerb; }
LinkTypeChanged: { /* OLE2NOTE: If IOleObject::DoVerb is executed on a Link object and it
** returns OLE_E_CLASSDIFF because the link source is no longer ** the expected class, then if the verb is a semantically ** defined verb (eg. OLEIVERB_PRIMARY, OLEIVERB_SHOW, ** OLEIVERB_OPEN, etc.), then the link should be re-created with ** the new link source and the same verb executed on the new ** link. there is no need to give a message to the user. if the ** user had selected a verb from the object's verb menu ** (fMenuInvoked==TRUE), then we can not be certain of the ** semantics of the verb and whether the new link can still ** support the verb. in this case the user is given a prompt ** telling him to "choose a different command offered by the new ** type". */
LPSTR lpszUserType = NULL;
if (fMenuInvoked) { hrErr = CallIOleObjectGetUserTypeA( lpContainerLine->m_lpOleObj,USERCLASSTYPE_FULL, &lpszUserType);
OleUIPromptUser( (WORD)IDD_LINKTYPECHANGED, hwndParent, (LPSTR)APPNAME, (hrErr == NOERROR) ? lpszUserType : (LPSTR)"Unknown Object" ); } else { fReDoVerb = TRUE; } ContainerLine_ReCreateLinkBecauseClassDiff(lpContainerLine);
if (lpszUserType) OleStdFreeString(lpszUserType, NULL);
return fReDoVerb; }
ServerNotFound:
OleUIPromptUser( (WORD)IDD_SERVERNOTFOUND, hwndParent, (LPSTR)APPNAME); return fReDoVerb;
OutOfMemory:
OleUIPromptUser( (WORD)IDD_OUTOFMEMORY, hwndParent, (LPSTR)APPNAME); return fReDoVerb; }
/* ContainerLine_ReCreateLinkBecauseClassDiff
** ------------------------------------------ ** Re-create the link. The existing link was created when ** the moniker binds to a link source bound of a different class ** than the same moniker currently binds to. the link may be a ** special link object specifically used with the old link ** source class. thus the link object needs to be re-created to ** give the new link source the opportunity to create its own ** special link object. (see description "Custom Link Source") */ HRESULT ContainerLine_ReCreateLinkBecauseClassDiff( LPCONTAINERLINE lpContainerLine ) { LPOLELINK lpOleLink = lpContainerLine->m_lpOleLink; HGLOBAL hMetaPict = NULL; LPMONIKER lpmkLinkSrc = NULL; SCODE sc = E_FAIL; HRESULT hrErr;
if (lpOleLink && lpOleLink->lpVtbl->GetSourceMoniker( lpOleLink, (LPMONIKER FAR*)&lpmkLinkSrc) == NOERROR) {
BOOL fDisplayAsIcon = (lpContainerLine->m_dwDrawAspect==DVASPECT_ICON); STGMEDIUM medium; LPDATAOBJECT lpDataObj = NULL; DWORD dwOleRenderOpt; FORMATETC renderFmtEtc; LPFORMATETC lpRenderFmtEtc = NULL;
// get the current icon if object is displayed as icon
if (fDisplayAsIcon && (lpDataObj = (LPDATAOBJECT)OleStdQueryInterface( (LPUNKNOWN) lpContainerLine->m_lpOleObj,&IID_IDataObject)) != NULL ) { hMetaPict = OleStdGetData( lpDataObj, CF_METAFILEPICT, NULL, DVASPECT_ICON, &medium); OleStdRelease((LPUNKNOWN)lpDataObj); }
if (fDisplayAsIcon && hMetaPict) { // a special icon should be used. first we create the object
// OLERENDER_NONE. then we stuff the special icon into the cache.
dwOleRenderOpt = OLERENDER_NONE;
} else if (fDisplayAsIcon && hMetaPict == NULL) { // the object's default icon should be used
dwOleRenderOpt = OLERENDER_DRAW; lpRenderFmtEtc = (LPFORMATETC)&renderFmtEtc; SETFORMATETC(renderFmtEtc,0,DVASPECT_ICON,NULL,TYMED_NULL,-1);
} else { // create standard DVASPECT_CONTENT/OLERENDER_DRAW object
dwOleRenderOpt = OLERENDER_DRAW; }
// unload original link object
ContainerLine_UnloadOleObject(lpContainerLine, OLECLOSE_SAVEIFDIRTY);
// delete entire contents of the current object's storage
OleStdDestroyAllElements(lpContainerLine->m_lpStg);
OLEDBG_BEGIN2("OleCreateLink called\r\n") hrErr = OleCreateLink ( lpmkLinkSrc, &IID_IOleObject, dwOleRenderOpt, lpRenderFmtEtc, (LPOLECLIENTSITE)&lpContainerLine->m_OleClientSite, lpContainerLine->m_lpStg, (LPVOID FAR*)&lpContainerLine->m_lpOleObj ); OLEDBG_END2
if (hrErr == NOERROR) { if (! ContainerLine_SetupOleObject( lpContainerLine, fDisplayAsIcon, hMetaPict) ) {
// ERROR: setup of the new link failed.
// revert the storage to restore the original link.
ContainerLine_UnloadOleObject( lpContainerLine, OLECLOSE_NOSAVE); lpContainerLine->m_lpStg->lpVtbl->Revert( lpContainerLine->m_lpStg); sc = E_FAIL; } else { sc = S_OK; // IT WORKED!
} } else { sc = GetScode(hrErr); OleDbgOutHResult("OleCreateLink returned", hrErr); // ERROR: Re-creating the link failed.
// revert the storage to restore the original link.
lpContainerLine->m_lpStg->lpVtbl->Revert( lpContainerLine->m_lpStg); } }
if (hMetaPict) OleUIMetafilePictIconFree(hMetaPict); // clean up metafile
return ResultFromScode(sc); }
/* ContainerLine_GetOleObject
** -------------------------- ** return pointer to desired interface of embedded/linked object. ** ** NOTE: this function causes an AddRef to the object. when the caller is ** finished with the object, it must call Release. ** this function does not AddRef the ContainerLine object. */ LPUNKNOWN ContainerLine_GetOleObject( LPCONTAINERLINE lpContainerLine, REFIID riid ) { /* if object is not already loaded, then load it now. objects are
** loaded lazily in this manner. */ if (! lpContainerLine->m_lpOleObj) ContainerLine_LoadOleObject(lpContainerLine);
if (lpContainerLine->m_lpOleObj) return OleStdQueryInterface( (LPUNKNOWN)lpContainerLine->m_lpOleObj, riid ); else return NULL; }
/* ContainerLine_RunOleObject
** -------------------------- ** Load and run the object. Upon running and if size of object has changed, ** use SetExtent to change to new size. ** */ HRESULT ContainerLine_RunOleObject(LPCONTAINERLINE lpContainerLine) { LPLINE lpLine = (LPLINE)lpContainerLine; SIZEL sizelNew; HRESULT hrErr; HCURSOR hPrevCursor;
if (! lpContainerLine) return NOERROR;
if (lpContainerLine->m_fGuardObj) { // object in process of creation--Fail to Run the object
return ResultFromScode(E_FAIL); }
if (lpContainerLine->m_lpOleObj && OleIsRunning(lpContainerLine->m_lpOleObj)) return NOERROR; // object already running
// this may take a while, put up hourglass cursor
hPrevCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); OLEDBG_BEGIN3("ContainerLine_RunOleObject\r\n")
if (! lpContainerLine->m_lpOleObj) { if (! ContainerLine_LoadOleObject(lpContainerLine)) return ResultFromScode(E_OUTOFMEMORY); // Error: couldn't load obj
}
OLEDBG_BEGIN2("OleRun called\r\n") hrErr = OleRun((LPUNKNOWN)lpContainerLine->m_lpOleObj); OLEDBG_END2
if (hrErr != NOERROR) { SetCursor(hPrevCursor); // restore original cursor
OleDbgOutHResult("OleRun returned", hrErr); OLEDBG_END3 return hrErr; }
if (lpContainerLine->m_fDoSetExtent) { /* OLE2NOTE: the OLE object was resized when it was not running
** and the object did not have the OLEMISC_RECOMPOSEONRESIZE ** bit set. if it had, the object would have been run ** immediately when it was resized. this flag indicates that ** the object does something other than simple scaling when ** it is resized. because the object is being run now, we ** will call IOleObject::SetExtent. */ lpContainerLine->m_fDoSetExtent = FALSE;
// the size stored in our Line includes the border around the object.
// we must subtract the border to get the size of the object itself.
sizelNew.cx = lpLine->m_nWidthInHimetric; sizelNew.cy = lpLine->m_nHeightInHimetric;
if ((sizelNew.cx != lpContainerLine->m_sizeInHimetric.cx) || (sizelNew.cy != lpContainerLine->m_sizeInHimetric.cy)) {
OLEDBG_BEGIN2("IOleObject::SetExtent called\r\n") lpContainerLine->m_lpOleObj->lpVtbl->SetExtent( lpContainerLine->m_lpOleObj, lpContainerLine->m_dwDrawAspect, (LPSIZEL)&sizelNew ); OLEDBG_END2 } }
SetCursor(hPrevCursor); // restore original cursor
OLEDBG_END3 return NOERROR;
}
/* ContainerLine_IsOleLink
** ----------------------- ** ** return TRUE if the ContainerLine has an OleLink. ** FALSE if the ContainerLine has an embedding */ BOOL ContainerLine_IsOleLink(LPCONTAINERLINE lpContainerLine) { if (!lpContainerLine) return FALSE;
return (lpContainerLine->m_dwLinkType != 0); }
/* ContainerLine_Draw
** ------------------ ** ** Draw a ContainerLine object on a DC. ** ** Parameters: ** hDC - DC to which the line will be drawn ** lpRect - the object rect in logical coordinates ** lpRectWBounds - bounding rect of the metafile underneath hDC ** (NULL if hDC is not a metafile DC) ** fHighlight - TRUE if line has selection highlight */ void ContainerLine_Draw( LPCONTAINERLINE lpContainerLine, HDC hDC, LPRECT lpRect, LPRECT lpRectWBounds, BOOL fHighlight ) { LPLINE lpLine = (LPLINE) lpContainerLine; HRESULT hrErr = NOERROR; RECTL rclHim; RECTL rclHimWBounds; RECT rcHim;
if (lpContainerLine->m_fGuardObj) { // object in process of creation--do NOT try to draw
return; }
/* if object is not already loaded, then load it now. objects are
** loaded lazily in this manner. */ if (! lpContainerLine->m_lpViewObj2) { if (! ContainerLine_LoadOleObject(lpContainerLine)) return; // Error: could not load object
}
if (lpRectWBounds) { rclHimWBounds.left = (long) lpRectWBounds->left; rclHimWBounds.bottom = (long) lpRectWBounds->bottom; rclHimWBounds.top = (long) lpRectWBounds->top; rclHimWBounds.right = (long) lpRectWBounds->right; }
/* construct bounds rectangle for the object.
** offset origin for object to correct tab indentation */ rclHim.left = (long) lpRect->left; rclHim.bottom = (long) lpRect->bottom; rclHim.top = (long) lpRect->top; rclHim.right = (long) lpRect->right;
rclHim.left += (long) ((LPLINE)lpContainerLine)->m_nTabWidthInHimetric; rclHim.right += (long) ((LPLINE)lpContainerLine)->m_nTabWidthInHimetric;
#if defined( INPLACE_CNTR )
/* OLE2NOTE: if the OLE object currently has a visible in-place
** window, then we do NOT want to draw on top of its window. ** this could interfere with the object's display. */ if ( !lpContainerLine->m_fIpVisible ) #endif
{ hrErr = lpContainerLine->m_lpViewObj2->lpVtbl->Draw( lpContainerLine->m_lpViewObj2, lpContainerLine->m_dwDrawAspect, -1, NULL, NULL, NULL, hDC, (LPRECTL)&rclHim, (lpRectWBounds ? (LPRECTL)&rclHimWBounds : NULL), NULL, 0 ); if (hrErr != NOERROR) OleDbgOutHResult("IViewObject::Draw returned", hrErr);
if (lpContainerLine->m_fObjWinOpen) { rcHim.left = (int) rclHim.left; rcHim.top = (int) rclHim.top; rcHim.right = (int) rclHim.right; rcHim.bottom = (int) rclHim.bottom;
/* OLE2NOTE: if the object servers window is Open (ie. not active
** in-place) then we must shade the object in our document to ** indicate to the user that the object is open elsewhere. */ OleUIDrawShading((LPRECT)&rcHim, hDC, OLEUI_SHADE_FULLRECT, 0); } }
/* if the object associated with the ContainerLine is an automatic
** link then try to connect it with its LinkSource if the ** LinkSource is already running. we do not want to force the ** LinkSource to run. ** ** OLE2NOTE: a sophistocated container will want to continually ** attempt to connect its automatic links. OLE does NOT ** automatically connect links when link sources become ** available. some containers will want to attempt to connect ** its links as part of idle time processing. another strategy ** is to attempt to connect an automatic link every time it is ** drawn on the screen. (this is the strategy used by this ** CntrOutl sample application.) */ if (lpContainerLine->m_dwLinkType == OLEUPDATE_ALWAYS) ContainerLine_BindLinkIfLinkSrcIsRunning(lpContainerLine);
return; }
void ContainerLine_DrawSelHilight( LPCONTAINERLINE lpContainerLine, HDC hDC, // MM_TEXT mode
LPRECT lprcPix, // listbox rect
UINT itemAction, UINT itemState ) { LPLINE lpLine = (LPLINE)lpContainerLine; RECT rcObj; DWORD dwFlags = OLEUI_HANDLES_INSIDE | OLEUI_HANDLES_USEINVERSE; int nHandleSize; LPCONTAINERDOC lpContainerDoc;
if (!lpContainerLine || !hDC || !lprcPix) return;
lpContainerDoc = lpContainerLine->m_lpDoc;
// Get size of OLE object
ContainerLine_GetOleObjectRectInPixels(lpContainerLine, (LPRECT)&rcObj);
nHandleSize = GetProfileInt("windows", "oleinplaceborderwidth", DEFAULT_HATCHBORDER_WIDTH) + 1;
OleUIDrawHandles((LPRECT)&rcObj, hDC, dwFlags, nHandleSize, TRUE); }
/* InvertDiffRect
** -------------- ** ** Paint the surrounding of the Obj rect black but within lprcPix ** (similar to the lprcPix minus lprcObj) */ static void InvertDiffRect(LPRECT lprcPix, LPRECT lprcObj, HDC hDC) { RECT rcBlack;
// draw black in all space outside of object's rectangle
rcBlack.top = lprcPix->top; rcBlack.bottom = lprcPix->bottom;
rcBlack.left = lprcPix->left + 1; rcBlack.right = lprcObj->left - 1; InvertRect(hDC, (LPRECT)&rcBlack);
rcBlack.left = lprcObj->right + 1; rcBlack.right = lprcPix->right - 1; InvertRect(hDC, (LPRECT)&rcBlack);
rcBlack.top = lprcPix->top; rcBlack.bottom = lprcPix->top + 1; rcBlack.left = lprcObj->left - 1; rcBlack.right = lprcObj->right + 1; InvertRect(hDC, (LPRECT)&rcBlack);
rcBlack.top = lprcPix->bottom; rcBlack.bottom = lprcPix->bottom - 1; rcBlack.left = lprcObj->left - 1; rcBlack.right = lprcObj->right + 1; InvertRect(hDC, (LPRECT)&rcBlack); }
/* Edit the ContainerLine line object.
** returns TRUE if line was changed ** FALSE if the line was NOT changed */ BOOL ContainerLine_Edit(LPCONTAINERLINE lpContainerLine, HWND hWndDoc,HDC hDC) { ContainerLine_DoVerb(lpContainerLine, OLEIVERB_PRIMARY, NULL, TRUE, TRUE);
/* assume object was NOT changed, if it was obj will send Changed
** or Saved notification. */ return FALSE; }
/* ContainerLine_SetHeightInHimetric
** --------------------------------- ** ** Set the height of a ContainerLine object. The widht will be changed ** to keep the aspect ratio */ void ContainerLine_SetHeightInHimetric(LPCONTAINERLINE lpContainerLine, int nHeight) { LPLINE lpLine = (LPLINE)lpContainerLine; SIZEL sizelOleObject; HRESULT hrErr;
if (!lpContainerLine) return;
if (lpContainerLine->m_fGuardObj) { // object in process of creation--Fail to set the Height
return; }
if (nHeight != -1) { BOOL fMustClose = FALSE; BOOL fMustRun = FALSE;
/* if object is not already loaded, then load it now. objects are
** loaded lazily in this manner. */ if (! lpContainerLine->m_lpOleObj) ContainerLine_LoadOleObject(lpContainerLine);
// the height argument specifies the desired height for the Line.
sizelOleObject.cy = nHeight;
// we will calculate the corresponding width for the object by
// maintaining the current aspect ratio of the object.
sizelOleObject.cx = (int)(sizelOleObject.cy * lpContainerLine->m_sizeInHimetric.cx / lpContainerLine->m_sizeInHimetric.cy);
/* OLE2NOTE: if the OLE object is already running then we can
** immediately call SetExtent. But, if the object is NOT ** currently running then we will check if the object ** indicates that it is normally recomposes itself on ** resizing. ie. that the object does not simply scale its ** display when it it resized. if so then we will force the ** object to run so that we can call IOleObject::SetExtent. ** SetExtent does not have any effect if the object is only ** loaded. if the object does NOT indicate that it ** recomposes on resize (OLEMISC_RECOMPOSEONRESIZE) then we ** will wait till the next time that the object is run to ** call SetExtent. we will store a flag in the ContainerLine ** to indicate that a SetExtent is necessary. It is ** necessary to persist this flag. */ if (! OleIsRunning(lpContainerLine->m_lpOleObj)) { DWORD dwStatus;
OLEDBG_BEGIN2("IOleObject::GetMiscStatus called\r\n") hrErr = lpContainerLine->m_lpOleObj->lpVtbl->GetMiscStatus( lpContainerLine->m_lpOleObj, lpContainerLine->m_dwDrawAspect, (LPDWORD)&dwStatus ); OLEDBG_END2 if (hrErr == NOERROR && (dwStatus & OLEMISC_RECOMPOSEONRESIZE)) { // force the object to run
ContainerLine_RunOleObject(lpContainerLine); fMustClose = TRUE; } else { /* the OLE object is NOT running and does NOT
** recompose on resize. simply scale the object now ** and do the SetExtent the next time the object is ** run. we set the Line to the new size even though ** the object's extents have not been changed. ** this has the result of scaling the object's ** display to the new size. */ lpContainerLine->m_fDoSetExtent = TRUE; ContainerLine_SetLineHeightFromObjectExtent( lpContainerLine, (LPSIZEL)&sizelOleObject); return; } }
OLEDBG_BEGIN2("IOleObject::SetExtent called\r\n") hrErr = lpContainerLine->m_lpOleObj->lpVtbl->SetExtent( lpContainerLine->m_lpOleObj, lpContainerLine->m_dwDrawAspect, (LPSIZEL)&sizelOleObject); OLEDBG_END2
if (hrErr != NOERROR) { /* OLE Object refuses to take on the new extents. Set the
** Line to the new size even though the object refused ** the new extents. this has the result of scaling the ** object's display to the new size. ** ** if the object HAD accepted the new extents, then it ** will send out an OnViewChange/OnDataChange ** notification. this results in our container receiving ** an OnViewChange notification; the line height will be ** reset when this notification is received. */ ContainerLine_SetLineHeightFromObjectExtent( lpContainerLine, (LPSIZEL)&sizelOleObject); }
if (fMustClose) ContainerLine_CloseOleObject( lpContainerLine, OLECLOSE_SAVEIFDIRTY); } else { /* Set the line to default height given the natural (unscaled)
** extents of the OLE object. */ ContainerLine_SetLineHeightFromObjectExtent( lpContainerLine,(LPSIZEL)&lpContainerLine->m_sizeInHimetric); }
}
/* ContainerLine_SetLineHeightFromObjectExtent
* * Purpose: * Calculate the corresponding line height from the OleObject size * Scale the line height to fit the limit if necessary * * Parameters: * lpsizelOleObject pointer to size of OLE Object * * Returns: * nil */ void ContainerLine_SetLineHeightFromObjectExtent( LPCONTAINERLINE lpContainerLine, LPSIZEL lpsizelOleObject ) { LPLINE lpLine = (LPLINE)lpContainerLine;
UINT uMaxObjectHeight = XformHeightInPixelsToHimetric(NULL, LISTBOX_HEIGHT_LIMIT);
if (!lpContainerLine || !lpsizelOleObject) return;
if (lpContainerLine->m_fGuardObj) { // object in process of creation--Fail to set the Height
return; }
lpLine->m_nWidthInHimetric = (int)lpsizelOleObject->cx; lpLine->m_nHeightInHimetric = (int)lpsizelOleObject->cy;
// Rescale the object if height is greater than the limit
if (lpLine->m_nHeightInHimetric > (UINT)uMaxObjectHeight) {
lpLine->m_nWidthInHimetric = (UINT) ((long)lpLine->m_nWidthInHimetric * (long)uMaxObjectHeight / (long)lpLine->m_nHeightInHimetric);
lpLine->m_nHeightInHimetric = uMaxObjectHeight; }
}
/* ContainerLine_SaveToStg
** ----------------------- ** Save a given ContainerLine and associated OLE object to an IStorage*. */ BOOL ContainerLine_SaveToStm( LPCONTAINERLINE lpContainerLine, LPSTREAM lpLLStm ) { CONTAINERLINERECORD_ONDISK objLineRecord; ULONG nWritten; HRESULT hrErr;
// Compilers should handle aligment correctly
lstrcpy(objLineRecord.m_szStgName, lpContainerLine->m_szStgName); objLineRecord.m_fMonikerAssigned = (USHORT) lpContainerLine->m_fMonikerAssigned; objLineRecord.m_dwDrawAspect = lpContainerLine->m_dwDrawAspect; objLineRecord.m_sizeInHimetric = lpContainerLine->m_sizeInHimetric; objLineRecord.m_dwLinkType = lpContainerLine->m_dwLinkType; objLineRecord.m_fDoSetExtent = (USHORT) lpContainerLine->m_fDoSetExtent;
/* write line record */ hrErr = lpLLStm->lpVtbl->Write( lpLLStm, (LPVOID)&objLineRecord, sizeof(objLineRecord), &nWritten );
if (hrErr != NOERROR) { OleDbgAssertSz(hrErr == NOERROR,"Could not write to LineList stream"); return FALSE; }
return TRUE; }
/* ContainerLine_SaveOleObjectToStg
** -------------------------------- ** Save the OLE object associated with the ContainerLine to an IStorage*. */ BOOL ContainerLine_SaveOleObjectToStg( LPCONTAINERLINE lpContainerLine, LPSTORAGE lpSrcStg, LPSTORAGE lpDestStg, BOOL fRemember ) { HRESULT hrErr; SCODE sc = S_OK; BOOL fStatus; BOOL fSameAsLoad = (lpSrcStg==lpDestStg ? TRUE : FALSE); LPSTORAGE lpObjDestStg;
if (lpContainerLine->m_fGuardObj) { // object in process of creation--Fail to save
return FALSE; }
if (! lpContainerLine->m_lpOleObj) {
/*****************************************************************
** CASE 1: object is NOT loaded. *****************************************************************/
if (fSameAsLoad) { /*************************************************************
** CASE 1A: we are saving to the current storage. because ** the object is not loaded, it is up-to-date ** (ie. nothing to do). *************************************************************/
;
} else { /*************************************************************
** CASE 1B: we are saving to a new storage. because ** the object is not loaded, we can simply copy the ** object's current storage to the new storage. *************************************************************/
/* if current object storage is not already open, then open it */ if (! lpContainerLine->m_lpStg) { lpContainerLine->m_lpStg = OleStdOpenChildStorage( lpSrcStg, lpContainerLine->m_szStgName, STGM_READWRITE ); if (lpContainerLine->m_lpStg == NULL) { #if defined( _DEBUG )
OleDbgAssertSz( lpContainerLine->m_lpStg != NULL, "Error opening child stg" ); #endif
return FALSE; } }
/* Create a child storage inside the destination storage. */ lpObjDestStg = OleStdCreateChildStorage( lpDestStg, lpContainerLine->m_szStgName );
if (lpObjDestStg == NULL) { #if defined( _DEBUG )
OleDbgAssertSz( lpObjDestStg != NULL, "Could not create obj storage!" ); #endif
return FALSE; }
hrErr = lpContainerLine->m_lpStg->lpVtbl->CopyTo( lpContainerLine->m_lpStg, 0, NULL, NULL, lpObjDestStg ); // REVIEW: should we handle error here?
fStatus = OleStdCommitStorage(lpObjDestStg);
/* if we are supposed to remember this storage as the new
** storage for the object, then release the old one and ** save the new one. else, throw away the new one. */ if (fRemember) { OleStdVerifyRelease( (LPUNKNOWN)lpContainerLine->m_lpStg, "Original object stg not released" ); lpContainerLine->m_lpStg = lpObjDestStg; } else { OleStdVerifyRelease( (LPUNKNOWN)lpObjDestStg, "Copied object stg not released" ); } }
} else {
/*****************************************************************
** CASE 2: object IS loaded. *****************************************************************/
if (fSameAsLoad) { /*************************************************************
** CASE 2A: we are saving to the current storage. if the object ** is not dirty, then the current storage is up-to-date ** (ie. nothing to do). *************************************************************/
LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg; OleDbgAssert(lpPersistStg);
hrErr = lpPersistStg->lpVtbl->IsDirty(lpPersistStg);
/* OLE2NOTE: we will only accept an explicit "no i
** am NOT dirty statement" (ie. S_FALSE) as an ** indication that the object is clean. eg. if ** the object returns E_NOTIMPL we must ** interpret it as the object IS dirty. */ if (GetScode(hrErr) != S_FALSE) {
/* OLE object IS dirty */
OLEDBG_BEGIN2("OleSave called\r\n") hrErr = OleSave( lpPersistStg, lpContainerLine->m_lpStg, fSameAsLoad); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: OleSave returned", hrErr); sc = GetScode(hrErr); }
// OLE2NOTE: if OleSave fails, SaveCompleted must be called.
OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n") hrErr=lpPersistStg->lpVtbl->SaveCompleted(lpPersistStg,NULL); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr); if (sc == S_OK) sc = GetScode(hrErr); }
if (sc != S_OK) return FALSE; }
} else { /*************************************************************
** CASE 2B: we are saving to a new storage. we must ** tell the object to save into the new storage. *************************************************************/
LPPERSISTSTORAGE lpPersistStg = lpContainerLine->m_lpPersistStg;
if (! lpPersistStg) return FALSE;
/* Create a child storage inside the destination storage. */ lpObjDestStg = OleStdCreateChildStorage( lpDestStg, lpContainerLine->m_szStgName );
if (lpObjDestStg == NULL) { #if defined( _DEBUG )
OleDbgAssertSz( lpObjDestStg != NULL, "Could not create object storage!" ); #endif
return FALSE; }
OLEDBG_BEGIN2("OleSave called\r\n") hrErr = OleSave(lpPersistStg, lpObjDestStg, fSameAsLoad); OLEDBG_END2
// OLE2NOTE: even if OleSave fails, must still call SaveCompleted
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: OleSave returned", hrErr); sc = GetScode(hrErr); }
/* OLE2NOTE: a root level container should immediately
** call IPersistStorage::SaveCompleted after calling ** OleSave. a nested level container should not call ** SaveCompleted now, but must wait until SaveCompleted ** is call on it by its container. since our container ** is not a container/server, then we always call ** SaveComplete here. ** ** if this is a SaveAs operation, then we need to pass ** the lpStg back in SaveCompleted to inform the object ** of its new storage that it may hold on to. if this is ** a Save or a SaveCopyAs operation, then we simply pass ** NULL in SaveCompleted; the object can continue to hold ** its current storage. if an error occurs during the ** OleSave call we must still call SaveCompleted but we ** must pass NULL. */ OLEDBG_BEGIN2("IPersistStorage::SaveCompleted called\r\n") hrErr = lpPersistStg->lpVtbl->SaveCompleted( lpPersistStg, ((FAILED(sc) || !fRemember) ? NULL : lpObjDestStg) ); OLEDBG_END2
if (hrErr != NOERROR) { OleDbgOutHResult("WARNING: SaveCompleted returned",hrErr); if (sc == S_OK) sc = GetScode(hrErr); }
if (sc != S_OK) { OleStdVerifyRelease( (LPUNKNOWN)lpObjDestStg, "Copied object stg not released" ); return FALSE; }
/* if we are supposed to remember this storage as the new
** storage for the object, then release the old one and ** save the new one. else, throw away the new one. */ if (fRemember) { OleStdVerifyRelease( (LPUNKNOWN)lpContainerLine->m_lpStg, "Original object stg not released" ); lpContainerLine->m_lpStg = lpObjDestStg; } else { OleStdVerifyRelease( (LPUNKNOWN)lpObjDestStg, "Copied object stg not released" ); } } }
/* OLE2NOTE: after saving an OLE object it is possible that it sent
** an OnViewChange notification because it had been modified. in ** this situation it is possible that the extents of the object ** have changed. if so we want to relayout the space for the ** object immediately so that the extent information saved with ** the ContainerLine match the data saved with the OLE object ** itself. */ if (lpContainerLine->m_fDoGetExtent) { BOOL fSizeChanged = ContainerLine_UpdateExtent(lpContainerLine, NULL); #if defined( INPLACE_CNTR )
/* if the extents of this ContainerLine have changed, then we
** need to reset the fDoGetExtent flag to TRUE so that later ** when ContainerDoc_UpdateExtentOfAllOleObjects is called ** (when the WM_U_UPDATEOBJECTEXTENT message is processed), ** it is recognized that the extents of this line have ** changed. if any line changes size, then any in-place ** active object below this line must be told to update the ** position of their windows (via SetObjectRects -- see ** ContainerDoc_UpdateInPlaceObjectRects function). */ lpContainerLine->m_fDoGetExtent = fSizeChanged; #endif
}
return TRUE; }
/* ContainerLine_LoadFromStg
** ------------------------- ** Create a ContainerLine object and initialize it with data that ** was previously writen to an IStorage*. this function does not ** immediately OleLoad the associated OLE object, only the data of ** the ContainerLine object itself is loaded from the IStorage*. */ LPLINE ContainerLine_LoadFromStg( LPSTORAGE lpSrcStg, LPSTREAM lpLLStm, LPOUTLINEDOC lpDestDoc ) { HDC hDC; LPLINELIST lpDestLL = &lpDestDoc->m_LineList; ULONG nRead; HRESULT hrErr; LPCONTAINERLINE lpContainerLine; CONTAINERLINERECORD_ONDISK objLineRecord;
lpContainerLine=(LPCONTAINERLINE) New((DWORD)sizeof(CONTAINERLINE)); if (lpContainerLine == NULL) { OleDbgAssertSz(lpContainerLine!=NULL,"Error allocating ContainerLine"); return NULL; }
hDC = LineList_GetDC(lpDestLL); ContainerLine_Init(lpContainerLine, 0, hDC); LineList_ReleaseDC(lpDestLL, hDC);
/* OLE2NOTE: In order to have a stable ContainerLine object we must
** AddRef the object's refcnt. this will be later released when ** the ContainerLine is deleted. */ ContainerLine_AddRef(lpContainerLine);
lpContainerLine->m_lpDoc = (LPCONTAINERDOC) lpDestDoc;
/* read line record */ hrErr = lpLLStm->lpVtbl->Read( lpLLStm, (LPVOID)&objLineRecord, sizeof(objLineRecord), &nRead );
if (hrErr != NOERROR) { OleDbgAssertSz(hrErr==NOERROR, "Could not read from LineList stream"); goto error; }
// Compilers should handle aligment correctly
lstrcpy(lpContainerLine->m_szStgName, objLineRecord.m_szStgName); lpContainerLine->m_fMonikerAssigned = (BOOL) objLineRecord.m_fMonikerAssigned; lpContainerLine->m_dwDrawAspect = objLineRecord.m_dwDrawAspect; lpContainerLine->m_sizeInHimetric = objLineRecord.m_sizeInHimetric; lpContainerLine->m_dwLinkType = objLineRecord.m_dwLinkType; lpContainerLine->m_fDoSetExtent = (BOOL) objLineRecord.m_fDoSetExtent;
return (LPLINE)lpContainerLine;
error: // destroy partially created ContainerLine
if (lpContainerLine) ContainerLine_Delete(lpContainerLine); return NULL; }
/* ContainerLine_GetTextLen
* ------------------------ * * Return length of the string representation of the ContainerLine * (not considering the tab level). we will use the following as the * string representation of a ContainerLine: * "<" + user type name of OLE object + ">" * eg: * <Microsoft Excel Worksheet> */ int ContainerLine_GetTextLen(LPCONTAINERLINE lpContainerLine) { LPSTR lpszUserType = NULL; HRESULT hrErr; int nLen; BOOL fIsLink = ContainerLine_IsOleLink(lpContainerLine);
/* if object is not already loaded, then load it now. objects are
** loaded lazily in this manner. */ if (! lpContainerLine->m_lpOleObj) ContainerLine_LoadOleObject(lpContainerLine);
OLEDBG_BEGIN2("IOleObject::GetUserType called\r\n")
hrErr = CallIOleObjectGetUserTypeA( lpContainerLine->m_lpOleObj, USERCLASSTYPE_FULL, &lpszUserType );
OLEDBG_END2
if (hrErr != NOERROR) { // user type is NOT available
nLen = sizeof(UNKNOWN_OLEOBJ_TYPE) + 2; // allow space for '<' + '>'
nLen += lstrlen((LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT)) + 1; } else { nLen = lstrlen(lpszUserType) + 2; // allow space for '<' + '>'
nLen += lstrlen((LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT)) + 1;
/* OLE2NOTE: we must free the string that was allocated by the
** IOleObject::GetUserType method. */ OleStdFreeString(lpszUserType, NULL); }
return nLen; }
/* ContainerLine_GetTextData
* ------------------------- * * Return the string representation of the ContainerLine * (not considering the tab level). we will use the following as the * string representation of a ContainerLine: * "<" + user type name of OLE object + ">" * eg: * <Microsoft Excel Worksheet> */ void ContainerLine_GetTextData(LPCONTAINERLINE lpContainerLine, LPSTR lpszBuf) { LPSTR lpszUserType = NULL; BOOL fIsLink = ContainerLine_IsOleLink(lpContainerLine); HRESULT hrErr;
/* if object is not already loaded, then load it now. objects are
** loaded lazily in this manner. */ if (! lpContainerLine->m_lpOleObj) ContainerLine_LoadOleObject(lpContainerLine);
hrErr = CallIOleObjectGetUserTypeA( lpContainerLine->m_lpOleObj, USERCLASSTYPE_FULL, &lpszUserType );
if (hrErr != NOERROR) { // user type is NOT available
wsprintf( lpszBuf, "<%s %s>", UNKNOWN_OLEOBJ_TYPE, (LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT) ); } else { wsprintf( lpszBuf, "<%s %s>", lpszUserType, (LPSTR)(fIsLink ? szOLELINK : szOLEOBJECT) );
/* OLE2NOTE: we must free the string that was allocated by the
** IOleObject::GetUserType method. */ OleStdFreeString(lpszUserType, NULL); } }
/* ContainerLine_GetOutlineData
* ---------------------------- * * Return the CF_OUTLINE format data for the ContainerLine. */ BOOL ContainerLine_GetOutlineData( LPCONTAINERLINE lpContainerLine, LPTEXTLINE lpBuf ) { LPLINE lpLine = (LPLINE)lpContainerLine; LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerLine->m_lpDoc)->m_LineList; HDC hDC; char szTmpBuf[MAXSTRLEN+1]; LPTEXTLINE lpTmpTextLine;
// Create a TextLine with the Text representation of the ContainerLine.
ContainerLine_GetTextData(lpContainerLine, (LPSTR)szTmpBuf);
hDC = LineList_GetDC(lpLL); lpTmpTextLine = TextLine_Create(hDC, lpLine->m_nTabLevel, szTmpBuf); LineList_ReleaseDC(lpLL, hDC);
TextLine_Copy(lpTmpTextLine, lpBuf);
// Delete the temporary TextLine
TextLine_Delete(lpTmpTextLine); return TRUE; }
/* ContainerLine_GetPosRect
** ----------------------- ** Get the PosRect in client coordinates for the OLE object's window. ** ** OLE2NOTE: the PosRect must take into account the scroll postion of ** the document window. */ void ContainerLine_GetPosRect( LPCONTAINERLINE lpContainerLine, LPRECT lprcPosRect ) { ContainerLine_GetOleObjectRectInPixels(lpContainerLine,lprcPosRect);
// shift rect for left margin
lprcPosRect->left += lpContainerLine->m_nHorizScrollShift; lprcPosRect->right += lpContainerLine->m_nHorizScrollShift; }
/* ContainerLine_GetOleObjectRectInPixels
** -------------------------------------- ** Get the extent of the OLE Object contained in the given Line in ** client coordinates after scaling. */ void ContainerLine_GetOleObjectRectInPixels(LPCONTAINERLINE lpContainerLine, LPRECT lprc) { LPOUTLINEDOC lpOutlineDoc; LPSCALEFACTOR lpscale; LPLINELIST lpLL; LPLINE lpLine; int nIndex; HDC hdcLL;
if (!lpContainerLine || !lprc) return;
lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc; lpscale = OutlineDoc_GetScaleFactor(lpOutlineDoc); lpLL = OutlineDoc_GetLineList(lpOutlineDoc); lpLine = (LPLINE)lpContainerLine; nIndex = LineList_GetLineIndex(lpLL, lpLine);
LineList_GetLineRect(lpLL, nIndex, lprc);
hdcLL = GetDC(lpLL->m_hWndListBox);
/* lprc is set to be size of Line Object (including the boundary) */ lprc->left += (int)( (long)XformWidthInHimetricToPixels(hdcLL, lpLine->m_nTabWidthInHimetric + LOWORD(OutlineDoc_GetMargin(lpOutlineDoc))) * lpscale->dwSxN / lpscale->dwSxD); lprc->right = (int)( lprc->left + (long) XformWidthInHimetricToPixels(hdcLL, lpLine->m_nWidthInHimetric) * lpscale->dwSxN / lpscale->dwSxD);
ReleaseDC(lpLL->m_hWndListBox, hdcLL); }
/* ContainerLine_GetOleObjectSizeInHimetric
** ---------------------------------------- ** Get the size of the OLE Object contained in the given Line */ void ContainerLine_GetOleObjectSizeInHimetric(LPCONTAINERLINE lpContainerLine, LPSIZEL lpsizel) { if (!lpContainerLine || !lpsizel) return;
*lpsizel = lpContainerLine->m_sizeInHimetric; }
/* ContainerLine_BindLinkIfLinkSrcIsRunning
** ---------------------------------------- ** Try to connect the OLE link object associated with the ** ContainerLine with its LinkSource if the LinkSource is already ** running and the link is an automatic link. we do not want to ** force the LinkSource to run. ** ** OLE2NOTE: a sophistocated container will want to continually ** attempt to connect its automatic links. OLE does NOT ** automatically connect links when link source become available. some ** containers will want to attempt to connect its links as part of ** idle time processing. another strategy is to attempt to connect ** an automatic link every time it is drawn on the screen. (this is ** the strategy used by this CntrOutl sample application.) */ void ContainerLine_BindLinkIfLinkSrcIsRunning(LPCONTAINERLINE lpContainerLine) { LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; HRESULT hrErr; BOOL fPrevEnable1; BOOL fPrevEnable2;
// if the link source is known to be un-bindable, then don't even try
if (lpContainerLine->m_fLinkUnavailable) return;
/* OLE2NOTE: we do not want to ever give the Busy/NotResponding
** dialogs when we are attempting to BindIfRunning to the link ** source. if the link source is currently busy, this could ** cause the Busy dialog to come up. even if the link source is ** busy, we do not want put up the busy dialog. thus we will ** disable the dialog and later re-enable them */ OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2);
OLEDBG_BEGIN2("IOleLink::BindIfRunning called\r\n") hrErr = lpContainerLine->m_lpOleLink->lpVtbl->BindIfRunning( lpContainerLine->m_lpOleLink); OLEDBG_END2
// re-enable the Busy/NotResponding dialogs
OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2); }
|