CNTRINPL.C

/************************************************************************* 
**
** OLE 2 Container Sample Code
**
** cntrinpl.c
**
** This file contains all interfaces, methods and related support
** functions for an In-Place Container application (aka. Visual
** Editing). The in-place Container application includes the following
** implementation objects:
**
** ContainerApp Object
** exposed interfaces:
** IOleInPlaceFrame
**
** ContainerDoc Object
** support functions only
** (ICntrOtl is an SDI app; it doesn't support a Doc level IOleUIWindow)
**
** ContainerLin Object
** exposed interfaces:
** IOleInPlaceSite
**
** (c) Copyright Microsoft Corp. 1992 - 1997 All Rights Reserved
**
*************************************************************************/

#include "outline.h"
#if defined( USE_STATUSBAR )
#include "status.h"
#endif

OLEDBGDATA

extern LPOUTLINEAPP g_lpApp;
extern BOOL g_fInsideOutContainer;
extern RECT g_rectNull;

/*************************************************************************
** ContainerApp::IOleInPlaceFrame interface implementation
*************************************************************************/

// IOleInPlaceFrame::QueryInterface
STDMETHODIMP CntrApp_IPFrame_QueryInterface(
LPOLEINPLACEFRAME lpThis,
REFIID riid,
LPVOID FAR* lplpvObj
)
{
SCODE sc = E_NOINTERFACE;
LPCONTAINERAPP lpContainerApp =
((struct COleInPlaceFrameImpl FAR*)lpThis)->lpContainerApp;

/* The object should not be able to access the other interfaces
** of our App object by doing QI on this interface.
*/
*lplpvObj = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IOleWindow) ||
IsEqualIID(riid, &IID_IOleInPlaceUIWindow) ||
IsEqualIID(riid, &IID_IOleInPlaceFrame)) {
OleDbgOut4("CntrApp_IPFrame_QueryInterface: IOleInPlaceFrame* RETURNED\r\n");
*lplpvObj = (LPVOID) &lpContainerApp->m_OleInPlaceFrame;
OleApp_AddRef((LPOLEAPP)lpContainerApp);
sc = S_OK;
}

OleDbgQueryInterfaceMethod(*lplpvObj);

return sc;
}


// IOleInPlaceFrame::AddRef
STDMETHODIMP_(ULONG) CntrApp_IPFrame_AddRef(LPOLEINPLACEFRAME lpThis)
{
OleDbgAddRefMethod(lpThis, "IOleInPlaceFrame");

return OleApp_AddRef((LPOLEAPP)g_lpApp);
}


// IOleInPlaceFrame::Release
STDMETHODIMP_(ULONG) CntrApp_IPFrame_Release(LPOLEINPLACEFRAME lpThis)
{
OleDbgReleaseMethod(lpThis, "IOleInPlaceFrame");

return OleApp_Release((LPOLEAPP)g_lpApp);
}


// IOleInPlaceFrame::GetWindow
STDMETHODIMP CntrApp_IPFrame_GetWindow(
LPOLEINPLACEFRAME lpThis,
HWND FAR* lphwnd
)
{
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

OLEDBG_BEGIN2("CntrApp_IPFrame_GetWindow\r\n")
*lphwnd = lpOutlineApp->m_hWndApp;
OLEDBG_END2
return NOERROR;
}


// IOleInPlaceFrame::ContextSensitiveHelp
STDMETHODIMP CntrApp_IPFrame_ContextSensitiveHelp(
LPOLEINPLACEFRAME lpThis,
BOOL fEnterMode
)
{
LPCONTAINERAPP lpContainerApp =
((struct COleInPlaceFrameImpl FAR*)lpThis)->lpContainerApp;

// artificial AddRef in case object is destroyed during call
CntrApp_IPFrame_AddRef(lpThis);

OleDbgOut("CntrApp_IPFrame_ContextSensitiveHelp\r\n");
/* NOTE: see context sensitive help technote (CSHELP.DOC)
** This method is called when F1 is pressed when a menu item is
** selected. We set the frame's m_fMenuMode flag here. later,
** in WM_COMMAND processing in the AppWndProc, if this flag is
** set then the command is NOT executed and help is given
** instead.
*/
lpContainerApp->m_fMenuHelpMode = fEnterMode;

// release artificial AddRef
CntrApp_IPFrame_Release(lpThis);

return NOERROR;
}


// IOleInPlaceFrame::GetBorder
STDMETHODIMP CntrApp_IPFrame_GetBorder(
LPOLEINPLACEFRAME lpThis,
LPRECT lprectBorder
)
{
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

// artificial AddRef in case object is destroyed during call
CntrApp_IPFrame_AddRef(lpThis);

OLEDBG_BEGIN2("CntrApp_IPFrame_GetBorder\r\n")

OutlineApp_GetFrameRect(lpOutlineApp, lprectBorder);

OLEDBG_END2
// release artificial AddRef
CntrApp_IPFrame_Release(lpThis);
return NOERROR;
}


// IOleInPlaceFrame::RequestBorderSpace
STDMETHODIMP CntrApp_IPFrame_RequestBorderSpace(
LPOLEINPLACEFRAME lpThis,
LPCBORDERWIDTHS lpWidths
)
{
#if defined( _DEBUG )
OleDbgOut2("CntrApp_IPFrame_RequestBorderSpace\r\n");

{
/* FOR DEBUGING PURPOSES ONLY -- we will fail to allow to an
** object to get any frame border space for frame tools if
** our own frame tools are poped up in the tool pallet. this
** is NOT recommended UI behavior but it allows us to test
** in the condition when the frame does not give border
** space for the object. an object in this situation must
** then either popup its tools in a floating pallet, do
** without the tools, or fail to in-place activate.
*/
LPCONTAINERAPP lpContainerApp =
((struct COleInPlaceFrameImpl FAR*)lpThis)->lpContainerApp;
LPFRAMETOOLS lpft = OutlineApp_GetFrameTools(
(LPOUTLINEAPP)lpContainerApp);

if (lpft->m_ButtonBar.m_nState == BARSTATE_POPUP &&
lpft->m_FormulaBar.m_nState == BARSTATE_POPUP) {
OleDbgOut3(
"CntrApp_IPFrame_RequestBorderSpace: allow NO SPACE\r\n");
return E_FAIL;
}
}
#endif // _DEBUG

/* NOTE: we allow the object to have as much border space as it
** wants.
*/
return NOERROR;
}


// IOleInPlaceFrame::SetBorderSpace
STDMETHODIMP CntrApp_IPFrame_SetBorderSpace(
LPOLEINPLACEFRAME lpThis,
LPCBORDERWIDTHS lpWidths
)
{
LPCONTAINERAPP lpContainerApp =
((struct COleInPlaceFrameImpl FAR*)lpThis)->lpContainerApp;

// artificial AddRef in case object is destroyed during call
CntrApp_IPFrame_AddRef(lpThis);

OLEDBG_BEGIN2("CntrApp_IPFrame_SetBorderSpace\r\n")

/* NOTE: this fMustResizeClientArea flag is used as part of our
** defensive programming for frame window resizing. when the
** frame window is resized,IOleInPlaceActiveObject::ResizeBorder
** is called the object should normally call back to renegotiate
** for frame-level tools space. if SetBorderSpace is called then
** our client area windows are properly resized. if the in-place
** active object does NOT call SetBorderSpace, then the
** container must take care to resize its client area windows
** itself (see ContainerDoc_FrameWindowResized)
*/
if (lpContainerApp->m_fMustResizeClientArea)
lpContainerApp->m_fMustResizeClientArea = FALSE;

if (lpWidths == NULL) {

/* NOTE: IOleInPlaceSite::SetBorderSpace(NULL) is called
** when the in-place active object does NOT want any tool
** space. in this situation the in-place container should
** put up its tools.
*/
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)lpContainerApp;
LPCONTAINERDOC lpContainerDoc;

lpContainerDoc =(LPCONTAINERDOC)OutlineApp_GetActiveDoc(lpOutlineApp);
ContainerDoc_AddFrameLevelTools(lpContainerDoc);
} else {

// NOTE: you could do validation of borderwidths here
#if defined( _DEBUG )
/* FOR DEBUGING PURPOSES ONLY -- we will fail to allow to an
** object to get any frame border space for frame tools if
** our own frame tools are poped up in the tool pallet. this
** is NOT recommended UI behavior but it allows us to test
** in the condition when the frame does not give border
** space for the object. an object in this situation must
** then either popup its tools in a floating pallet, do
** without the tools, or fail to in-place activate.
*/
LPFRAMETOOLS lpft = OutlineApp_GetFrameTools(
(LPOUTLINEAPP)lpContainerApp);

if ((lpft->m_ButtonBar.m_nState == BARSTATE_POPUP &&
lpft->m_FormulaBar.m_nState == BARSTATE_POPUP) &&
(lpWidths->top || lpWidths->bottom ||
lpWidths->left || lpWidths->right) ) {
OleDbgOut3("CntrApp_IPFrame_SetBorderSpace: allow NO SPACE\r\n");
OLEDBG_END2

OutlineApp_SetBorderSpace(
(LPOUTLINEAPP) lpContainerApp,
(LPBORDERWIDTHS)&g_rectNull
);
OLEDBG_END2
// release artificial AddRef
CntrApp_IPFrame_Release(lpThis);

return E_FAIL;
}
#endif // _DEBUG

OutlineApp_SetBorderSpace(
(LPOUTLINEAPP) lpContainerApp,
(LPBORDERWIDTHS)lpWidths
);
}
OLEDBG_END2
// release artificial AddRef
CntrApp_IPFrame_Release(lpThis);

return NOERROR;
}


// IOleInPlaceFrame::SetActiveObject
STDMETHODIMP CntrApp_IPFrame_SetActiveObject(
LPOLEINPLACEFRAME lpThis,
LPOLEINPLACEACTIVEOBJECT lpActiveObject,
LPCOLESTR lpszObjName
)
{
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
// artificial AddRef in case object is destroyed during call
CntrApp_IPFrame_AddRef(lpThis);

OLEDBG_BEGIN2("CntrApp_IPFrame_SetActiveObject\r\n")

lpContainerApp->m_hWndUIActiveObj = NULL;

if (lpContainerApp->m_lpIPActiveObj)
lpContainerApp->m_lpIPActiveObj->lpVtbl->Release(lpContainerApp->m_lpIPActiveObj);

if ((lpContainerApp->m_lpIPActiveObj = lpActiveObject) != NULL) {
lpContainerApp->m_lpIPActiveObj->lpVtbl->AddRef(
lpContainerApp->m_lpIPActiveObj);

OLEDBG_BEGIN2("IOleInPlaceActiveObject::GetWindow called\r\n")
lpActiveObject->lpVtbl->GetWindow(
lpActiveObject,
(HWND FAR*)&lpContainerApp->m_hWndUIActiveObj
);
OLEDBG_END2

/* NOTE: see comment for ContainerDoc_ForwardPaletteChangedMsg */
/* No need to do this if you don't allow object to own the palette */
OleApp_QueryNewPalette((LPOLEAPP)lpContainerApp);
}

/* NOTE: the new UI Guidelines recommend that in-place
** containers do NOT change their window titles when an object
** becomes in-place (UI) active.
*/

OLEDBG_END2
// release artificial AddRef
CntrApp_IPFrame_Release(lpThis);
return NOERROR;
}


// IOleInPlaceFrame::InsertMenus
STDMETHODIMP CntrApp_IPFrame_InsertMenus(
LPOLEINPLACEFRAME lpThis,
HMENU hMenu,
LPOLEMENUGROUPWIDTHS lpMenuWidths
)
{
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
BOOL fNoError = TRUE;

OLEDBG_BEGIN2("CntrApp_IPFrame_InsertMenus\r\n")

fNoError &= AppendMenu(hMenu, MF_POPUP, (UINT)lpContainerApp->m_hMenuFile,
"&File");
fNoError &= AppendMenu(hMenu, MF_POPUP, (UINT)lpContainerApp->m_hMenuView,
"O&utline");
fNoError &= AppendMenu(hMenu, MF_POPUP,(UINT)lpContainerApp->m_hMenuDebug,
"DbgI&Cntr");
lpMenuWidths->width[0] = 1;
lpMenuWidths->width[2] = 1;
lpMenuWidths->width[4] = 1;

OLEDBG_END2

return (fNoError ? NOERROR : E_FAIL);
}


// IOleInPlaceFrame::SetMenu
STDMETHODIMP CntrApp_IPFrame_SetMenu(
LPOLEINPLACEFRAME lpThis,
HMENU hMenuShared,
HOLEMENU hOleMenu,
HWND hwndActiveObject
)
{
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
HMENU hMenu;
HRESULT hrErr;

OLEDBG_BEGIN2("CntrApp_IPFrame_InsertMenus\r\n")


/* NOTE: either put up the shared menu (combined menu from
** in-place server and in-place container) or our container's
** normal menu as directed.
*/
if (hOleMenu && hMenuShared)
hMenu = hMenuShared;
else
hMenu = lpOutlineApp->m_hMenuApp;

/* NOTE: SDI apps put menu on frame by calling SetMenu.
** MDI apps would send WM_MDISETMENU message instead.
*/
SetMenu (lpOutlineApp->m_hWndApp, hMenu);
OLEDBG_BEGIN2("OleSetMenuDescriptor called\r\n")
hrErr = OleSetMenuDescriptor (hOleMenu, lpOutlineApp->m_hWndApp,
hwndActiveObject, NULL, NULL);
OLEDBG_END2

OLEDBG_END2
return hrErr;
}


// IOleInPlaceFrame::RemoveMenus
STDMETHODIMP CntrApp_IPFrame_RemoveMenus(
LPOLEINPLACEFRAME lpThis,
HMENU hMenu
)
{
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
BOOL fNoError = TRUE;

OLEDBG_BEGIN2("CntrApp_IPFrame_RemoveMenus\r\n")

/* Remove container group menus */
while (GetMenuItemCount(hMenu))
fNoError &= RemoveMenu(hMenu, 0, MF_BYPOSITION);

OleDbgAssert(fNoError == TRUE);

OLEDBG_END2

return (fNoError ? NOERROR : E_FAIL);
}


// IOleInPlaceFrame::SetStatusText
STDMETHODIMP CntrApp_IPFrame_SetStatusText(
LPOLEINPLACEFRAME lpThis,
LPCOLESTR lpszStatusText
)
{
#if defined( USE_STATUSBAR )
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
OLECHAR szMessageHold[128];
OleDbgOut2("CntrApp_IPFrame_SetStatusText\r\n");

/* NOTE: it is important to save a private copy of status text.
** lpszStatusText is only valid for the duration of this call.
*/
OLESTRCPY(szMessageHold, lpszStatusText);
OutlineApp_SetStatusText(lpOutlineApp, /*(LPOLESTR)*/szMessageHold);

return S_OK;
#else
return E_NOTIMPL;
#endif // USE_STATUSBAR
}


// IOleInPlaceFrame::EnableModeless
STDMETHODIMP CntrApp_IPFrame_EnableModeless(
LPOLEINPLACEFRAME lpThis,
BOOL fEnable
)
{
LPOLEAPP lpOleApp = (LPOLEAPP)
((struct COleInPlaceFrameImpl FAR*)lpThis)->lpContainerApp;
#if defined( _DEBUG )
if (fEnable)
OleDbgOut2("CntrApp_IPFrame_EnableModeless(TRUE)\r\n");
else
OleDbgOut2("CntrApp_IPFrame_EnableModeless(FALSE)\r\n");
#endif // _DEBUG

/* NOTE: this method is called when an object puts up a modal
** dialog. it tells the top-level in-place container to disable
** it modeless dialogs for the duration that the object is
** displaying a modal dialog.
**
** ICNTROTL does not use any modeless dialogs, thus we can
** ignore this method.
*/
return NOERROR;
}


// IOleInPlaceFrame::TranslateAccelerator
STDMETHODIMP CntrApp_IPFrame_TranslateAccelerator(
LPOLEINPLACEFRAME lpThis,
LPMSG lpmsg,
WORD wID
)
{
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
SCODE sc;

if (TranslateAccelerator (lpOutlineApp->m_hWndApp,
lpContainerApp->m_hAccelIPCntr, lpmsg))
sc = S_OK;

#if defined (MDI_VERSION)
else if (TranslateMDISysAccel(lpOutlineApp->m_hWndMDIClient, lpmsg))
sc = S_OK;
#endif // MDI_VERSION

else
sc = S_FALSE;

return sc;
}



/*************************************************************************
** ContainerDoc Support Functions
*************************************************************************/


/* ContainerDoc_UpdateInPlaceObjectRects
** -------------------------------------
** Update the PosRect and ClipRect of the currently in-place active
** object. if there is no object active in-place, then do nothing.
**
** NOTE: this function should be called when an action occurs
** that changes either the position of the object in the document
** (eg. changing document margins changes PosRect) or the clipRect
** changes (eg. resizing the document window changes the ClipRect).
*/
void ContainerDoc_UpdateInPlaceObjectRects(LPCONTAINERDOC lpContainerDoc, int nIndex)
{
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
int i;
LPLINE lpLine;
RECT rcClipRect;

if (g_fInsideOutContainer) {

if (lpContainerDoc->m_cIPActiveObjects) {

/* NOTE: (INSIDE-OUT CONTAINER) we must update the
** PosRect/ClipRect for all in-place active objects
** starting from line "nIndex".
*/
ContainerDoc_GetClipRect(lpContainerDoc, (LPRECT)&rcClipRect);

#if defined( _DEBUG )
OleDbgOutRect3(
"ContainerDoc_UpdateInPlaceObjectRects (ClipRect)",
(LPRECT)&rcClipRect
);
#endif
for (i = nIndex; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
ContainerLine_UpdateInPlaceObjectRects(
lpContainerLine, &rcClipRect);
}
}
}
}
else {
/* NOTE: (OUTSIDE-IN CONTAINER) if there is a currently
** UIActive object, we must inform it that the
** PosRect/ClipRect has now changed.
*/
LPCONTAINERLINE lpLastUIActiveLine =
lpContainerDoc->m_lpLastUIActiveLine;
if (lpLastUIActiveLine && lpLastUIActiveLine->m_fUIActive) {
ContainerDoc_GetClipRect(lpContainerDoc, (LPRECT)&rcClipRect);

OleDbgOutRect3("OutlineDoc_Resize (ClipRect)",(LPRECT)&rcClipRect);
ContainerLine_UpdateInPlaceObjectRects(
lpLastUIActiveLine, &rcClipRect);
}
}
}

/* ContainerDoc_IsUIDeactivateNeeded
** ---------------------------------
** Check if it is necessary to UIDeactivate an in-place active
** object upon a mouse LBUTTONDOWN event. The position of the button
** down click is given by "pt".
** If there is not currently an in-place active line, then
** UIDeactivate is NOT needed.
** If there is a current in-place active line, then check if the
** point position is outside of the object extents on the screen. If
** so, then the object should be UIDeactivated, otherwise not.
*/
BOOL ContainerDoc_IsUIDeactivateNeeded(
LPCONTAINERDOC lpContainerDoc,
POINT pt
)
{
LPCONTAINERLINE lpUIActiveLine=lpContainerDoc->m_lpLastUIActiveLine;
RECT rect;

if (! lpUIActiveLine || ! lpUIActiveLine->m_fUIActive)
return FALSE;

ContainerLine_GetPosRect(
lpUIActiveLine,
(LPRECT) &rect
);

if (! PtInRect((LPRECT) &rect, pt))
return TRUE;

return FALSE;
}


/* ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded
** -------------------------------------------------
** NOTE: this function ONLY applies for OUTSIDE-IN containers
**
** If there is a previous in-place active server still running and
** this server will not be needed to support the next OLE object
** about to be activated, then shut it down.
** in this way we manage a policy of having at most one in-place
** server running at a time. we do not imediately shut down the
** in-place server when the object is UIDeactivated because we want
** it to be fast if the server decides to re-activate the object
** in-place.
**
** shutting down the server is achieved by forcing the object to
** transition from running to loaded by calling IOleObject::Close.
*/
void ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded(
LPCONTAINERDOC lpContainerDoc,
LPCONTAINERLINE lpNextActiveLine
)
{
LPCONTAINERLINE lpLastIpActiveLine = lpContainerDoc->m_lpLastIpActiveLine;
BOOL fEnableServerShutDown = TRUE;
LPMONIKER lpmkLinkSrc;
LPMONIKER lpmkLastActiveObj;
LPMONIKER lpmkCommonPrefix;
LPOLELINK lpOleLink;
HRESULT hrErr;

/* NOTE: an inside-out style container can NOT use this scheme
** to shut down in-place servers. it would have to have a more
** sophistocated mechanism by which it keeps track of which
** objects are on screen and which were the last recently used.
*/
if (g_fInsideOutContainer)
return;


if (lpLastIpActiveLine != lpNextActiveLine) {
if (lpLastIpActiveLine) {

/* NOTE: if the object which is about to be activated is
** actually a link to the OLE object in last activated line,
** then we do NOT want to shut down the last activated
** server because it is about to be used. when activating a
** linked object, the source of the link gets activated.
*/
lpOleLink = (LPOLELINK)ContainerLine_GetOleObject(
lpNextActiveLine,
&IID_IOleLink
);
if (lpOleLink) {
OLEDBG_BEGIN2("IOleObject::GetSourceMoniker called\r\n")
lpOleLink->lpVtbl->GetSourceMoniker(
lpOleLink,
(LPMONIKER FAR*)&lpmkLinkSrc
);
OLEDBG_END2

if (lpmkLinkSrc) {
lpmkLastActiveObj = ContainerLine_GetFullMoniker(
lpLastIpActiveLine,
GETMONIKER_ONLYIFTHERE
);
if (lpmkLastActiveObj) {
hrErr = lpmkLinkSrc->lpVtbl->CommonPrefixWith(
lpmkLinkSrc,
lpmkLastActiveObj,
&lpmkCommonPrefix

);
if (hrErr == MK_S_HIM ||
hrErr == NOERROR ||
hrErr == MK_S_US) {
/* the link source IS to the object
** contained in the last activated
** line of the document; disable the
** attempt to shut down the last
** running in-place server.
*/
fEnableServerShutDown = FALSE;
}
if (lpmkCommonPrefix)
OleStdRelease((LPUNKNOWN)lpmkCommonPrefix);
OleStdRelease((LPUNKNOWN)lpmkLastActiveObj);
}
OleStdRelease((LPUNKNOWN)lpmkLinkSrc);
}
OleStdRelease((LPUNKNOWN)lpOleLink);
}

/* if it is OK to shut down the previous in-place server
** and one is still running, then shut it down. shutting
** down the server is accomplished by forcing the OLE
** object to close. this forces the object to transition
** from running to loaded. if the object is actually
** only loaded then this is a NOP.
*/
if (fEnableServerShutDown &&
lpLastIpActiveLine->m_fIpServerRunning) {

OleDbgOut1("@@@ previous in-place server SHUT DOWN\r\n");
ContainerLine_CloseOleObject(
lpLastIpActiveLine, OLECLOSE_SAVEIFDIRTY);

// we can now forget this last in-place active line.
lpContainerDoc->m_lpLastIpActiveLine = NULL;
}
}
}
}


/* ContainerDoc_GetUIActiveWindow
** ------------------------------
** If there is an UIActive object, then return its HWND.
*/
HWND ContainerDoc_GetUIActiveWindow(LPCONTAINERDOC lpContainerDoc)
{
return lpContainerDoc->m_hWndUIActiveObj;
}


/* ContainerDoc_GetClipRect
** ------------------------
** Get the ClipRect in client coordinates.
**
** NOTE: the ClipRect is defined as the maximum window rectangle
** that the in-place active object must be clipped to. this
** rectangle MUST be described in Client Coordinates of the window
** that is used as the Parent of the in-place active object's
** window. in our case, the LineList ListBox window is both the
** parent of the in-place active object AND defines precisely the
** clipping rectangle.
*/
void ContainerDoc_GetClipRect(
LPCONTAINERDOC lpContainerDoc,
LPRECT lprcClipRect
)
{
/* NOTE: the ClipRect can be used to ensure that the in-place
** server does not overwrite areas of the window that the
** container paints into but which should never be overwritten
** (eg. if an app were to paint row and col headings directly in
** the same window that is the parent of the in-place window.
** whenever the window size changes or gets scrolled, in-place
** active object must be informed of the new clip rect.
**
** normally an app would pass the rect returned from GetClientRect.
** but because CntrOutl uses separate windows for row/column
** headings, status line, formula/tool bars, etc. it is NOT
** necessary to pass a constrained clip rect. Windows standard
** window clipping will automatically take care of all clipping
** that is necessary. thus we can take a short cut of passing an
** "infinite" clip rect, and then we do NOT need to call
** IOleInPlaceObject::SetObjectRects when our document is scrolled.
*/

lprcClipRect->top = -32767;
lprcClipRect->left = -32767;
lprcClipRect->right = 32767;
lprcClipRect->bottom = 32767;
}


/* ContainerDoc_GetTopInPlaceFrame
** -------------------------------
** returns NON-AddRef'ed pointer to Top In-Place Frame interface
*/
LPOLEINPLACEFRAME ContainerDoc_GetTopInPlaceFrame(
LPCONTAINERDOC lpContainerDoc
)
{
#if defined( INPLACE_CNTRSVR )
return lpContainerDoc->m_lpTopIPFrame;
#else
return (LPOLEINPLACEFRAME)&((LPCONTAINERAPP)g_lpApp)->m_OleInPlaceFrame;
#endif
}

void ContainerDoc_GetSharedMenuHandles(
LPCONTAINERDOC lpContainerDoc,
HMENU FAR* lphSharedMenu,
HOLEMENU FAR* lphOleMenu
)
{
#if defined( INPLACE_CNTRSVR )
if (lpContainerDoc->m_DocType == DOCTYPE_EMEBEDDEDOBJECT) {
*lphSharedMenu = lpContainerDoc->m_hSharedMenu;
*lphOleMenu = lpContainerDoc->m_hOleMenu;
return;
}
#endif

*lphSharedMenu = NULL;
*lphOleMenu = NULL;
}


#if defined( USE_FRAMETOOLS )
void ContainerDoc_RemoveFrameLevelTools(LPCONTAINERDOC lpContainerDoc)
{
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
OleDbgAssert(lpOutlineDoc->m_lpFrameTools != NULL);

FrameTools_Enable(lpOutlineDoc->m_lpFrameTools, FALSE);
}
#endif


void ContainerDoc_AddFrameLevelUI(LPCONTAINERDOC lpContainerDoc)
{
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;
LPOLEINPLACEFRAME lpTopIPFrame = ContainerDoc_GetTopInPlaceFrame(
lpContainerDoc);
HMENU hSharedMenu; // combined obj/cntr menu
HOLEMENU hOleMenu; // returned by OleCreateMenuDesc.

ContainerDoc_GetSharedMenuHandles(
lpContainerDoc,
&hSharedMenu,
&hOleMenu
);

lpTopIPFrame->lpVtbl->SetMenu(
lpTopIPFrame,
hSharedMenu,
hOleMenu,
lpOutlineDoc->m_hWndDoc
);

/* NOTE: even if our app does NOT use FrameTools, we must still
** call IOleInPlaceFrame::SetBorderSpace.
*/
ContainerDoc_AddFrameLevelTools(lpContainerDoc);
}


void ContainerDoc_AddFrameLevelTools(LPCONTAINERDOC lpContainerDoc)
{
LPOLEINPLACEFRAME lpTopIPFrame = ContainerDoc_GetTopInPlaceFrame(
lpContainerDoc);
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerDoc;


OleDbgAssert(lpTopIPFrame != NULL);

#if defined( USE_FRAMETOOLS )

FrameTools_Enable(lpOutlineDoc->m_lpFrameTools, TRUE);
OutlineDoc_UpdateFrameToolButtons(lpOutlineDoc);

FrameTools_NegotiateForSpaceAndShow(
lpOutlineDoc->m_lpFrameTools,
NULL,
lpTopIPFrame
);

#else // ! USE_FRAMETOOLS

#if defined( INPLACE_CNTRSVR )
if (lpContainerDoc->m_DocType == DOCTYPE_EMBEDDEDOBJECT) {
/* this says i do not need space, so the top Frame should
** leave its tools behind
*/
OLEDBG_BEGIN2("IOleInPlaceFrame::SetBorderSpace(NULL) called\r\n")
lpTopIPFrame->lpVtbl->SetBorderSpace(lpTopIPFrame, NULL);
OLEDBG_END2
return;
}
#else // INPLACE_CNTR && ! USE_FRAMETOOLS

OLEDBG_BEGIN2("IOleInPlaceFrame::SetBorderSpace(0,0,0,0) called\r\n")
lpTopIPFrame->lpVtbl->SetBorderSpace(
lpTopIPFrame,
(LPCBORDERWIDTHS)&g_rectNull
);
OLEDBG_END2

#endif // INPLACE_CNTR && ! USE_FRAMETOOLS
#endif // ! USE_FRAMETOOLS

}


void ContainerDoc_FrameWindowResized(LPCONTAINERDOC lpContainerDoc)
{
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;


if (lpContainerApp->m_lpIPActiveObj) {
RECT rcFrameRect;

/* NOTE: this fMustResizeClientArea flag is used as part of
** our defensive programming for frame window resizing. when

**    the frame window is 
** resized, IOleInPlaceActiveObject::ResizeBorder is called
** the object should normally call back to renegotiate
** for frame-level tools space. if SetBorderSpace is called
** then our client area windows are properly resized.
** CntrApp_IPFrame_SetBorderSpace clears this flag. if the
** in-place active object does NOT call SetBorderSpace, then
** the container must take care to resize its client area
** windows itself.
*/
lpContainerApp->m_fMustResizeClientArea = TRUE;

OutlineApp_GetFrameRect(g_lpApp, (LPRECT)&rcFrameRect);

OLEDBG_BEGIN2("IOleInPlaceActiveObject::ResizeBorder called\r\n")
lpContainerApp->m_lpIPActiveObj->lpVtbl->ResizeBorder(
lpContainerApp->m_lpIPActiveObj,
(LPCRECT)&rcFrameRect,
(LPOLEINPLACEUIWINDOW)&lpContainerApp->m_OleInPlaceFrame,
TRUE /* fFrameWindow */
);
OLEDBG_END2

/* the object did NOT call IOleInPlaceUIWindow::SetBorderSpace
** therefore we must resize our client area windows ourself.
*/
if (lpContainerApp->m_fMustResizeClientArea) {
lpContainerApp->m_fMustResizeClientArea = FALSE;
OutlineApp_ResizeClientArea(g_lpApp);
}
}

#if defined( USE_FRAMETOOLS )
else {
ContainerDoc_AddFrameLevelTools(lpContainerDoc);
}
#endif
}


#if defined( INPLACE_CNTRSVR ) || defined( INPLACE_MDICNTR )

/* ContainerDoc_GetTopInPlaceDoc
** returns NON-AddRef'ed pointer to Top In-Place Doc interface
*/
LPOLEINPLACEUIWINDOW ContainerDoc_GetTopInPlaceDoc(
LPCONTAINERDOC lpContainerDoc
)
{
#if defined( INPLACE_CNTRSVR )
return lpContainerDoc->m_lpTopIPDoc;
#else
return (LPOLEINPLACEUIWINDOW)&lpContainerDoc->m_OleInPlaceDoc;
#endif
}


void ContainerDoc_RemoveDocLevelTools(LPCONTAINERDOC lpContainerDoc);
{
LPOLEINPLACEUIWINDOW lpTopIPDoc = ContainerDoc_GetTopInPlaceDoc(
lpContainerDoc);

if (lpTopIPDoc && lpContainerDoc->m_fMyToolsOnDoc) {
lpContainerDoc->m_fMyToolsOnDoc = FALSE;

// if we had doc tools we would HIDE them here;
// but NOT call SetBorderSpace(NULL)

}
}

void ContainerDoc_AddDocLevelTools(LPCONTAINERDOC lpContainerDoc)
{
LPOLEINPLACEUIWINDOW lpTopIPDoc = ContainerDoc_GetTopInPlaceDoc(
lpContainerDoc);

if (! lpTopIPDoc)
return;


#if defined( USE_DOCTOOLS )
if (lpTopIPDoc && ! lpContainerDoc->m_fMyToolsOnDoc) {

/* if we had doc tools we would negotiate for toolbar space at
** doc level and SHOW them.
*/

/* we do NOT have doc level tools, so we just call
** SetBorderSpace() to indicate to the top doc that
** our object does not need tool space.
*/

lpContainerDoc->m_fMyToolsOnDoc = TRUE;
return;
}
#else // ! USE_DOCTOOLS

#if defined( INPLACE_CNTRSVR )
if (lpContainerDoc->m_DocType == DOCTYPE_EMBEDDEDOBJECT) {
/* this says i do not need space, so the top doc should
** leave its tools behind
*/
lpTopIPDoc->lpVtbl->SetBorderSpace(lpTopIPDoc, NULL);
return;
}
#else
lpTopIPDoc->lpVtbl->SetBorderSpace(
lpTopIPDoc,
(LPCBORDERWIDTHS)&g_rectNull
);

#endif
#endif // ! USE_DOCTOOLS
}

#endif // INPLACE_CNTRSVR || INPLACE_MDICNTR


/* ContainerDoc_ContextSensitiveHelp
** ---------------------------------
** forward the ContextSensitiveHelp mode on to any in-place objects
** that currently have their window visible. this informs the
** objects whether to give help or take action on subsequent mouse
** clicks and menu commands. this function is called from our
** IOleInPlaceSite::ContextSensitiveHelp implementation.
**
** NOTE: see context sensitive help technote (CSHELP.DOC).
** This function is called when SHIFT-F1 context sensitive help is
** entered. the cursor should then change to a question mark
** cursor and the app should enter a modal state where the next
** mouse click does not perform its normal action but rather
** gives help corresponding to the location clicked. if the app
** does not implement a help system, it should at least eat the
** click and do nothing.
*/
void ContainerDoc_ContextSensitiveHelp(
LPCONTAINERDOC lpContainerDoc,
BOOL fEnterMode,
BOOL fInitiatedByObj
)
{
LPOLEDOC lpOleDoc = (LPOLEDOC)lpContainerDoc;
LPLINELIST lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
int i;
LPLINE lpLine;

lpOleDoc->m_fCSHelpMode = fEnterMode;

if (g_fInsideOutContainer) {

if (lpContainerDoc->m_cIPActiveObjects) {
for (i = 0; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
ContainerLine_ContextSensitiveHelp(
lpContainerLine, fEnterMode);
}
}
}
}
else if (! fInitiatedByObj) {
/* NOTE: (OUTSIDE-IN CONTAINER) if there is a currently
** UIActive object (ie. its window is visible), we must
** forward the ContextSensitiveHelp mode on to the
** object--assuming it was not the object that initiated the
** context sensitve help mode.
*/
LPCONTAINERLINE lpLastUIActiveLine =
lpContainerDoc->m_lpLastUIActiveLine;
if (lpLastUIActiveLine && lpLastUIActiveLine->m_fUIActive) {
ContainerLine_ContextSensitiveHelp(lpLastUIActiveLine,fEnterMode);
}
}
}

/* ContainerDoc_ForwardPaletteChangedMsg
** -------------------------------------
** forward the WM_PALETTECHANGED message (via SendMessage) to any
** in-place objects that currently have their window visible. this
** gives these objects the chance to select their palette as a
** BACKGROUND palette.
**
** SEE THE TECHNOTE FOR DETAILED INFORMATION ON PALETTE COORDINATION
**
** NOTE: For containers and objects to manage palettes properly
** (when objects are getting inplace edited) they should follow a
** set of rules.
**
** Rule 1: The Container can decide if it wants to own the palette or
** it wants to allow its UIActive object to own the palette.
** a) If the container wants to let its UIActive object own the
** palette then it should forward WM_QUERYNEWPALETTE to the object
** when it is received to the top frame window. also it should send
** WM_QUERYNEWPALETTE to the object in its
** IOleInPlaceFrame::SetActiveObject implementation. if the object
** is given ownership of the palette, then it owns the palette until
** it is UIDeactivated.
** b) If the container wants to own the palette it should NOT send
** WM_QUERYNEWPALETTE to the UIActive object.
**
** Rule 2: Whether the container wants to own the palette or not, it
** should forward the WM_PALETTECHANGED to the immediate child
** inplace active objects in its documents. if it is an inside-out
** style container then it must forward it to ALL objects that
** currently have their windows visible. When the object recives the
** WM_PALETTECHANGED message it must select its color palette as the
** background palette. When the objects are in loaded state they will be
** drawn by (OLE) by selecting their palettes as background palettes
** anyway.
**
** Note: IOleObject::SetColorScheme is not related to the palette
** issue.
*/
void ContainerDoc_ForwardPaletteChangedMsg(
LPCONTAINERDOC lpContainerDoc,
HWND hwndPalChg
)
{
LPLINELIST lpLL;
int i;
LPLINE lpLine;

if (!lpContainerDoc)
return;

lpLL = &((LPOUTLINEDOC)lpContainerDoc)->m_LineList;
if (g_fInsideOutContainer) {

if (lpContainerDoc->m_cIPActiveObjects) {
for (i = 0; i < lpLL->m_nNumLines; i++) {
lpLine=LineList_GetLine(lpLL, i);

if (lpLine && (Line_GetLineType(lpLine)==CONTAINERLINETYPE)) {
LPCONTAINERLINE lpContainerLine = (LPCONTAINERLINE)lpLine;
ContainerLine_ForwardPaletteChangedMsg(
lpContainerLine, hwndPalChg);
}
}
}
}
else {
/* NOTE: (OUTSIDE-IN CONTAINER) if there is a currently
** UIActive object (ie. its window is visible), we must
** forward it the WM_PALETTECHANGED message.
*/
LPCONTAINERLINE lpLastUIActiveLine =
lpContainerDoc->m_lpLastUIActiveLine;
if (lpLastUIActiveLine && lpLastUIActiveLine->m_fUIActive) {
ContainerLine_ForwardPaletteChangedMsg(
lpLastUIActiveLine, hwndPalChg);
}
}
}


/*************************************************************************
** ContainerLine Support Functions and Interfaces
*************************************************************************/


/* ContainerLine_UIDeactivate
** --------------------------
** tell the OLE object associated with the ContainerLine to
** UIDeactivate. this informs the in-place server to tear down
** the UI and window that it put up for the object. it will remove
** its active editor menus and any frame and object adornments
** (eg. toolbars, rulers, etc.).
*/
void ContainerLine_UIDeactivate(LPCONTAINERLINE lpContainerLine)
{
HRESULT hrErr;

if (!lpContainerLine || !lpContainerLine->m_fUIActive)
return;

OLEDBG_BEGIN2("IOleInPlaceObject::UIDeactivate called\r\n")
hrErr = lpContainerLine->m_lpOleIPObj->lpVtbl->UIDeactivate(
lpContainerLine->m_lpOleIPObj);
OLEDBG_END2

if (hrErr != NOERROR) {
OleDbgOutHResult("IOleInPlaceObject::UIDeactivate returned", hrErr);
}
}



/* ContainerLine_UpdateInPlaceObjectRects
** -------------------------------------
** Update the PosRect and ClipRect of the given line
** currently in-place active
** object. if there is no object active in-place, then do nothing.
**
** NOTE: this function should be called when an action occurs
** that changes either the position of the object in the document
** (eg. changing document margins changes PosRect) or the clipRect
** changes (eg. resizing the document window changes the ClipRect).
*/
void ContainerLine_UpdateInPlaceObjectRects(
LPCONTAINERLINE lpContainerLine,
LPRECT lprcClipRect
)
{
LPCONTAINERDOC lpContainerDoc = lpContainerLine->m_lpDoc;
RECT rcClipRect;
RECT rcPosRect;

if (! lpContainerLine->m_fIpVisible)
return;

if (! lprcClipRect) {
ContainerDoc_GetClipRect(lpContainerDoc, (LPRECT)&rcClipRect);
lprcClipRect = (LPRECT)&rcClipRect;
}

#if defined( _DEBUG )
OleDbgOutRect3(
"ContainerLine_UpdateInPlaceObjectRects (ClipRect)",
(LPRECT)&rcClipRect
);
#endif
ContainerLine_GetPosRect(lpContainerLine,(LPRECT)&rcPosRect);

#if defined( _DEBUG )
OleDbgOutRect3(
"ContainerLine_UpdateInPlaceObjectRects (PosRect)",(LPRECT)&rcPosRect);
#endif

OLEDBG_BEGIN2("IOleInPlaceObject::SetObjectRects called\r\n")
lpContainerLine->m_lpOleIPObj->lpVtbl->SetObjectRects(
lpContainerLine->m_lpOleIPObj,
(LPRECT)&rcPosRect,
lprcClipRect
);
OLEDBG_END2
}


/* ContainerLine_ContextSensitveHelp
** ---------------------------------
** forward the ContextSensitiveHelp mode on to the in-place object
** if it currently has its window visible. this informs the
** object whether to give help or take action on subsequent mouse
** clicks and menu commands.
**
** this function is called from ContainerDoc_ContextSensitiveHelp
** function as a result of a call to
** IOleInPlaceSite::ContextSensitiveHelp if the in-place container
** is operating as an in-side out container.
*/
void ContainerLine_ContextSensitiveHelp(
LPCONTAINERLINE lpContainerLine,
BOOL fEnterMode
)
{
if (! lpContainerLine->m_fIpVisible)
return;

OLEDBG_BEGIN2("IOleInPlaceObject::ContextSensitiveHelp called\r\n")
lpContainerLine->m_lpOleIPObj->lpVtbl->ContextSensitiveHelp(
lpContainerLine->m_lpOleIPObj, fEnterMode);
OLEDBG_END2
}


/* ContainerLine_ForwardPaletteChangedMsg
** --------------------------------------
** forward it the WM_PALETTECHANGED message (via SendMessage) to the
** in-place object if it currently has its window visible. this
** gives the object the chance to select its palette as a BACKGROUND
** palette if it doesn't own the palette--or as the
** foreground palette if it currently DOES own the palette.
**
** SEE THE TECHNOTE FOR DETAILED INFORMATION ON PALETTE COORDINATION
*/
void ContainerLine_ForwardPaletteChangedMsg(
LPCONTAINERLINE lpContainerLine,
HWND hwndPalChg
)
{
if (! lpContainerLine->m_fIpVisible)
return;

if (lpContainerLine->m_hWndIpObject)
{
SendMessage(
lpContainerLine->m_hWndIpObject,
WM_PALETTECHANGED,
(WPARAM)hwndPalChg,
0L
);
}
}



/*************************************************************************
** ContainerLine::IOleInPlaceSite interface implementation
*************************************************************************/

STDMETHODIMP CntrLine_IPSite_QueryInterface(
LPOLEINPLACESITE lpThis,
REFIID riid,
LPVOID FAR* lplpvObj
)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;

return ContainerLine_QueryInterface(lpContainerLine, riid, lplpvObj);
}


STDMETHODIMP_(ULONG) CntrLine_IPSite_AddRef(LPOLEINPLACESITE lpThis)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;

OleDbgAddRefMethod(lpThis, "IOleInPlaceSite");

return ContainerLine_AddRef(lpContainerLine);
}


STDMETHODIMP_(ULONG) CntrLine_IPSite_Release(LPOLEINPLACESITE lpThis)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;

OleDbgReleaseMethod(lpThis, "IOleInPlaceSite");

return ContainerLine_Release(lpContainerLine);
}


STDMETHODIMP CntrLine_IPSite_GetWindow(
LPOLEINPLACESITE lpThis,
HWND FAR* lphwnd
)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);

OleDbgOut2("CntrLine_IPSite_GetWindow\r\n");

*lphwnd = LineList_GetWindow(
&((LPOUTLINEDOC)lpContainerLine->m_lpDoc)->m_LineList);

// release artificial AddRef
CntrLine_IPSite_Release(lpThis);

return NOERROR;
}

// IOleInPlaceSite::ContextSensitiveHelp
STDMETHODIMP CntrLine_IPSite_ContextSensitiveHelp(
LPOLEINPLACESITE lpThis,
BOOL fEnterMode
)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;
LPOLEDOC lpOleDoc = (LPOLEDOC)lpContainerLine->m_lpDoc;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);

OleDbgOut2("CntrLine_IPSite_ContextSensitiveHelp\r\n");

/* NOTE: see context sensitive help technote (CSHELP.DOC).
** This method is called when SHIFT-F1 context sensitive help is
** entered. the cursor should then change to a question mark
** cursor and the app should enter a modal state where the next
** mouse click does not perform its normal action but rather
** gives help corresponding to the location clicked. if the app
** does not implement a help system, it should at least eat the
** click and do nothing.
**
** NOTE: the implementation here is specific to an SDI simple
** container. an MDI container or container/server application
** would have additional work to do (see the technote).
**
** NOTE: (INSIDE-OUT CONTAINER) if there are currently
** any in-place objects active with their windows visible
** (ie. fIpVisible), we must forward the
** ContextSensitiveHelp mode on to these objects.
*/
ContainerDoc_ContextSensitiveHelp(
lpContainerLine->m_lpDoc,fEnterMode,TRUE /*InitiatedByObj*/);

// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_CanInPlaceActivate(LPOLEINPLACESITE lpThis)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;
OleDbgOut2("CntrLine_IPSite_CanInPlaceActivate\r\n");

/* NOTE: the container can NOT allow in-place activation if it
** is currently displaying the object as an ICON
** (DVASPECT_ICON). it can ONLY do in-place activation if it is
** displaying the DVASPECT_CONTENT of the OLE object.
*/
if (lpContainerLine->m_dwDrawAspect == DVASPECT_CONTENT)
return NOERROR;
else
return S_FALSE;
}

STDMETHODIMP CntrLine_IPSite_OnInPlaceActivate(LPOLEINPLACESITE lpThis)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;
LPCONTAINERDOC lpContainerDoc = lpContainerLine->m_lpDoc;
SCODE sc = S_OK;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);
OLEDBG_BEGIN2("CntrLine_IPSite_OnInPlaceActivate\r\n");

/* NOTE: (OUTSIDE-IN CONTAINER) as a policy for managing
** running in-place servers, we will keep only 1 inplace server
** active at any given time. so when we start to inp-place activate
** 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. this is NOT a required policy. apps
** may choose to have a more sophisticated strategy. inside-out
** containers will HAVE to have a more sophisticated strategy,
** because they need (at a minimum) to keep all visible object
** servers running.
**
** if the in-place activation is the result of activating a
** linked object in another container, then we may arrive at
** this method while another object is currently active.
** normally, if the object is in-place activated by
** double-clicking or Edit.<verb> from our own container, then
** the previous in-place object would have been de-activated in
** ContainerLine_DoVerb method.
*/
if (!g_fInsideOutContainer) {
if (lpContainerDoc->m_lpLastIpActiveLine) {
ContainerDoc_ShutDownLastInPlaceServerIfNotNeeded(
lpContainerDoc, lpContainerLine);
}
lpContainerDoc->m_lpLastIpActiveLine = lpContainerLine;
}

/* NOTE: to avoid LRPC problems it is important to cache the
** IOleInPlaceObject* pointer and NOT to call QueryInterface
** each time it is needed.
*/
lpContainerLine->m_lpOleIPObj = (LPOLEINPLACEOBJECT)
ContainerLine_GetOleObject(lpContainerLine,&IID_IOleInPlaceObject);

if (! lpContainerLine->m_lpOleIPObj) {
#if defined( _DEBUG )
OleDbgAssertSz(
lpContainerLine->m_lpOleIPObj!=NULL,
"OLE object must support IOleInPlaceObject"
);
#endif
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return E_FAIL; // ERROR: refuse to in-place activate
}

lpContainerLine->m_fIpActive = TRUE;
lpContainerLine->m_fIpVisible = TRUE;

OLEDBG_BEGIN2("IOleInPlaceObject::GetWindow called\r\n")
lpContainerLine->m_lpOleIPObj->lpVtbl->GetWindow(
lpContainerLine->m_lpOleIPObj,
(HWND FAR*)&lpContainerLine->m_hWndIpObject
);
OLEDBG_END2

if (! lpContainerLine->m_fIpServerRunning) {
/* NOTE: 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.
** this lock gets unlocked in ContainerLine_CloseOleObject.
*/
lpContainerLine->m_fIpServerRunning = TRUE;

OLEDBG_BEGIN2("OleLockRunning(TRUE, 0) called\r\n")
OleLockRunning((LPUNKNOWN)lpContainerLine->m_lpOleIPObj, TRUE, 0);
OLEDBG_END2
}

lpContainerLine->m_lpDoc->m_cIPActiveObjects++;

OLEDBG_END2
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_OnUIActivate (LPOLEINPLACESITE lpThis)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;
LPCONTAINERDOC lpContainerDoc = lpContainerLine->m_lpDoc;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
LPCONTAINERLINE lpLastUIActiveLine = lpContainerDoc->m_lpLastUIActiveLine;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);

OLEDBG_BEGIN2("CntrLine_IPSite_OnUIActivate\r\n");

lpContainerLine->m_fUIActive = TRUE;
lpContainerDoc->m_fAddMyUI = FALSE;
lpContainerDoc->m_lpLastUIActiveLine = lpContainerLine;

if (g_fInsideOutContainer) {
/* NOTE: (INSIDE-OUT CONTAINER) an inside-out style
** container must UIDeactivate the previous UIActive object
** when a new object is going UIActive. since the inside-out
** objects have their own windows visible, it is possible
** that a click directly in an another server window will
** cause it to UIActivate. OnUIActivate is the containers
** notification that such has occured. it must then
** UIDeactivate the other because at most one object can be
** UIActive at a time.
*/
if (lpLastUIActiveLine && (lpLastUIActiveLine!=lpContainerLine)) {
ContainerLine_UIDeactivate(lpLastUIActiveLine);

// Make sure new UIActive window is on top of all others
SetWindowPos(
lpContainerLine->m_hWndIpObject,
HWND_TOPMOST,
0,0,0,0,
SWP_NOMOVE | SWP_NOSIZE
);

OLEDBG_END2
goto cleanup;
}
}

lpContainerDoc->m_hWndUIActiveObj = lpContainerLine->m_hWndIpObject;

#if defined( USE_FRAMETOOLS )
ContainerDoc_RemoveFrameLevelTools(lpContainerDoc);
#endif

#if defined( USE_DOCTOOLS )
ContainerDoc_RemoveDocLevelTools(lpContainerDoc);
#endif

OLEDBG_END2
cleanup:
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);

return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_GetWindowContext(
LPOLEINPLACESITE lpThis,
LPOLEINPLACEFRAME FAR* lplpFrame,
LPOLEINPLACEUIWINDOW FAR* lplpDoc,
LPRECT lprcPosRect,
LPRECT lprcClipRect,
LPOLEINPLACEFRAMEINFO lpFrameInfo
)
{
LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP)g_lpApp;
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);

OLEDBG_BEGIN2("CntrLine_IPSite_GetWindowContext\r\n")

/* NOTE: The server should fill in the "cb" field so that the
** container can tell what size structure the server is
** expecting. this enables this structure to be easily extended
** in future releases of OLE. the container should check this
** field so that it doesn't try to use fields that do not exist
** since the server may be using an old structure definition.
** Since this is the first release of OLE2.0, the structure is
** guaranteed to be at least large enough for the current
** definition of OLEINPLACEFRAMEINFO struct. thus we do NOT need
** to consider this an error if the server did not fill in this
** field correctly. this server may have trouble in the future,
** however, when the structure is extended.
*/
*lplpFrame = (LPOLEINPLACEFRAME)&lpContainerApp->m_OleInPlaceFrame;
(*lplpFrame)->lpVtbl->AddRef(*lplpFrame); // must return AddRef'ed ptr

// NOTE: an MDI app would have to provide *lplpDoc
*lplpDoc = NULL;

ContainerLine_GetPosRect(lpContainerLine, lprcPosRect);
ContainerDoc_GetClipRect(lpContainerLine->m_lpDoc, lprcClipRect);

OleDbgOutRect3("CntrLine_IPSite_GetWindowContext (PosRect)", lprcPosRect);
OleDbgOutRect3("CntrLine_IPSite_GetWindowContext (ClipRect)",lprcClipRect);
lpFrameInfo->hwndFrame = lpOutlineApp->m_hWndApp;

#if defined( MDI_VERSION )
lpFrameInfo->fMDIApp = TRUE;
#else
lpFrameInfo->fMDIApp = FALSE;
#endif

lpFrameInfo->haccel = lpContainerApp->m_hAccelIPCntr;
lpFrameInfo->cAccelEntries =
GetAccelItemCount(lpContainerApp->m_hAccelIPCntr);

OLEDBG_END2
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_Scroll(
LPOLEINPLACESITE lpThis,
SIZE scrollExtent
)
{
OleDbgOut2("CntrLine_IPSite_Scroll\r\n");
return E_FAIL;
}


STDMETHODIMP CntrLine_IPSite_OnUIDeactivate(
LPOLEINPLACESITE lpThis,
BOOL fUndoable
)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
LPCONTAINERAPP lpContainerApp = (LPCONTAINERAPP) g_lpApp;
LPLINELIST lpLL;
int nIndex;
MSG msg;
HRESULT hrErr;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);

OLEDBG_BEGIN2("CntrLine_IPSite_OnUIDeactivate\r\n")

lpContainerLine->m_fUIActive = FALSE;
lpContainerLine->m_fIpChangesUndoable = fUndoable;
lpContainerLine->m_lpDoc->m_hWndUIActiveObj = NULL;

if (lpContainerLine == lpContainerLine->m_lpDoc->m_lpLastUIActiveLine) {

lpContainerLine->m_lpDoc->m_lpLastUIActiveLine = NULL;

/* NOTE: here we look ahead if there is a DBLCLK sitting in our
** message queue. if so, it could result in in-place activating
** another object. we want to avoid placing our tools and
** repainting if immediately another object is going to do the
** same. SO, if there is a DBLCLK in the queue for this document
** we will only set the fAddMyUI flag to indicate that this work
** is still to be done. if another object actually in-place
** activates then this flag will be cleared in
** IOleInPlaceSite::OnUIActivate. if it does NOT get cleared,
** then at the end of processing the DBLCLK message in our
** OutlineDocWndProc we will put our tools back.
*/
if (! PeekMessage(&msg, lpOutlineDoc->m_hWndDoc,
WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK,
PM_NOREMOVE | PM_NOYIELD)) {

/* NOTE: You need to generate QueryNewPalette only if
** you own the top level frame (ie. you are a top-level
** inplace container).
*/

OleApp_QueryNewPalette((LPOLEAPP)g_lpApp);


#if defined( USE_DOCTOOLS )
ContainerDoc_AddDocLevelTools(lpContainerLine->m_lpDoc);
#endif

#if defined( USE_FRAMETOOLS )
ContainerDoc_AddFrameLevelUI(lpContainerLine->m_lpDoc);
#endif
} else {
lpContainerLine->m_lpDoc->m_fAddMyUI = TRUE;
}

/* NOTE: we should re-take focus. the in-place server window
** previously had the focus; this window has just been removed.
*/
SetFocus(OutlineDoc_GetWindow((LPOUTLINEDOC)lpContainerLine->m_lpDoc));

// force the line to redraw to remove in-place active hatch
lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);
LineList_ForceLineRedraw(lpLL, nIndex, TRUE);
}

#if defined( UNDOSUPPORTED )

/* NOTE: an outside-in style container that supports UNDO would
** call IOleObject::DoVerb(OLEIVERB_HIDE) to make the in-place
** object go invisible. when it wants the in-place active object
** to discard its undo state, it would call
** IOleInPlaceObject::InPlaceDeactivate when it wants the object
** to discard its undo state. there is no need for an outside-in
** style container to call
** IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE). if either the
** container or the object do not support UNDO, then the
** container might as well immediately call InPlaceDeactivate
** instead of calling DoVerb(HIDE).
**
** an inside-out style container that supports UNDO would simply
** UIDeactivate the object. it would call
** IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE) when it wants
** the object discard its undo state. it would call
** IOleInPlaceObject::InPlaceDeactivate if it wants the object
** to take down its window.
*/
if (! g_fInsideOutContainer || !lpContainerLine->m_fInsideOutObj) {

if (lpContainerLine->m_fIpChangesUndoable) {
ContainerLine_DoVerb(
lpContainerLine,OLEIVERB_HIDE,NULL,FALSE,FALSE);
} else {
lpContainerLine->m_lpOleIPObj->lpVtbl->InPlaceDeactivate(
lpContainerLine->m_lpOleIPObj);
}
lpContainerLine->m_fIpVisible = FALSE;
lpContainerLine->m_hWndIpObject = NULL;
}
#else

/* NOTE: an outside-in style container that does NOT support
** UNDO would immediately tell the UIDeactivated server (UI
** removed) to IOleInPlaceObject::InPlaceDeactivate.
**
** an inside-out style container that does NOT support UNDO
** would call IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE) to
** tell the object to discard its undo state. it would call
** IOleInPlaceObject::InPlaceDeactivate if it wants the object
** to take down its window.
*/

if (g_fInsideOutContainer) {

if (lpContainerLine->m_fInsideOutObj) {

if (lpContainerLine->m_fIpChangesUndoable) {
OLEDBG_BEGIN3("ContainerLine_DoVerb(OLEIVERB_DISCARDUNDOSTATE) called!\r\n")
ContainerLine_DoVerb(lpContainerLine,
OLEIVERB_DISCARDUNDOSTATE,NULL,FALSE,FALSE);
OLEDBG_END3
}

} else { // !fInsideOutObj

/* NOTE: (INSIDEOUT CONTAINER) if the object is not
** registered OLEMISC_ACTIVATEWHENVISIBLE, then we will
** make the object behave in an outside-in manner. since
** we do NOT deal with UNDO we can simply
** InPlaceDeactivate the object. it should NOT be
** allowed to leave its window visible when
** UIDeactivated.
*/
OLEDBG_BEGIN2("IOleInPlaceObject::InPlaceDeactivate called\r\n")
hrErr = lpContainerLine->m_lpOleIPObj->lpVtbl->InPlaceDeactivate(
lpContainerLine->m_lpOleIPObj);
OLEDBG_END2
if (hrErr != NOERROR) {
OleDbgOutHResult("IOleInPlaceObject::InPlaceDeactivate returned", hrErr);
}
}

} else {

/* NOTE: (OUTSIDE-IN CONTAINER) since we do NOT deal with
** UNDO we can simply InPlaceDeactivate the object. it
** should NOT be allowed to leave its window visible when
** UIDeactivated.
**
** NOTE: In-place servers must handle InPlaceDeactivate
** being called before its call to
** IOleInPlaceSite::OnUIDeactivate returns. in case there
** are misbehaving servers out there, one way to work around
** this problem is to call
** IOleObject::DoVerb(OLEIVERB_HIDE...) here.
*/
OLEDBG_BEGIN2("IOleInPlaceObject::InPlaceDeactivate called\r\n")
hrErr = lpContainerLine->m_lpOleIPObj->lpVtbl->InPlaceDeactivate(
lpContainerLine->m_lpOleIPObj);
OLEDBG_END2
if (hrErr != NOERROR) {
OleDbgOutHResult("IOleInPlaceObject::InPlaceDeactivate returned", hrErr);
}
}

#endif // ! UNDOSUPPORTED

OLEDBG_END2
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_OnInPlaceDeactivate(LPOLEINPLACESITE lpThis)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);
OLEDBG_BEGIN2("CntrLine_IPSite_OnInPlaceDeactivate\r\n");

lpContainerLine->m_fIpActive = FALSE;
lpContainerLine->m_fIpVisible = FALSE;
lpContainerLine->m_fIpChangesUndoable = FALSE;
lpContainerLine->m_hWndIpObject = NULL;

OleStdRelease((LPUNKNOWN) lpContainerLine->m_lpOleIPObj);
lpContainerLine->m_lpOleIPObj = NULL;
lpContainerLine->m_lpDoc->m_cIPActiveObjects--;

OLEDBG_END2
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_DiscardUndoState(LPOLEINPLACESITE lpThis)
{
OleDbgOut2("CntrLine_IPSite_DiscardUndoState\r\n");
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_DeactivateAndUndo(LPOLEINPLACESITE lpThis)
{
OleDbgOut2("CntrLine_IPSite_DeactivateAndUndo\r\n");
return NOERROR;
}


STDMETHODIMP CntrLine_IPSite_OnPosRectChange(
LPOLEINPLACESITE lpThis,
LPCRECT lprcPosRect
)
{
LPCONTAINERLINE lpContainerLine =
((struct COleInPlaceSiteImpl FAR*)lpThis)->lpContainerLine;
LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpContainerLine->m_lpDoc;
LPSCALEFACTOR lpscale = OutlineDoc_GetScaleFactor(lpOutlineDoc);
LPLINE lpLine = (LPLINE)lpContainerLine;
LPLINELIST lpLL;
int nIndex;
RECT rcClipRect;
RECT rcNewPosRect;
SIZEL sizelPix;
SIZEL sizelHim;
int nIPObjHeight = lprcPosRect->bottom - lprcPosRect->top;
int nIPObjWidth = lprcPosRect->right - lprcPosRect->left;

// artificial AddRef in case object is destroyed during call
CntrLine_IPSite_AddRef(lpThis);

OLEDBG_BEGIN2("CntrLine_IPSite_OnPosRectChange\r\n")
OleDbgOutRect3("CntrLine_IPSite_OnPosRectChange (PosRect --IN)", (LPRECT)lprcPosRect);

/* NOTE: if the in-place container does NOT impose any
** size contraints on its in-place active objects, then it may
** simply grant the size requested by the object by immediately
** calling IOleInPlaceObject::SetObjectRects with the
** lprcPosRect passed by the in-place object.
**
** Container Outline, however, imposes a size constraint on its
** embedded objects (see comment in ContainerLine_UpdateExtent),
** thus it is necessary to calculate the size that the in-place
** active object is allowed to be.
**
** Here we need to know the new extents of the in-place object.
** We can NOT directly ask the object via IOleObject::GetExtent
** because this method will retreive the extents of the last
** cached metafile. the cache has not yet been updated by this
** point. We can not reliably call IOleObject::GetExtent
** because, despite the fact that will be delegated to the
** object properly, some objects may not have reformated their
** object and computed the new extents at the time of calling
** OnPosRectChange.
**
** the best we can do to get the new extents of the object is
** to determine the scale factor that the object was operating at
** prior to the OnPosRect call and scale the new lprcPosRect
** using this scale factor back to HIMETRIC units.
*/
if (lpContainerLine->m_sizeInHimetric.cx > 0 &&
lpContainerLine->m_sizeInHimetric.cy > 0) {
sizelHim.cx = lpLine->m_nWidthInHimetric;
sizelHim.cy = lpLine->m_nHeightInHimetric;
XformSizeInHimetricToPixels(NULL, &sizelHim, &sizelPix);
sizelHim.cx = lpContainerLine->m_sizeInHimetric.cx *
nIPObjWidth / sizelPix.cx;
sizelHim.cy = lpContainerLine->m_sizeInHimetric.cy *
nIPObjHeight / sizelPix.cy;

// Convert size back to 100% zoom
sizelHim.cx = sizelHim.cx * lpscale->dwSxD / lpscale->dwSxN;
sizelHim.cy = sizelHim.cy * lpscale->dwSyD / lpscale->dwSyN;
} else {
sizelHim.cx = (long)DEFOBJWIDTH;
sizelHim.cy = (long)DEFOBJHEIGHT;
XformSizeInHimetricToPixels(NULL, &sizelHim, &sizelPix);
sizelHim.cx = sizelHim.cx * nIPObjWidth / sizelPix.cx;
sizelHim.cy = sizelHim.cy * nIPObjHeight / sizelPix.cy;
}

ContainerLine_UpdateExtent(lpContainerLine, &sizelHim);
ContainerLine_GetPosRect(lpContainerLine, (LPRECT)&rcNewPosRect);
ContainerDoc_GetClipRect(lpContainerLine->m_lpDoc, (LPRECT)&rcClipRect);

#if defined( _DEBUG )
OleDbgOutRect3("CntrLine_IPSite_OnPosRectChange (PosRect --OUT)",
(LPRECT)&rcNewPosRect);
OleDbgOutRect3("CntrLine_IPSite_OnPosRectChange (ClipRect--OUT)",
(LPRECT)&rcClipRect);
#endif
OLEDBG_BEGIN2("IOleInPlaceObject::SetObjectRects called\r\n")
lpContainerLine->m_lpOleIPObj->lpVtbl->SetObjectRects(
lpContainerLine->m_lpOleIPObj,
(LPRECT)&rcNewPosRect,
(LPRECT)&rcClipRect
);
OLEDBG_END2

/* NOTE: (INSIDEOUT CONTAINER) Because this object just changed
** size, this may cause other in-place active objects in the
** document to move. in ICNTROTL's case any object below this
** object would be affected. in this case it would be necessary
** to call SetObjectRects to each affected in-place active object.
*/
if (g_fInsideOutContainer) {
lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
nIndex = LineList_GetLineIndex(lpLL, (LPLINE)lpContainerLine);

ContainerDoc_UpdateInPlaceObjectRects(
lpContainerLine->m_lpDoc, nIndex);
}
OLEDBG_END2
// release artificial AddRef
CntrLine_IPSite_Release(lpThis);
return NOERROR;
}