3541 lines
106 KiB
C
3541 lines
106 KiB
C
|
/*************************************************************************
|
||
|
**
|
||
|
** 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);
|
||
|
}
|