/*************************************************************************
**
**    OLE 2 Server Sample Code
**
**    svrinpl.c
**
**    This file contains all interfaces, methods and related support
**    functions for an In-Place Object (Server) application (aka. Visual
**    Editing). The in-place Object application includes the following
**    implementation objects:
**
**    ServerDoc Object
**      exposed interfaces:
**          IOleInPlaceObject
**          IOleInPlaceActiveObject
**
**    ServerApp Object
**      exposed interfaces:
**          IUnknown
**
**    (c) Copyright Microsoft Corp. 1992 - 1996 All Rights Reserved
**
*************************************************************************/


#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP             g_lpApp;


/* NOTE: the object should compose a string that is used by
**    in-place containers to be used for the window titles. this string
**    is passed to the container application via
**    IOleInPlaceUIWindow::SetActiveObject. the string should have the
**    following form:
**          <application name> - <object short type name>
**    SDI containers can use the string directly to display in the
**    frame window title. the container would concatenate the string
**    " in <container doc name>".
**    an MDI container with the MDI child window maximized can do the
**    same as the SDI container. an MDI container with the MDI child
**    windows NOT maximized can look for the " - " in the string from
**    the object. the first part of the string (app name) would be put
**    as the frame window title; the second part would be composed with
**    " in <container doc name>" and used as the MDI child window
**    title.
*/

// REVIEW: should use string resource for messages
OLECHAR g_szIPObjectTitle[] = APPNAME OLESTR(" - ") SHORTUSERTYPENAME;

extern RECT g_rectNull;



/*************************************************************************
** ServerDoc::IOleInPlaceObject interface implementation
*************************************************************************/

// IOleInPlaceObject::QueryInterface method

STDMETHODIMP SvrDoc_IPObj_QueryInterface(
      LPOLEINPLACEOBJECT  lpThis,
      REFIID              riid,
      LPVOID FAR *        lplpvObj
)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   return OleDoc_QueryInterface((LPOLEDOC)lpServerDoc, riid, lplpvObj);
}


// IOleInPlaceObject::AddRef method

STDMETHODIMP_(ULONG) SvrDoc_IPObj_AddRef(LPOLEINPLACEOBJECT lpThis)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   OleDbgAddRefMethod(lpThis, "IOleInPlaceObject");

   return OleDoc_AddRef((LPOLEDOC)lpServerDoc);
}


// IOleInPlaceObject::Release method

STDMETHODIMP_(ULONG) SvrDoc_IPObj_Release(LPOLEINPLACEOBJECT lpThis)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   OleDbgReleaseMethod(lpThis, "IOleInPlaceObject");

   return OleDoc_Release((LPOLEDOC)lpServerDoc);
}


// IOleInPlaceObject::GetWindow method

STDMETHODIMP SvrDoc_IPObj_GetWindow(
      LPOLEINPLACEOBJECT  lpThis,
      HWND FAR*           lphwnd
)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   OLEDBG_BEGIN2("SvrDoc_IPObj_GetWindow\r\n")

   *lphwnd = ((LPOUTLINEDOC)lpServerDoc)->m_hWndDoc;

   OLEDBG_END2
   return S_OK;
}


// IOleInPlaceObject::ContextSensitiveHelp method

STDMETHODIMP SvrDoc_IPObj_ContextSensitiveHelp(
      LPOLEINPLACEOBJECT  lpThis,
      BOOL                fEnable
)
{
   LPOLEDOC lpOleDoc =
         (LPOLEDOC)((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   OleDbgOut2("SvrDoc_IPObj_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.
   */
   lpOleDoc->m_fCSHelpMode = fEnable;

   return S_OK;
}


// IOleInPlaceObject::InPlaceDeactivate method

STDMETHODIMP SvrDoc_IPObj_InPlaceDeactivate(LPOLEINPLACEOBJECT lpThis)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   HRESULT hrErr;

   // artificial AddRef in case object is deleted during call
   SvrDoc_IPObj_AddRef(lpThis);

   OLEDBG_BEGIN2("SvrDoc_IPObj_InPlaceDeactivate\r\n")

   hrErr = ServerDoc_DoInPlaceDeactivate(lpServerDoc);

   OLEDBG_END2

   // release artificial AddRef
   SvrDoc_IPObj_Release(lpThis);

   return hrErr;
}


// IOleInPlaceObject::UIDeactivate method

STDMETHODIMP SvrDoc_IPObj_UIDeactivate(LPOLEINPLACEOBJECT lpThis)
{
   LPSERVERDOC     lpServerDoc =
                  ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   LPSERVERAPP     lpServerApp = (LPSERVERAPP)g_lpApp;
   LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;
   LPLINELIST      lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpServerDoc)->m_LineList;
   HWND            hWndApp = OutlineApp_GetWindow(g_lpApp);

   OLEDBG_BEGIN2("SvrDoc_IPObj_UIDeactivate\r\n");

   if (!lpServerDoc->m_fUIActive) {
      OLEDBG_END2
      return NOERROR;
   }

   // artificial AddRef in case object is deleted during call
   SvrDoc_IPObj_AddRef(lpThis);

   lpServerDoc->m_fUIActive = FALSE;

   // Clip the hatch window to the size of pos rect so, that the object
   // adornments and hatch border will not be visible.
   ServerDoc_ResizeInPlaceWindow(lpServerDoc,
         (LPRECT)&(lpServerDoc->m_lpIPData->rcPosRect),
         (LPRECT)&(lpServerDoc->m_lpIPData->rcPosRect)
   );

   if (lpIPData->lpDoc)
      lpIPData->lpDoc->lpVtbl->SetActiveObject(lpIPData->lpDoc, NULL, NULL);

   if (lpIPData->lpFrame) {
      lpIPData->lpFrame->lpVtbl->SetActiveObject(
         lpIPData->lpFrame,
         NULL,
         NULL
      );
   }

#if defined( USE_FRAMETOOLS )
   /* NOTE: we must hide our frame tools here but NOT call
   **    IOleInPlaceFrame::SetBorderSpace(NULL) or SetMenu(NULL).
   **    we must hide our tools BEFORE calling
   **    IOleInPlaceSite::OnUIDeactivate. the container will put
   **    his menus and tools back when OnUIDeactivate is called.
   */
   ServerDoc_RemoveFrameLevelTools(lpServerDoc);
#endif

   OLEDBG_BEGIN2("IOleInPlaceSite::OnUIDeactivate called\r\n");
   lpIPData->lpSite->lpVtbl->OnUIDeactivate(lpIPData->lpSite, FALSE);
   OLEDBG_END2

   /* Reset to use our normal app's accelerator table */
   g_lpApp->m_hAccelApp = lpServerApp->m_hAccelBaseApp;
   g_lpApp->m_hAccel = lpServerApp->m_hAccelBaseApp;
   g_lpApp->m_hWndAccelTarget = hWndApp;

   OLEDBG_END2

#if !defined( SVR_INSIDEOUT )
   /* NOTE: an "outside-in" style in-place server would hide its
   **    window here. an "inside-out" style server leaves its window
   **    visible when it is UIDeactivated. it would only hide its
   **    window when InPlaceDeactivated. this app is an "inside-out"
   **    style server. it is recommended for most server to support
   **    inside-out behavior if possible.
   */
   ServerDoc_DoInPlaceHide(lpServerDoc);
#endif // INSIEDOUT
   // release artificial AddRef
   SvrDoc_IPObj_Release(lpThis);

   return NOERROR;
}


// IOleInPlaceObject::SetObjectRects method

STDMETHODIMP SvrDoc_IPObj_SetObjectRects(
      LPOLEINPLACEOBJECT  lpThis,
      LPCRECT             lprcPosRect,
      LPCRECT             lprcClipRect
)
{
   LPSERVERDOC  lpServerDoc =
               ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   LPINPLACEDATA lpIPData = lpServerDoc->m_lpIPData;
   LPLINELIST   lpLL = OutlineDoc_GetLineList((LPOUTLINEDOC)lpServerDoc);


   // artificial AddRef in case object is deleted during call
   SvrDoc_IPObj_AddRef(lpThis);

   OLEDBG_BEGIN2("SvrDoc_IPObj_SetObjectRects\r\n")

#if defined( _DEBUG )
   OleDbgOutRect3("SvrDoc_IPObj_SetObjectRects (PosRect)",
         (LPRECT)lprcPosRect);
   OleDbgOutRect3("SvrDoc_IPObj_SetObjectRects (ClipRect)",
         (LPRECT)lprcClipRect);
#endif
   // save the current PosRect and ClipRect
   lpIPData->rcPosRect = *lprcPosRect;
   lpIPData->rcClipRect = *lprcClipRect;

   if (! lpServerDoc->m_fUIActive) // hatch and adornaments must not be drawn
      lprcClipRect = lprcPosRect;

   ServerDoc_ResizeInPlaceWindow(
         lpServerDoc, (LPRECT)lprcPosRect, (LPRECT)lprcClipRect);

   OLEDBG_END2

   // release artificial AddRef
   SvrDoc_IPObj_Release(lpThis);

   return NOERROR;
}


// IOleInPlaceObject::ReactivateAndUndo method

STDMETHODIMP SvrDoc_IPObj_ReactivateAndUndo(LPOLEINPLACEOBJECT lpThis)
{
   OLEDBG_BEGIN2("SvrDoc_IPObj_ReactivateAndUndo\r\n")

   // We do not support support UNDO.

   /* REVIEW: for debugging purposes it would be useful to give a
   **    message box indicating that this method has been called.
   */

   OLEDBG_END2
   return NOERROR;
}


/*************************************************************************
** ServerDoc::IOleInPlaceActiveObject interface implementation
*************************************************************************/

// IOleInPlaceActiveObject::QueryInterface method

STDMETHODIMP SvrDoc_IPActiveObj_QueryInterface(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      REFIID                      riid,
      LPVOID FAR *                lplpvObj
)
{
   SCODE sc = E_NOINTERFACE;
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   /* The container should not be able to access the other interfaces
   ** of our object by doing QI on this interface.
   */

   *lplpvObj = NULL;
   if (IsEqualIID(riid, &IID_IUnknown) ||
      IsEqualIID(riid, &IID_IOleWindow) ||
      IsEqualIID(riid, &IID_IOleInPlaceActiveObject)) {
      OleDbgOut4("OleDoc_QueryInterface: IOleInPlaceActiveObject* RETURNED\r\n");

      *lplpvObj = lpThis;
      OleDoc_AddRef((LPOLEDOC)lpServerDoc);
      sc = NOERROR;
   }

   OleDbgQueryInterfaceMethod(*lplpvObj);

   return ResultFromScode(sc);
}


// IOleInPlaceActiveObject::AddRef method

STDMETHODIMP_(ULONG) SvrDoc_IPActiveObj_AddRef(
      LPOLEINPLACEACTIVEOBJECT lpThis
)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   OleDbgAddRefMethod(lpThis, "IOleInPlaceActiveObject");

   return OleDoc_AddRef((LPOLEDOC)lpServerDoc);
}


// IOleInPlaceActiveObject::Release method

STDMETHODIMP_(ULONG) SvrDoc_IPActiveObj_Release(
      LPOLEINPLACEACTIVEOBJECT lpThis
)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   OleDbgReleaseMethod(lpThis, "IOleInPlaceActiveObject");

   return OleDoc_Release((LPOLEDOC)lpServerDoc);
}


// IOleInPlaceActiveObject::GetWindow method

STDMETHODIMP SvrDoc_IPActiveObj_GetWindow(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      HWND FAR*                   lphwnd
)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;

   OLEDBG_BEGIN2("SvrDoc_IPActiveObj_GetWindow\r\n")

   *lphwnd = ((LPOUTLINEDOC)lpServerDoc)->m_hWndDoc;

   OLEDBG_END2
   return NOERROR;
}


// IOleInPlaceActiveObject::ContextSensitiveHelp method

STDMETHODIMP SvrDoc_IPActiveObj_ContextSensitiveHelp(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      BOOL                        fEnterMode
)
{
   LPSERVERDOC lpServerDoc =
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   OleDbgOut2("SvrDoc_IPActiveObj_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. this tells the in-place server application to give
   **    help rather than execute the next menu command. at a minimum,
   **    even if the in-place server application does not implement a
   **    help system, it should NOT execute the next command when
   **    fEnable==TRUE. We set the active object's m_fMenuMode flag here.
   **    later, in WM_COMMAND processing in the DocWndProc, if this
   **    flag is set then the command is NOT executed (and help could
   **    be given if we had a help system....but we don't.)
   */
   lpServerDoc->m_fMenuHelpMode = fEnterMode;

#if !defined( HACK )
   ((LPOLEDOC)lpServerDoc)->m_fCSHelpMode = fEnterMode;
#endif
   return NOERROR;
}


// IOleInPlaceActiveObject::TranslateAccelerator method

STDMETHODIMP SvrDoc_IPActiveObj_TranslateAccelerator(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      LPMSG                       lpmsg
)
{
   // This will never be called because this server is implemented as an EXE
   return NOERROR;
}


// IOleInPlaceActiveObject::OnFrameWindowActivate method

STDMETHODIMP SvrDoc_IPActiveObj_OnFrameWindowActivate(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      BOOL                        fActivate
)
{
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)
         ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   HWND hWndDoc = OutlineDoc_GetWindow(lpOutlineDoc);
#if defined( _DEBUG )
   if (fActivate)
      OleDbgOut2("SvrDoc_IPActiveObj_OnFrameWindowActivate(TRUE)\r\n");
   else
      OleDbgOut2("SvrDoc_IPActiveObj_OnFrameWindowActivate(FALSE)\r\n");
#endif  // _DEBUG

   /* NOTE: this is a notification of the container application's
   **    WM_ACTIVATEAPP status. some applications may find this
   **    important. we need to update the enable/disable status of our
   **    tool bar buttons.
   */

   // NOTE: We can't call OutlineDoc_UpdateFrameToolButtons
   //           right away which
   //           would generate some OLE calls and eventually
   //           WM_ACTIVATEAPP and a loop was formed. Therefore, we
   //           should delay the frame tool initialization until
   //           WM_ACTIVATEAPP is finished by posting a message
   //           to ourselves.

   /* Update enable/disable state of buttons in toolbar */
   if (fActivate)
      PostMessage(hWndDoc, WM_U_INITFRAMETOOLS, 0, 0L);

   return NOERROR;
}


// IOleInPlaceActiveObject::OnDocWindowActivate method

STDMETHODIMP SvrDoc_IPActiveObj_OnDocWindowActivate(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      BOOL                        fActivate
)
{
   LPSERVERDOC     lpServerDoc =
                  ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;
#if defined( _DEBUG )
   if (fActivate)
      OleDbgOut2("SvrDoc_IPActiveObj_OnDocWindowActivate(TRUE)\r\n");
   else
      OleDbgOut2("SvrDoc_IPActiveObj_OnDocWindowActivate(FALSE)\r\n");
#endif

   if (fActivate) {
      ServerDoc_AddFrameLevelUI(lpServerDoc);
   }
   else {
#if defined( USE_FRAMETOOLS )
      /* NOTE: we must NOT call IOleInPlaceFrame::SetBorderSpace(NULL)
      **    or SetMenu(NULL) here. we should simply hide our tools.
      */
      ServerDoc_RemoveFrameLevelTools(lpServerDoc);
#endif
   }

   OLEDBG_END2
   return NOERROR;
}


// IOleInPlaceActiveObject::ResizeBorder method

STDMETHODIMP SvrDoc_IPActiveObj_ResizeBorder(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      LPCRECT                     lprectBorder,
      LPOLEINPLACEUIWINDOW        lpIPUiWnd,
      BOOL                        fFrameWindow
)
{
   LPSERVERDOC lpServerDoc =
               ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;

   OLEDBG_BEGIN2("SvrDoc_IPActiveObj_ResizeBorder\r\n")


#if defined( USE_FRAMETOOLS )

   if (fFrameWindow) {
      FrameTools_NegotiateForSpaceAndShow(
            lpOutlineDoc->m_lpFrameTools,
            (LPRECT)lprectBorder,
            (LPOLEINPLACEFRAME)lpIPUiWnd
      );
   }

#endif

   OLEDBG_END2
   return NOERROR;
}


// IOleInPlaceActiveObject::EnableModeless method

STDMETHODIMP SvrDoc_IPActiveObj_EnableModeless(
      LPOLEINPLACEACTIVEOBJECT    lpThis,
      BOOL                        fEnable
)
{
#if defined( USE_FRAMETOOLS )
   LPSERVERDOC lpServerDoc =
            ((struct CDocOleObjectImpl FAR*)lpThis)->lpServerDoc;
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPFRAMETOOLS lptb;

   /* NOTE: we must enable/disable mouse and keyboard input to our
   **    floating tool palette
   */
   if (lpOutlineDoc) {
      lptb = lpOutlineDoc->m_lpFrameTools;
      if (lptb)
         FrameTools_EnableWindow(lptb, fEnable);
   }
#endif  // USE_FRAMETOOLS

#if defined( _DEBUG )
   if (fEnable)
      OleDbgOut2("SvrDoc_IPActiveObj_EnableModeless(TRUE)\r\n");
   else
      OleDbgOut2("SvrDoc_IPActiveObj_EnableModeless(FALSE)\r\n");
#endif  // _DEBUG

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


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


HRESULT ServerDoc_DoInPlaceActivate(
      LPSERVERDOC     lpServerDoc,
      LONG            lVerb,
      LPMSG           lpmsg,
      LPOLECLIENTSITE lpActiveSite
)
{
   LPOUTLINEAPP            lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPSERVERAPP             lpServerApp = (LPSERVERAPP)g_lpApp;
   LPOLEAPP                lpOleApp = (LPOLEAPP)g_lpApp;
   SCODE                   sc = E_FAIL;
   RECT                    rcPos;
   RECT                    rcClip;
   LPINPLACEDATA           lpIPData = lpServerDoc->m_lpIPData;
   LPOUTLINEDOC            lpOutlineDoc=(LPOUTLINEDOC)lpServerDoc;
   HWND                    hWndDoc = lpOutlineDoc->m_hWndDoc;
   HWND                    hWndHatch = lpServerDoc->m_hWndHatch;
   HRESULT                 hrErr;
   LPLINELIST              lpLL=(LPLINELIST)&lpOutlineDoc->m_LineList;
   LPOLEINPLACESITE    lpIPSite = NULL;

   /* NOTE: lpActiveSite should be used only for InPlace PLAYing.
   **    This app does not do inplace PLAYing, so it never uses
   **    lpActiveSite.
   */

   /* InPlace activation can only be done if the ClientSite is non-NULL. */
   if (! lpServerDoc->m_lpOleClientSite)
      return NOERROR;

   if (! lpServerDoc->m_fInPlaceActive) {

      // if the object is in open mode then we do not want to do inplace
      // activation.
      if (IsWindowVisible(lpOutlineDoc->m_hWndDoc))
      {
         return NOERROR;
      }

      lpIPSite = (LPOLEINPLACESITE)OleStdQueryInterface(
            (LPUNKNOWN)lpServerDoc->m_lpOleClientSite,
            &IID_IOleInPlaceSite
      );

      if (! lpIPSite)
         goto errActivate;

      OLEDBG_BEGIN2("IOleInPlaceSite::CanInPlaceActivate called\r\n");
      hrErr = lpIPSite->lpVtbl->CanInPlaceActivate(lpIPSite);
      OLEDBG_END2
      if (hrErr != NOERROR)
         goto errActivate;

      lpServerDoc->m_fInPlaceActive = TRUE;
      OLEDBG_BEGIN2("IOleInPlaceSite::OnInPlaceActivate called\r\n");
      hrErr = lpIPSite->lpVtbl->OnInPlaceActivate(lpIPSite);
      OLEDBG_END2
      if (hrErr != NOERROR)
         goto errActivate;

      if (! ServerDoc_AllocInPlaceData(lpServerDoc)) {
         sc = E_OUTOFMEMORY;
         OLEDBG_BEGIN2("IOleInPlaceSite::OnInPlaceDeactivate called\r\n");
         lpIPSite->lpVtbl->OnInPlaceDeactivate(lpIPSite);
         OLEDBG_END2
         goto errActivate;
      }

      (lpIPData = lpServerDoc->m_lpIPData)->lpSite = lpIPSite;
      goto InPlaceActive;

   errActivate:
      lpServerDoc->m_fInPlaceActive = FALSE;
      if (lpIPSite)
         OleStdRelease((LPUNKNOWN)lpIPSite);

      return ResultFromScode(sc);
   }


InPlaceActive:

   if (! lpServerDoc->m_fInPlaceVisible) {
      lpServerDoc->m_fInPlaceVisible = TRUE;

      OLEDBG_BEGIN2("IOleInPlaceSite::GetWindow called\r\n");
      hrErr = lpIPData->lpSite->lpVtbl->GetWindow(
               lpIPData->lpSite, &lpServerDoc->m_hWndParent);
      OLEDBG_END2
      if (hrErr != NOERROR) {
         sc = GetScode(hrErr);
         goto errRtn;
      }

      if (! lpServerDoc->m_hWndParent)
         goto errRtn;

      /* 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.
      */
      _fmemset(
         (LPOLEINPLACEFRAMEINFO)&lpIPData->frameInfo,
         0,
         sizeof(OLEINPLACEFRAMEINFO)
      );
      lpIPData->frameInfo.cb = sizeof(OLEINPLACEFRAMEINFO);

      OLEDBG_BEGIN2("IOleInPlaceSite::GetWindowContext called\r\n");
      hrErr = lpIPData->lpSite->lpVtbl->GetWindowContext(lpIPData->lpSite,
               (LPOLEINPLACEFRAME FAR*) &lpIPData->lpFrame,
               (LPOLEINPLACEUIWINDOW FAR*)&lpIPData->lpDoc,
               (LPRECT)&rcPos,
               (LPRECT)&rcClip,
               (LPOLEINPLACEFRAMEINFO)&lpIPData->frameInfo);
      OLEDBG_END2

      if (hrErr != NOERROR) {
         sc = GetScode(hrErr);
         goto errRtn;
      }

      lpServerApp->m_lpIPData = lpIPData;
      ShowWindow(hWndDoc, SW_HIDE);   // make sure we are hidden

      /* NOTE: reparent in-place server document's window to the
      **    special in-place hatch border window. set the in-place site's
      **    window as the parent of the hatch window. position the
      **    in-place and hatch border windows using the PosRect and
      **    ClipRect.
      **    it is important to properly parent and position the in-place
      **    server window BEFORE calling IOleInPlaceFrame::SetMenu and
      **    SetBorderSpace.
      */
      ShowWindow(lpServerDoc->m_hWndHatch, SW_SHOW);
      // make sure App busy/blocked dialogs are parented to our
      // new hWndFrame
      OleStdMsgFilter_SetParentWindow(
         lpOleApp->m_lpMsgFilter,lpIPData->frameInfo.hwndFrame);
      SetParent(lpServerDoc->m_hWndHatch, lpServerDoc->m_hWndParent);
      SetParent(hWndDoc, lpServerDoc->m_hWndHatch);

#if defined( _DEBUG )
      OleDbgOutRect3("IOleInPlaceSite::GetWindowContext (PosRect)",
            (LPRECT)&rcPos);
      OleDbgOutRect3("IOleInPlaceSite::GetWindowContext (ClipRect)",
            (LPRECT)&rcClip);
#endif
      // save the current PosRect and ClipRect
      lpIPData->rcPosRect  = rcPos;
      lpIPData->rcClipRect = rcClip;

      /* NOTE: build the shared menu for the in-place container and
      **    the server.
      */
      if (ServerDoc_AssembleMenus (lpServerDoc) != NOERROR)
         goto errRtn;

#if defined( SVR_INSIDEOUT )
      if (lVerb == OLEIVERB_INPLACEACTIVATE) {
         // Clip the hatch window to the size of pos rect so, that
         // hatch and object adornments  will not be visible.
         ServerDoc_ResizeInPlaceWindow(lpServerDoc,
            (LPRECT)&(lpServerDoc->m_lpIPData->rcPosRect),
            (LPRECT)&(lpServerDoc->m_lpIPData->rcPosRect)
         );
      }
#endif  // SVR_INSIDEOUT
   }

#if defined( SVR_INSIDEOUT )
   // NOTE: if verb is OLEIVERB_INPLACEACTIVATE we do NOT want to
   // show our UI
   if (lVerb == OLEIVERB_INPLACEACTIVATE) {

      return NOERROR;
   }
#endif  // SVR_INSIDEOUT

   if (! lpServerDoc->m_fUIActive) {
      lpServerDoc->m_fUIActive = TRUE;
      OLEDBG_BEGIN2("IOleInPlaceSite::OnUIActivate called\r\n");
      hrErr = lpIPData->lpSite->lpVtbl->OnUIActivate(lpIPData->lpSite);
      OLEDBG_END2
      if (hrErr != NOERROR) {
         lpServerDoc->m_fUIActive = FALSE;
         goto errRtn;
      }

      SetFocus(hWndDoc);

      // Show the object adornments and hacth border around them.
      ServerDoc_ResizeInPlaceWindow(lpServerDoc,
               (LPRECT)&lpIPData->rcPosRect,
               (LPRECT)&lpIPData->rcClipRect
      );

      /* NOTE: IOleInPlaceFrame::SetActiveObject must be called BEFORE
      **    IOleInPlaceFrame::SetMenu.
      */
      OLEDBG_BEGIN2("IOleInPlaceSite::SetActiveObject called\r\n");
      lpIPData->lpFrame->lpVtbl->SetActiveObject(
         lpIPData->lpFrame,
         (LPOLEINPLACEACTIVEOBJECT) &lpServerDoc->m_OleInPlaceActiveObject,
         /*(LPOLESTR)*/g_szIPObjectTitle
      );
      OLEDBG_END2

      /* NOTE: If the container wants to give ownership of the
      **    palette then he would sendmessage WM_QUEYNEWPALETTE to
      **    the object window proc, before returning from
      **    IOleInPlaceFrame::SetActiveObject. Those objects which
      **    want to be edited inplace only if they have the ownership of
      **    the palette, can check at this point in the code whether
      **    they got WM_QUERYNEWPALETTE or not. If they didn't get
      **    the message, then they can inplace deactivate and do open
      **    editing instead.
      */



      if (lpIPData->lpDoc) {
         lpIPData->lpDoc->lpVtbl->SetActiveObject(
            lpIPData->lpDoc,
            (LPOLEINPLACEACTIVEOBJECT)&lpServerDoc->m_OleInPlaceActiveObject,
            /*(LPOLESTR)*/g_szIPObjectTitle
         );
      }

      /* NOTE: install the menu and frame-level tools on the in-place
      **    frame.
      */
      ServerDoc_AddFrameLevelUI(lpServerDoc);
   }


   return NOERROR;

errRtn:
   ServerDoc_DoInPlaceDeactivate(lpServerDoc);

   return ResultFromScode(sc);
}



HRESULT ServerDoc_DoInPlaceDeactivate(LPSERVERDOC lpServerDoc)
{
   LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;
   LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;

   if (!lpServerDoc->m_fInPlaceActive)
      return S_OK;

   lpServerDoc->m_fInPlaceActive = FALSE;

   SvrDoc_IPObj_UIDeactivate(
         (LPOLEINPLACEOBJECT)&lpServerDoc->m_OleInPlaceObject);

   /* NOTE: an inside-out style in-place server will
   **    NOT hide its window in UIDeactive (an outside-in
   **    style object will hide its window in UIDeactivate).
   **    thus, an inside-out server must explicitly hide
   **    its window in InPlaceDeactivate. it is ALSO important for an
   **    outside-in style object to call ServerDoc_DoInPlaceHide here
   **    BEFORE freeing the InPlaceData structure. it will be common
   **    for in-place containers to call IOleInPlaceObject::
   **    InPlaceDeactivate in their IOleInPlaceSite::OnUIDeactiate
   **    implementation.
   */
   ServerDoc_DoInPlaceHide(lpServerDoc);

   OLEDBG_BEGIN2("IOleInPlaceSite::OnInPlaceDeactivate called\r\n");
   lpIPData->lpSite->lpVtbl->OnInPlaceDeactivate(lpIPData->lpSite);
   OLEDBG_END2

   OleStdRelease((LPUNKNOWN)lpIPData->lpSite);
   lpIPData->lpSite = NULL;

   ServerDoc_FreeInPlaceData(lpServerDoc);

   return NOERROR;
}


HRESULT ServerDoc_DoInPlaceHide(LPSERVERDOC lpServerDoc)
{
   LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;
   LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPOLEAPP        lpOleApp = (LPOLEAPP)g_lpApp;
   HWND            hWndApp = OutlineApp_GetWindow(g_lpApp);

   if (! lpServerDoc->m_fInPlaceVisible)
      return NOERROR;

   // Set the parent back to server app's window
   OleDoc_HideWindow((LPOLEDOC)lpServerDoc, FALSE /* fShutdown */);

   /* we need to enusure that our window is set to normal 100% zoom.
   **    if the window is next shown in open mode it should start out
   **    at normal zoom factor. our window may have been set to a
   **    different zoom factor while it was in-place active.
   */
   OutlineDoc_SetCurrentZoomCommand(lpOutlineDoc,IDM_V_ZOOM_100);

   lpServerDoc->m_fInPlaceVisible = FALSE;

   lpServerDoc->m_hWndParent = hWndApp;
   SetParent(
      lpOutlineDoc->m_hWndDoc,
      lpServerDoc->m_hWndParent
   );

   // make sure App busy/blocked dialogs are parented to our own hWndApp
   OleStdMsgFilter_SetParentWindow(lpOleApp->m_lpMsgFilter, hWndApp);

   // Hide the in-place hatch border window.
   ShowWindow(lpServerDoc->m_hWndHatch, SW_HIDE);

   ServerDoc_DisassembleMenus(lpServerDoc);

   /* we no longer need the IOleInPlaceFrame* or the doc's
   **    IOleInPlaceWindow* interface pointers.
   */
   if (lpIPData->lpDoc) {
      OleStdRelease((LPUNKNOWN)lpIPData->lpDoc);
      lpIPData->lpDoc = NULL;
   }

   if (lpIPData->lpFrame) {
      OleStdRelease((LPUNKNOWN)lpIPData->lpFrame);
      lpIPData->lpFrame = NULL;
   }

   ((LPSERVERAPP)g_lpApp)->m_lpIPData = NULL;

   return NOERROR;
}


BOOL ServerDoc_AllocInPlaceData(LPSERVERDOC lpServerDoc)
{
   LPINPLACEDATA   lpIPData;

   if (!(lpIPData = (LPINPLACEDATA) New(sizeof(INPLACEDATA))))
      return FALSE;

   lpIPData->lpFrame       = NULL;
   lpIPData->lpDoc         = NULL;
   lpIPData->lpSite        = NULL;
   lpIPData->hOlemenu      = NULL;
   lpIPData->hMenuShared   = NULL;

   lpServerDoc->m_lpIPData = lpIPData;
   return TRUE;
}


void ServerDoc_FreeInPlaceData(LPSERVERDOC lpServerDoc)
{
   Delete(lpServerDoc->m_lpIPData);
   lpServerDoc->m_lpIPData = NULL;
}


HRESULT ServerDoc_AssembleMenus(LPSERVERDOC lpServerDoc)
{
   HMENU           hMenuShared;
   LONG FAR*       lpWidths;
   UINT            uPosition;
   UINT            uPositionStart;
   LPSERVERAPP     lpServerApp = (LPSERVERAPP) g_lpApp;
   LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;
   HRESULT         hresult;
   BOOL            fNoError = TRUE;

   lpWidths = lpIPData->menuGroupWidths.width;
   hMenuShared = CreateMenu();

   if (hMenuShared &&
      (hresult = lpIPData->lpFrame->lpVtbl->InsertMenus(
         lpIPData->lpFrame, hMenuShared,
         &lpIPData->menuGroupWidths)) == NOERROR) {

      /* Insert EDIT group menus */

      uPosition = (UINT)lpWidths[0]; /* # of menus in the FILE group */
      uPositionStart = uPosition;

      fNoError &= InsertMenu(
            hMenuShared,
            (UINT)uPosition,
            (UINT)(MF_BYPOSITION | MF_POPUP),
            (UINT)lpServerApp->m_hMenuEdit,
            (LPCSTR)"&Edit"
      );
      uPosition++;

      lpWidths[1] = uPosition - uPositionStart;

      /* Insert OBJECT group menus */

      uPosition += (UINT)lpWidths[2];
      uPositionStart = uPosition;

      fNoError &= InsertMenu(
            hMenuShared,
            (UINT)uPosition,
            (UINT)(MF_BYPOSITION | MF_POPUP),
            (UINT)lpServerApp->m_hMenuLine,
            (LPCSTR)"&Line"
      );
      uPosition++;

      fNoError &= InsertMenu(
            hMenuShared,
            (UINT)uPosition,
            (UINT)(MF_BYPOSITION | MF_POPUP),
            (UINT)lpServerApp->m_hMenuName,
            (LPCSTR)"&Name"
      );
      uPosition++;

      fNoError &= InsertMenu(
            hMenuShared,
            (UINT)uPosition,
            (UINT)(MF_BYPOSITION | MF_POPUP),
            (UINT)lpServerApp->m_hMenuOptions,
            (LPCSTR)"&Options"
      );
      uPosition++;

      fNoError &= InsertMenu(
            hMenuShared,
            (UINT)uPosition,
            (UINT)(MF_BYPOSITION | MF_POPUP),
            (UINT)lpServerApp->m_hMenuDebug,
            (LPCSTR)"DbgI&Svr"
      );
      uPosition++;

      lpWidths[3] = uPosition - uPositionStart;

      /* Insert HELP group menus */

      uPosition += (UINT) lpWidths[4]; /* # of menus in WINDOW group */
      uPositionStart = uPosition;

      fNoError &= InsertMenu(
            hMenuShared,
            (UINT)uPosition,
            (UINT)(MF_BYPOSITION | MF_POPUP),
            (UINT)lpServerApp->m_hMenuHelp,
            (LPCSTR)"&Help"
      );
      uPosition++;

      lpWidths[5] = uPosition - uPositionStart;

      OleDbgAssert(fNoError == TRUE);

   } else {
      /* In-place container does not allow us to add menus to the
      **    frame.
      ** NOTE: even when the in-place container does NOT allow
      **    the building of a merged menu bar, it is CRITICAL that
      **    the in-place server still call OleCreateMenuDescriptor
      **    passing NULL for hMenuShared.
      */
      if (hMenuShared) {
         DestroyMenu(hMenuShared);
         hMenuShared = NULL;
      }
   }

   lpIPData->hMenuShared = hMenuShared;

   if (!(lpIPData->hOlemenu = OleCreateMenuDescriptor(hMenuShared,
                                 &lpIPData->menuGroupWidths)))
    {
      return ResultFromScode(E_OUTOFMEMORY);
    }

   return NOERROR;
}


void ServerDoc_DisassembleMenus(LPSERVERDOC lpServerDoc)
{
   UINT             uCount;
   UINT            uGroup;
   UINT            uDeleteAt;
   LPINPLACEDATA   lpIPData = lpServerDoc->m_lpIPData;
   LONG FAR*       lpWidths = lpIPData->menuGroupWidths.width;
   BOOL            fNoError = TRUE;

   /* NOTE: even when hMenuShared is NULL (ie. the server has no
   **    Menu), there is still an hOleMenu created that must be destroyed.
   */
   if (lpIPData->hOlemenu) {
      OleDestroyMenuDescriptor (lpIPData->hOlemenu);
      lpIPData->hOlemenu = NULL;
   }

   if (! lpIPData->hMenuShared)
      return;     // no menus to be destroyed

   /* Remove server group menus. */
   uDeleteAt = 0;
   for (uGroup = 0; uGroup < 6; uGroup++) {
      uDeleteAt += (UINT)lpWidths[uGroup++];
      for (uCount = 0; uCount < (UINT)lpWidths[uGroup]; uCount++)
         fNoError &= RemoveMenu(lpIPData->hMenuShared, uDeleteAt,
                        MF_BYPOSITION);
   }

   /* Remove container group menus */
   fNoError &= (lpIPData->lpFrame->lpVtbl->RemoveMenus(
      lpIPData->lpFrame,
      lpIPData->hMenuShared) == NOERROR);

   OleDbgAssert(fNoError == TRUE);

   DestroyMenu(lpIPData->hMenuShared);
   lpIPData->hMenuShared = NULL;
}


/* ServerDoc_UpdateInPlaceWindowOnExtentChange
** -------------------------------------------
**    The size of the in-place window needs to be changed.
**    calculate the size required in Client coordinates (taking into
**    account the current scale factor imposed by the in-place
**    container) and ask our in-place container to allow us to resize.
**    our container must call us back via
**    IOleInPlaceObject::SetObjectRects for the actual sizing to take
**    place.
**
**    NOTE: the rectangle that we ask for from our in-place
**    container is always the rectangle required for the object display
**    itself (in our case the size of the LineList contents). it does
**    NOT include the space we require for object frame adornments.
*/
void ServerDoc_UpdateInPlaceWindowOnExtentChange(LPSERVERDOC lpServerDoc)
{
   SIZEL       sizelHim;
   SIZEL       sizelPix;
   RECT        rcPosRect;
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPLINELIST  lpLL=(LPLINELIST)&lpOutlineDoc->m_LineList;
   HWND        hWndLL = lpLL->m_hWndListBox;
   LPSCALEFACTOR lpscale = (LPSCALEFACTOR)&lpOutlineDoc->m_scale;

   if (!lpServerDoc->m_fInPlaceActive)
      return;

   OleDoc_GetExtent((LPOLEDOC)lpServerDoc, (LPSIZEL)&sizelHim);

   // apply current scale factor
   sizelHim.cx = sizelHim.cx * lpscale->dwSxN / lpscale->dwSxD;
   sizelHim.cy = sizelHim.cy * lpscale->dwSxN / lpscale->dwSxD;
   XformSizeInHimetricToPixels(NULL, (LPSIZEL)&sizelHim, (LPSIZEL)&sizelPix);

   GetWindowRect(hWndLL, (LPRECT)&rcPosRect);
   ScreenToClient(lpServerDoc->m_hWndParent, (POINT FAR *)&rcPosRect);

   rcPosRect.right = rcPosRect.left + (int) sizelPix.cx;
   rcPosRect.bottom = rcPosRect.top + (int) sizelPix.cy;
   OleDbgOutRect3("ServerDoc_UpdateInPlaceWindowOnExtentChange: (PosRect)", (LPRECT)&rcPosRect);

   OLEDBG_BEGIN2("IOleInPlaceSite::OnPosRectChange called\r\n");
   lpServerDoc->m_lpIPData->lpSite->lpVtbl->OnPosRectChange(
         lpServerDoc->m_lpIPData->lpSite,
         (LPRECT) &rcPosRect
   );
   OLEDBG_END2
}


/* ServerDoc_CalcInPlaceWindowPos
 * ------------------------------
 *
 *  Move (and re-scale) the ServerDoc to the specified rectangle.
 *
 *  Parameters:
 *      lprcListBox - rect in client coordinate in which the listbox will fit
 *      lprcDoc     - corresponding size of the Doc in client coordinate
 *
 */
void ServerDoc_CalcInPlaceWindowPos(
      LPSERVERDOC         lpServerDoc,
      LPRECT              lprcListBox,
      LPRECT              lprcDoc,
      LPSCALEFACTOR       lpscale
)
{
   SIZEL sizelHim;
   SIZEL sizelPix;
   LPLINELIST lpLL;
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPHEADING lphead;

   if (!lpServerDoc || !lprcListBox || !lprcDoc)
      return;
   lphead = (LPHEADING)&lpOutlineDoc->m_heading;

   lpLL = OutlineDoc_GetLineList(lpOutlineDoc);
   OleDoc_GetExtent((LPOLEDOC)lpServerDoc, (LPSIZEL)&sizelHim);
   XformSizeInHimetricToPixels(NULL, &sizelHim, &sizelPix);

   if (sizelHim.cx == 0 || sizelPix.cx == 0) {
      lpscale->dwSxN = 1;
      lpscale->dwSxD = 1;
   } else {
      lpscale->dwSxN = lprcListBox->right - lprcListBox->left;
      lpscale->dwSxD = sizelPix.cx;
   }

   if (sizelHim.cy == 0 || sizelPix.cy == 0) {
      lpscale->dwSyN = 1;
      lpscale->dwSyD = 1;
   } else {
      lpscale->dwSyN = lprcListBox->bottom - lprcListBox->top;
      lpscale->dwSyD = sizelPix.cy;
   }

   lprcDoc->left = lprcListBox->left - Heading_RH_GetWidth(lphead,lpscale);
   lprcDoc->right = lprcListBox->right;
   lprcDoc->top = lprcListBox->top - Heading_CH_GetHeight(lphead,lpscale);
   lprcDoc->bottom = lprcListBox->bottom;
}


/* ServerDoc_ResizeInPlaceWindow
** -----------------------------
**    Actually resize the in-place ServerDoc windows according to the
**    PosRect and ClipRect allowed by our in-place container.
**
**    NOTE: the PosRect rectangle that our in-place container tells
**    us is always the rectangle required for the object display
**    itself (in our case the size of the LineList contents). it does
**    NOT include the space we require for object frame adornments.
*/
void ServerDoc_ResizeInPlaceWindow(
      LPSERVERDOC         lpServerDoc,
      LPCRECT             lprcPosRect,
      LPCRECT             lprcClipRect
)
{
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPLINELIST   lpLL = (LPLINELIST)&lpOutlineDoc->m_LineList;
   SCALEFACTOR  scale;
   RECT         rcDoc;
   POINT        ptOffset;

   /* NOTE: calculate the space needed for our object frame
   **    adornments. our in-place container tells us the size that our
   **    object should take in window client coordinates
   **    (lprcPosRect). the rectangle cooresponds to the size that our
   **    LineList ListBox should be. our Doc window must the correct
   **    amount larger to accomodate our row/column headings.
   **    then move all windows into position.
   */
   ServerDoc_CalcInPlaceWindowPos(
         lpServerDoc,
         (LPRECT)lprcPosRect,
         (LPRECT)&rcDoc,
         (LPSCALEFACTOR)&scale
   );

   /* NOTE: we need to honor the lprcClipRect specified by our
   **    in-place container. we must NOT draw outside of the ClipRect.
   **    in order to achieve this, we will size the hatch window to be
   **    exactly the size that should be visible (rcVisRect). the
   **    rcVisRect is defined as the intersection of the full size of
   **    the in-place server window and the lprcClipRect.
   **    the ClipRect could infact clip the HatchRect on the
   **    right/bottom and/or on the top/left. if it is clipped on the
   **    right/bottom then it is sufficient to simply resize the hatch
   **    window. but if the HatchRect is clipped on the top/left then
   **    we must "move" the ServerDoc window (child of HatchWindow) by
   **    the delta that was clipped. the window origin of the
   **    ServerDoc window will then have negative coordinates relative
   **    to its parent HatchWindow.
   */
   SetHatchWindowSize(
         lpServerDoc->m_hWndHatch,
         (LPRECT)&rcDoc,
         (LPRECT)lprcClipRect,
         (LPPOINT)&ptOffset
   );

   // shift Doc window to account for hatch frame being drawn
   OffsetRect((LPRECT)&rcDoc, ptOffset.x, ptOffset.y);

   // move/size/set scale factor of ServerDoc window.
   OutlineDoc_SetScaleFactor(
         lpOutlineDoc, (LPSCALEFACTOR)&scale, (LPRECT)&rcDoc);

   /* reset the horizontal extent of the listbox. this makes
   **    the listbox realize that a scroll bar is not needed.
   */
   SendMessage(
         lpLL->m_hWndListBox,
         LB_SETHORIZONTALEXTENT,
         (int) 0,
         0L
   );
   SendMessage(
         lpLL->m_hWndListBox,
         LB_SETHORIZONTALEXTENT,
         (int) (lprcPosRect->right - lprcPosRect->left),
         0L
   );
}


/* ServerDoc_SetStatusText
**    Tell the active in-place frame to display a status message.
*/
void ServerDoc_SetStatusText(LPSERVERDOC lpServerDoc, LPOLESTR lpszMessage)
{
   if (lpServerDoc && lpServerDoc->m_fUIActive &&
      lpServerDoc->m_lpIPData != NULL) {

      OLEDBG_BEGIN2("IOleInPlaceFrame::SetStatusText called\r\n")
      lpServerDoc->m_lpIPData->lpFrame->lpVtbl->SetStatusText
            (lpServerDoc->m_lpIPData->lpFrame, lpszMessage);
      OLEDBG_END2
   }
}


/* ServerDoc_GetTopInPlaceFrame
** ----------------------------
**    returns NON-AddRef'ed pointer to Top In-Place Frame interface
*/
LPOLEINPLACEFRAME ServerDoc_GetTopInPlaceFrame(LPSERVERDOC lpServerDoc)
{
   if (lpServerDoc->m_lpIPData)
      return lpServerDoc->m_lpIPData->lpFrame;
   else
      return NULL;
}

void ServerDoc_GetSharedMenuHandles(
      LPSERVERDOC lpServerDoc,
      HMENU FAR*      lphSharedMenu,
      HOLEMENU FAR*   lphOleMenu
)
{
   if (lpServerDoc->m_lpIPData) {
      *lphSharedMenu = lpServerDoc->m_lpIPData->hMenuShared;
      *lphOleMenu = lpServerDoc->m_lpIPData->hOlemenu;
   } else {
      *lphSharedMenu = NULL;
      *lphOleMenu = NULL;
   }
}


void ServerDoc_AddFrameLevelUI(LPSERVERDOC lpServerDoc)
{
   LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPSERVERAPP lpServerApp = (LPSERVERAPP)g_lpApp;
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPOLEINPLACEFRAME lpTopIPFrame=ServerDoc_GetTopInPlaceFrame(lpServerDoc);
   HMENU           hSharedMenu;            // combined obj/cntr menu
   HOLEMENU        hOleMenu;               // returned by OleCreateMenuDesc.

   ServerDoc_GetSharedMenuHandles(
         lpServerDoc,
         &hSharedMenu,
         &hOleMenu
   );

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

   // save normal accelerator table
   lpServerApp->m_hAccelBaseApp = lpOutlineApp->m_hAccelApp;

   // install accelerator table for UIActive server (w/ active editor cmds)
   lpOutlineApp->m_hAccel = lpServerApp->m_hAccelIPSvr;
   lpOutlineApp->m_hAccelApp = lpServerApp->m_hAccelIPSvr;
   lpOutlineApp->m_hWndAccelTarget = lpOutlineDoc->m_hWndDoc;

#if defined( USE_FRAMETOOLS )
   ServerDoc_AddFrameLevelTools(lpServerDoc);

   // update toolbar button enable states
   OutlineDoc_UpdateFrameToolButtons(lpOutlineDoc);
#endif
}


void ServerDoc_AddFrameLevelTools(LPSERVERDOC lpServerDoc)
{
   LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
   LPSERVERAPP     lpServerApp = (LPSERVERAPP)g_lpApp;
   LPOUTLINEDOC    lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   LPOLEINPLACEFRAME lpTopIPFrame=ServerDoc_GetTopInPlaceFrame(lpServerDoc);

#if defined( USE_FRAMETOOLS )
   HWND            hWndFrame;

   FrameTools_Enable(lpOutlineDoc->m_lpFrameTools, TRUE);

   // if not in-place UI active, add our tools to our own frame.
   if (! lpServerDoc->m_fUIActive) {
      OutlineDoc_AddFrameLevelTools(lpOutlineDoc);
      return;
   }

   if ((hWndFrame = OutlineApp_GetFrameWindow(lpOutlineApp)) == NULL) {
      /* we could NOT get a valid frame window, so POP our tools up. */

      /* NOTE: since we are poping up our tools, we MUST inform
      **    the top in-place frame window that we need NO tool space
      **    BUT that it should NOT put its own tools up. if we were
      **    to pass NULL instead of (0,0,0,0), then the container
      **    would have the option to leave its own tools up.
      */
      lpTopIPFrame->lpVtbl->SetBorderSpace(
            lpTopIPFrame,
            (LPCBORDERWIDTHS)&g_rectNull
      );
      FrameTools_PopupTools(lpOutlineDoc->m_lpFrameTools);
   } else {

      /* NOTE: we need to negotiate for space and attach our frame
      **    level tools to the top-level in-place container's frame window.
      */
      FrameTools_AttachToFrame(lpOutlineDoc->m_lpFrameTools, hWndFrame);

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

#else   // ! USE_FRAMETOOLS
   /* NOTE: if you do NOT use frame tools, you MUST inform the top
   **    in-place frame window so that it can put back its own tools.
   */
   lpTopIPFrame->lpVtbl->SetBorderSpace(lpIPData->lpFrame, NULL);
#endif  // ! USE_FRAMETOOLS
}


#if defined( USE_FRAMETOOLS )

void ServerDoc_RemoveFrameLevelTools(LPSERVERDOC lpServerDoc)
{
   LPOUTLINEDOC lpOutlineDoc = (LPOUTLINEDOC)lpServerDoc;
   OleDbgAssert(lpOutlineDoc->m_lpFrameTools != NULL);

    // Reparent our tools back to one of our own windows
    FrameTools_AttachToFrame(lpOutlineDoc->m_lpFrameTools,g_lpApp->m_hWndApp);

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



void ServerDoc_UIActivate (LPSERVERDOC lpServerDoc)
{
   if (lpServerDoc->m_fInPlaceActive && !lpServerDoc->m_fUIActive) {
      ServerDoc_DoInPlaceActivate(lpServerDoc,
            OLEIVERB_UIACTIVATE,
            NULL /*lpmsg*/,
            lpServerDoc->m_lpOleClientSite
      );
      OutlineDoc_ShowWindow((LPOUTLINEDOC)lpServerDoc);
   }
}
