home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 May / Pcwk5b98.iso / Borland / Cplus45 / BC45 / BOCOLE.PAK / BOLEDOC.CPP < prev    next >
C/C++ Source or Header  |  1995-08-29  |  37KB  |  1,269 lines

  1. //
  2. //**************************************************************************
  3. //
  4. // BOleDoc.cpp --    Implements the Bolero half of the document-level
  5. //                   object. Provides plumbing for activation of OLE2 objects
  6. //                   as document windows become active. Also the main drag/drop
  7. //                   and clipboard implementation.
  8. //
  9. // Copyright (c) 1993,94 by Borland International, Inc. All rights reserved
  10. //
  11. //**************************************************************************
  12.  
  13. #include "BOleDoc.h"
  14. #include "BOleSvc.h"
  15. #include "BOlePart.h"
  16.  
  17. #include <olestd.h>
  18.  
  19. //**************************************************************************
  20. //
  21. // Initialize static/global data
  22. //
  23. //**************************************************************************
  24.  
  25. // IOleUILinkContainer is only used here
  26.  
  27. #include "initguid.h"
  28. DEFINE_GUID(IID_IOleUILinkContainer, 0x000004FF, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
  29. PREDECLARE_INTERFACE(IOleUILinkContainer)
  30. DEFINE_INLINE_QI(IOleUILinkContainer, IUnknown)
  31.  
  32. // CLIPFORMAT is a WORD while RegisterClipboardFormat returns a UINT
  33.  
  34. #ifdef ANSI
  35. CLIPFORMAT BOleDocument::oleEmbdObjClipFmt   = RegisterClipboardFormatA("Embedded Object");
  36. CLIPFORMAT BOleDocument::oleEmbSrcClipFmt    = RegisterClipboardFormatA("Embed Source");
  37. CLIPFORMAT BOleDocument::oleLinkSrcClipFmt   = RegisterClipboardFormatA("Link Source");
  38. CLIPFORMAT BOleDocument::oleLinkSrcDescFmt    = RegisterClipboardFormatA("Link Source Descriptor");
  39.  
  40. CLIPFORMAT BOleDocument::oleLinkClipFmt      = RegisterClipboardFormatA("ObjectLink");
  41. CLIPFORMAT BOleDocument::oleNativeClipFmt    = RegisterClipboardFormatA("Native");
  42. CLIPFORMAT BOleDocument::oleOwnerLinkClipFmt = RegisterClipboardFormatA("OwnerLink");
  43. CLIPFORMAT BOleDocument::oleObjectDescFmt    = RegisterClipboardFormatA("Object Descriptor");
  44.  
  45. UINT BOleDocument::dragScrollInset = GetProfileIntA ("windows", "DragScrollInset", DD_DEFSCROLLINSET);
  46. UINT BOleDocument::dragScrollDelay = GetProfileIntA ("windows", "DragScrollDelay", DD_DEFSCROLLDELAY);
  47. UINT BOleDocument::dragScrollInterval = GetProfileIntA ("windows", "DragScrollInterval", DD_DEFSCROLLINTERVAL);
  48. #else
  49. CLIPFORMAT BOleDocument::oleEmbdObjClipFmt   = RegisterClipboardFormat (OLESTR("Embedded Object"));
  50. CLIPFORMAT BOleDocument::oleEmbSrcClipFmt    = RegisterClipboardFormat (OLESTR("Embed Source"));
  51. CLIPFORMAT BOleDocument::oleLinkSrcClipFmt   = RegisterClipboardFormat (OLESTR("Link Source"));
  52. CLIPFORMAT BOleDocument::oleLinkSrcDescFmt    = RegisterClipboardFormat (OLESTR("Link Source Descriptor"));
  53.  
  54. CLIPFORMAT BOleDocument::oleLinkClipFmt      = RegisterClipboardFormat (OLESTR("ObjectLink"));
  55. CLIPFORMAT BOleDocument::oleNativeClipFmt    = RegisterClipboardFormat (OLESTR("Native"));
  56. CLIPFORMAT BOleDocument::oleOwnerLinkClipFmt = RegisterClipboardFormat (OLESTR("OwnerLink"));
  57. CLIPFORMAT BOleDocument::oleObjectDescFmt    = RegisterClipboardFormat (OLESTR("Object Descriptor"));
  58.  
  59. UINT BOleDocument::dragScrollInset = GetProfileInt (OLESTR("windows"), OLESTR("DragScrollInset"), DD_DEFSCROLLINSET);
  60. UINT BOleDocument::dragScrollDelay = GetProfileInt (OLESTR("windows"), OLESTR("DragScrollDelay"), DD_DEFSCROLLDELAY);
  61. UINT BOleDocument::dragScrollInterval = GetProfileInt (OLESTR("windows"), OLESTR("DragScrollInterval"), DD_DEFSCROLLINTERVAL);
  62. #endif
  63.  
  64. #ifdef OLEDBG
  65. OLEDBGDATA;
  66. #endif
  67.  
  68. //**************************************************************************
  69. //
  70. // BOleDocument implementation
  71. //
  72. //**************************************************************************
  73.  
  74. HRESULT _IFUNC BOleDocument::QueryInterfaceMain(REFIID iid, LPVOID FAR *ppv)
  75. {
  76.     HRESULT hr = ResultFromScode(E_NOINTERFACE);
  77.     *ppv = NULL;
  78.  
  79.     // self
  80.  
  81.     if (iid == IID_BOleDocument) {
  82.         (BOleDocument*) *ppv = this;
  83.         AddRef ();
  84.         return NOERROR;
  85.     }
  86.  
  87.     // interfaces
  88.  
  89.        SUCCEEDED(hr = IOleInPlaceUIWindow_QueryInterface(this, iid, ppv))
  90.     || SUCCEEDED(hr = IDropTarget_QueryInterface(this, iid, ppv))
  91.     || SUCCEEDED(hr = IBDocument_QueryInterface(this, iid, ppv))
  92.     || SUCCEEDED(hr = IOleUILinkContainer_QueryInterface(this, iid, ppv))
  93.     || SUCCEEDED(hr = IOleInPlaceFrame_QueryInterface(this, iid, ppv))
  94.     || SUCCEEDED(hr = IBOverrideBrowseLinks_QueryInterface(this, iid, ppv))
  95.  
  96.     // base classes
  97.  
  98.     || SUCCEEDED(hr = BOleComponent::QueryInterfaceMain(iid, ppv))
  99.  
  100.     // helpers
  101.     ;
  102.  
  103.     return hr;
  104. }
  105.  
  106. BOleDocument::BOleDocument (BOleClassManager *pF, IBUnknownMain * pObj, BOleService * pOS) :
  107.     BOleComponent(pF, pObj), pService (pOS), fInBrowseLinks(FALSE),
  108.     pActivePart (NULLP), pFirstPart(NULLP), fRegistered(FALSE)
  109. {
  110.     pContainer = NULLP;
  111.     pApplication = NULLP;
  112.  
  113. #ifdef OLEDBG
  114.     DebugMode = 0;
  115. #endif
  116.  
  117.     WIN::SetRect (&rcScrollTop,0,0,0,0);
  118.     WIN::SetRect (&rcScrollBottom,0,0,0,0);
  119.     WIN::SetRect (&rcScrollLeft,0,0,0,0);
  120.     WIN::SetRect (&rcScrollRight,0,0,0,0);
  121.     scrollEnterTime = 0;
  122.     scrollLastTime = 0;
  123.     fScrollDelay = FALSE;
  124.  
  125.     dndObjPos.left = dndObjPos.top = dndObjPos.right = dndObjPos.bottom = 0;
  126.     dndLastPos.x = dndLastPos.y = -1;
  127.     dndSize.cx = dndSize.cy = 0;
  128.     dndOffset.x = dndOffset.y = 0;
  129.     dndInfoAvailable = FALSE;
  130.  
  131.     dndFormatCount = 0;
  132.     dndFormatList = NULL;
  133.     fDrawn = FALSE;
  134. }
  135.  
  136. BOleDocument::~BOleDocument ()
  137. {
  138.     if (pService->GetActiveDoc() == this)
  139.         pService->SetActiveDoc (NULL);
  140.     
  141.     if (pActivePart)  {
  142.         pActivePart->Release ();
  143.         pActivePart = NULL;
  144.     }
  145. }
  146.  
  147. //**************************************************************************
  148. //
  149. // IBDocument implementation
  150. //
  151. //**************************************************************************
  152.  
  153. HRESULT _IFUNC BOleDocument::Init( IBContainer *pBack)
  154. {
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.     HRESULT hRes = NOERROR;
  175.  
  176.     pContainer = pBack;
  177.     pApplication = pService->GetApplication();
  178.  
  179.     HWND hwnd = pContainer->GetWindow();
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.     if (!fRegistered) {
  189.  
  190.         // Register the document window as a Drag & Drop target. This
  191.         // means that we can't drag onto the frame window, but that
  192.         // doesn't seem like an oppressive restriction
  193.         //
  194.         if (hwnd) {
  195.  
  196.             CoLockObjectExternal (AsPIUnknown(pObjOuter), TRUE, TRUE);
  197.             hRes = OLE::RegisterDragDrop(hwnd, this);
  198.             fRegistered = SUCCEEDED(hRes);
  199.         }
  200.     }
  201.  
  202.     return hRes;
  203. }
  204.  
  205. // Call when the container window in the client app was resized
  206. //
  207. void _IFUNC BOleDocument::OnResize ()
  208. {
  209.     if (pContainer) {
  210.         RECT r;
  211.         pContainer->GetWindowRect (&r); 
  212.         if (pActivePart) {
  213.  
  214.             // You would think that telling the object that its container has
  215.             // resized would renegotiate the clip rect, but it doesn't seem to
  216.             //
  217.             pActivePart->ResizeBorder (&r, this, FALSE);
  218.  
  219.             // We renegotiate the clipping rect for them here
  220.             //
  221.             BOlePart *pWalk = pFirstPart;
  222.             while (pWalk) {
  223.                 if (pWalk->IsInPlace()) {
  224.                     pWalk->ResetObjectRects();
  225.                     pWalk = NULL;
  226.                 }
  227.                 else
  228.                     pWalk = pWalk->GetNextPart();
  229.             }
  230.         }
  231.     }
  232. }
  233.  
  234. void _IFUNC BOleDocument::OnActivate (BOOL fActivate)
  235. {
  236.     // Remember the active document state
  237.     //
  238.     if (fActivate)
  239.         pService->SetActiveDoc (this);
  240.     else
  241.         if (pService->GetActiveDoc() == this)
  242.             pService->SetActiveDoc (NULL);
  243.  
  244.     // Make the OLE2 call to remove the inplace active object's
  245.     // user interface
  246.     //
  247.     BOOL fMDI = (GetScode(pService->GetApplication()->IsMDI()) == S_OK);
  248.  
  249.     if (fMDI && pActivePart) {
  250.         pActivePart->OnDocWindowActivate (fActivate);
  251.  
  252.         // Strangely, no OLE2 traffic is generated by deactivating the
  253.         // document window of an inplace active object. I would expect
  254.         // them to call SetBorderSpace(NULL), but the docs specifically
  255.         // instruct *not* to do that. I suppose straight OLE2 apps can
  256.         // just put back their UI in addition to calling OnDocWindowActivate,
  257.         // but Bolero will notify clients to do it.
  258.         //
  259.         if (!fActivate)
  260.             pService->GetApplication()->RestoreUI ();
  261.     }
  262. }
  263.  
  264. // Call just before the container window is destroyed
  265. //
  266. void _IFUNC BOleDocument::OnClose ()
  267. {
  268.     if (fRegistered) {
  269.         HWND hwnd = pContainer->GetWindow();
  270.         HRESULT hRes = OLE::RevokeDragDrop(hwnd);
  271.  
  272.         // If we can't unregister the window, it isn't registered and can't
  273.         // be unlocked. In particular, CoLockObjectExternal can recursively
  274.         // call OnClose, and calling CoLockObjectExternal recursively will
  275.         // be a big mess
  276.         //
  277.         if (hRes == NOERROR) {
  278.             CoLockObjectExternal (AsPIUnknown(pObjOuter), FALSE, TRUE);
  279.             fRegistered = FALSE;
  280.         }
  281.         else
  282.             fRegistered = TRUE;
  283.     }
  284. }
  285.  
  286. HRESULT _IFUNC BOleDocument::OnSetFocus(BOOL bSet)
  287. {
  288.     // The policy is that inplace active object always gets first shot at 
  289.     // keystrokes, but the practicality is that the user can set focus to 
  290.     // any old window by clicking on it. So, if some other window does
  291.     // succeed in getting focus, it should give it immediately back to
  292.     // the inplace server object.
  293.     //
  294.     if(bSet && pActivePart) {
  295.         HWND w = NULL;
  296.         HRESULT h = pActivePart->GetWindow(&w);
  297.         if (SUCCEEDED(h)) {
  298.             ::SetFocus (w);
  299.             return ResultFromScode (S_FALSE);
  300.         }
  301.     }
  302.  
  303.     // The Bolero container app gets to keep focus since there was no
  304.     // a inplace active server object
  305.     //
  306.     return ResultFromScode (S_OK);
  307. }
  308.  
  309. HRESULT _IFUNC BOleDocument::EnumLinks (PIBLinkInfo FAR*)
  310. {
  311.     return ResultFromScode (E_NOTIMPL);
  312. }
  313.  
  314. // UpdateLinks --    This function packages up some functionality of IBLinkInfo
  315. //                and IBDocument::EnumLinks. The goal is to provide a 
  316. //                one-stop way for client apps to update the links in their
  317. //                document. They are responsible for the dialog box, if any.
  318. //
  319. HRESULT _IFUNC BOleDocument::UpdateLinks ()
  320. {
  321.     BOlePart FAR *pWalk = pFirstPart->GetNextLink (TRUE);
  322.     HRESULT hr = NOERROR;
  323.  
  324.     while (pWalk) {
  325.         hr = pWalk->UpdateNow();
  326.         pWalk = pWalk->GetNextLink (FALSE);
  327.     }
  328.  
  329.     return hr;
  330. }
  331.  
  332. HRESULT _IFUNC BOleDocument::BrowseLinks ()
  333. {
  334.     if( fInBrowseLinks )
  335.         // Cannot nest link browser for the same document
  336.         return ResultFromScode (E_FAIL);
  337.     fInBrowseLinks++;
  338.     
  339.     OLEUIEDITLINKS      el;
  340.     _fmemset((LPOLEUIEDITLINKS)&el,0,sizeof(el));
  341.     el.cbStruct = sizeof (OLEUIEDITLINKS);
  342.     el.hWndOwner = ::GetActiveWindow(); // pContainer->GetWindow ();
  343.     el.lpOleUILinkContainer = this;
  344.     el.dwIBApplication = (DWORD) pApplication;
  345.     if (pService->ShowHelpButton(BOLE_HELP_BROWSELINKS))
  346.         el.dwFlags = ELF_SHOWHELP;
  347.  
  348.     pService->EnterBOleDialog (TRUE, el.hHook, el.hTask);
  349.     UINT r = OleUIEditLinks (&el);
  350.     pService->EnterBOleDialog (FALSE, el.hHook, el.hTask);
  351.     
  352.     fInBrowseLinks--;
  353.  
  354.     if (r == 1)
  355.         return ResultFromScode (S_OK); //
  356.     return ResultFromScode (S_FALSE);
  357. }
  358.  
  359. // This guy is private implementation called by BOleService because the
  360. // document knows if any parts are links
  361. //
  362. BOOL _IFUNC BOleDocument::EnableBrowseLinks ()
  363. {
  364.     return pFirstPart && pFirstPart->GetNextLink (TRUE) != NULLP;
  365. }
  366.  
  367. //**************************************************************************
  368. //
  369. // IOleWindow implementation
  370. //
  371. //**************************************************************************
  372.  
  373. HRESULT _IFUNC BOleDocument::GetWindow (HWND FAR *phwnd)
  374. {
  375.     *phwnd = pContainer->GetWindow();
  376.     OLERET(S_OK);
  377. }
  378.  
  379. HRESULT _IFUNC BOleDocument::ContextSensitiveHelp (BOOL fEnterMode)
  380. {
  381.     pService->SetHelpMode (fEnterMode);
  382.     OLERET(S_OK);
  383. }
  384.  
  385. //**************************************************************************
  386. //
  387. // IOleInPlaceUIWindow implementation
  388. //
  389. //**************************************************************************
  390.  
  391. HRESULT _IFUNC BOleDocument::GetBorder (LPRECT prectBorder)
  392. {
  393.     OLERET (SUCCEEDED(pContainer->GetWindowRect(prectBorder))
  394.         ? S_OK : INPLACE_E_NOTOOLSPACE);
  395. }
  396.  
  397. HRESULT _IFUNC BOleDocument::RequestBorderSpace (LPCRECT prectWidths)
  398. {
  399.     OLERET (SUCCEEDED(pContainer->RequestBorderSpace(prectWidths))
  400.         ? S_OK : INPLACE_E_NOTOOLSPACE);
  401. }
  402.  
  403. HRESULT _IFUNC BOleDocument::SetBorderSpace (LPCBORDERWIDTHS prectWidths)
  404. {
  405.  
  406.  
  407.  
  408.     OLERET(SUCCEEDED(pContainer->SetBorderSpace(prectWidths))
  409.         ? S_OK : OLE_E_INVALIDRECT);
  410. }
  411.  
  412. HRESULT _IFUNC BOleDocument::SetActiveObject (IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
  413. {
  414.     // Decrement the ref count of the pointer we're giving up, and
  415.     // bump the ref count of the new pointer we're going to hold
  416.     //
  417.     if (pActivePart)
  418.         pActivePart->Release ();
  419.     pActivePart = pActiveObject;
  420.     if (pActivePart)
  421.         pActivePart->AddRef ();
  422.  
  423.     // Although it seems a little strange, we're supposed to put the
  424.     // name of the object in our caption bar in the client.
  425.     //
  426.     if (pszObjName)
  427.         pContainer->AppendWindowTitle (pszObjName);
  428.     else
  429.         // Null name means object is deactivating. 
  430.         pContainer->RestoreUI();
  431.  
  432.     OLERET (S_OK);
  433. }
  434.  
  435. //**************************************************************************
  436. //
  437. // IDropTarget implementation (and some helper functions)
  438. //
  439. //**************************************************************************
  440.  
  441. // Helper function called by IDropTarget functions to make sure the object the
  442. // user is trying to drop is acceptable to the application
  443. //
  444. short _IFUNC BOleDocument::ValidateDrag(DWORD keyState, POINTL *where, DWORD *effect, IDataObject *dataObj)
  445. {
  446.     // No container formats -- no legal drop available
  447.     //
  448.     if (!dndFormatCount) {
  449.         *effect = DROPEFFECT_NONE;
  450.         dndFormat = 0;
  451.         return -1;
  452.     }
  453.  
  454.     IBApplication*pIA = pService->GetApplication();
  455.  
  456.     // Find out what the user wants to do by looking at the modifier keys
  457.     if (keyState & MK_CONTROL)
  458.         *effect = (keyState & MK_SHIFT ? DROPEFFECT_LINK : DROPEFFECT_COPY);
  459.     else
  460.         // !(MK_SHIFT) is the "default" case for no modifier keys. In this case
  461.         // the default has the same effect as shift, but it isn't required to
  462.         //
  463.         *effect = (keyState & MK_SHIFT ? DROPEFFECT_MOVE : DROPEFFECT_MOVE);
  464.  
  465.     // if the key state doesn't match with the server capabilities, fail
  466.     // ie. don't allow user to press control shift if server doesn't allow link
  467.     if (*effect == DROPEFFECT_LINK) {
  468.         if (fCantLink) {
  469.             *effect = DROPEFFECT_NONE;
  470.             return -1;
  471.         }
  472.     }
  473.     // dndFormat explanation: The reason dndFormat is in member data is that 
  474.     // for DragOver messages, we don't have a data object to negotiate with. 
  475.     // But we still want to give the format to the container window, so 
  476.     // dndFormat remembers what the last agreed format was and we can still 
  477.     // pass it back.
  478.     //
  479.     // Also, dndFormat is an actual clipboard format and agreedFormat is a
  480.     // list index.
  481.  
  482.     // Now make sure the application supports what the user wants to do
  483.     //
  484.     BOOL canLink = (GetScode (pIA->CanLink()) == S_OK);
  485.     BOOL canEmbed = (GetScode(pIA->CanEmbed()) == S_OK);
  486.     if ((*effect & DROPEFFECT_LINK && !canLink) ||
  487.         ((*effect & DROPEFFECT_COPY || *effect & DROPEFFECT_MOVE) && !canEmbed)) {
  488.         *effect = DROPEFFECT_NONE;
  489.         dndFormat = 0;
  490.         return -1;
  491.     }
  492.  
  493.     // Find the best data format the server and client can agree upon and
  494.     // see if that format can be dropped at the current mouse location
  495.     //
  496.     // If we didn't get an IDataObject (it isn't passed to DragOver) we can't
  497.     // validate the clipboard format, which is fine with us because it's pretty slow
  498.     //
  499.     if (dataObj) {
  500.         short agreedFormat = -1;
  501.         agreedFormat = pService->MatchPriorityClipFormat(dataObj,
  502.             dndFormatList, dndFormatCount);
  503.         if (agreedFormat == -1) {    
  504.             *effect = DROPEFFECT_NONE;
  505.             dndFormat = 0;        //  to 0 to know we don't agree during DragOver
  506.             return -1;
  507.         }
  508.         else {
  509.             dndFormat = dndFormatList[agreedFormat].fmtetc.cfFormat;
  510.             return agreedFormat;
  511.         }
  512.     }
  513.     else
  514.         // No data object -- no format to negotiate (don't touch dndFormat)
  515.         //
  516.         if (dndFormat == 0)            // in case we couldn't agree in DragEnter
  517.             return -1;
  518.         else
  519.             return 1;
  520.  
  521. }
  522.  
  523. // In OLE2, the container (IDropTarget) may decide to scroll if certain
  524. // conditions are met:
  525. //        1. The mouse must be within 11 pixels of the edge of the target
  526. //        2. The mouse must remain within 11 pixels for 50 millisecs ("delay")
  527. //            before any scrolling takes place
  528. //        3. The rate of scrolling ("interval") must be no more than once
  529. //            every 50 millisecs.
  530. //    These values are pulled from WIN.INI when the Bolero DLL is loaded.
  531. //
  532. // Bolero clients will receive an IContainer::Scroll message whenever
  533. // it's appropriate to scroll. The BOleScroll bitfield may specify
  534. // two-directional scrolling (e.g. up and to the left). The client may
  535. // scroll or not, and should return TRUE if it does in order to tell
  536. // the server to change the cursor shape to the scrolling version.
  537. //
  538. BOleScroll _IFUNC BOleDocument::DragScrollReqd (POINTL *where)
  539. {
  540.     // Check to see if the point is in any of our cached scrolling rects
  541.     //
  542.     BOleScroll ret = (BOleScroll) 0;
  543.     POINT shortWhere = {where->x, where->y};
  544.     HWND w = pContainer->GetWindow(); //
  545.     WIN::ScreenToClient (w, &shortWhere);
  546.     if (::PtInRect (&rcScrollLeft, shortWhere))
  547.         ret = ret | BOLE_SCROLL_RIGHT;
  548.     if (::PtInRect (&rcScrollRight, shortWhere))
  549.         ret = ret | BOLE_SCROLL_LEFT;
  550.     if (::PtInRect (&rcScrollTop, shortWhere))
  551.         ret = ret| BOLE_SCROLL_DOWN;
  552.     if (::PtInRect (&rcScrollBottom, shortWhere))
  553.         ret = ret | BOLE_SCROLL_UP;
  554.  
  555.     // If not in any scroll rects, reset the times and leave
  556.     //
  557.     if (!ret) {
  558.         scrollEnterTime = 0;
  559.         scrollLastTime = 0;
  560.         return (BOleScroll) ret;
  561.     }
  562.  
  563.     DWORD now = WIN::GetCurrentTime ();
  564.  
  565.     // If this is the first time we've noticed the mouse in the scroll
  566.     // area, make a note of the time and start waiting for the
  567.       // initial delay latency
  568.     //
  569.     if (!scrollEnterTime) {
  570.         scrollEnterTime = now;
  571.         fScrollDelay = TRUE;
  572.         return (BOleScroll) 0;
  573.     }
  574.  
  575.     // If we're waiting for the initial delay to pass, check 'now' against
  576.     // the initial delay latency instead of the interval latency. These
  577.     // are probably the same, but we're good citizens. If the delay has
  578.     // expired, we can return the scrolling direction to indicate the
  579.     // beginning of a scroll operation.
  580.     //
  581.     if (fScrollDelay)
  582.         if (now > scrollEnterTime + dragScrollDelay) {
  583.             fScrollDelay = FALSE;
  584.             scrollLastTime = now;
  585.             return (BOleScroll) ret;
  586.         }
  587.         else
  588.             return (BOleScroll) 0;
  589.  
  590.     // If we've gotten this far, we must be in the middle of a scrolling
  591.     // operation. Check 'now' against the interval latency and return
  592.     // the scrolling direction, or 0 if the latency has not expired.
  593.     //
  594.     if (now > scrollLastTime + dragScrollInterval) {
  595.         scrollLastTime = now;
  596.         return (BOleScroll) ret;
  597.     }
  598.     else
  599.         return (BOleScroll) 0;
  600. }
  601.  
  602. // CalculateDndObjPos is a helper function which sets up a rectangle which
  603. // represents the position of the object being dragged RELATIVE TO ITS
  604. // ORIGINAL HIT POINT.
  605. //
  606. // I know I'm not passing any parameters, but this stuff is all in member
  607. // data anyway, so it seems silly to pass it on the stack too.
  608. //
  609. void _IFUNC BOleDocument::CalculateDndObjPos ()
  610. {
  611.     ::SetRect (&dndObjPos,
  612.         dndLastPos.x - dndOffset.x,
  613.         dndLastPos.y - dndOffset.y,
  614.         dndLastPos.x - dndOffset.x + dndSize.cx,
  615.         dndLastPos.y - dndOffset.y + dndSize.cy);
  616. }
  617.  
  618. HRESULT _IFUNC BOleDocument::DragEnter (IDataObject *dataObj, 
  619.                                 DWORD keyState, POINTL where, LPDWORD effect)
  620. {
  621.     POINT shortPoint;
  622.     shortPoint.x = LOWORD(where.x);
  623.     shortPoint.y = LOWORD(where.y);
  624.  
  625.     HRESULT hr = pContainer->FindDropDest (&shortPoint, &pDropDest);
  626.     if (!SUCCEEDED(hr)) {
  627.         *effect = DROPEFFECT_NONE;
  628.         return ResultFromScode (E_FAIL);
  629.     }
  630.  
  631.     // cache this for use in ValidateDrag during drag-over
  632.     fCantLink = S_OK != GetScode(OleQueryLinkFromData (dataObj));
  633.     
  634.     // same dropdest/document pair
  635.     //
  636.     RECT r;
  637.     if (SUCCEEDED(pDropDest->GetScrollRect (&r)))
  638.         CacheDragRects (&r);
  639.  
  640.     // Find out how many formats the client can accept. If they can't
  641.     // accept any, return an error so no drop can take place
  642.     //
  643.     dndFormatCount = pDropDest->CountFormats ();
  644.     if (!dndFormatCount) {
  645.         *effect = DROPEFFECT_NONE;
  646.         return ResultFromScode (E_FAIL);
  647.     }
  648.     
  649.     // Hold the container's legal data formats in an array
  650.     //
  651.     dndFormatList = new OLEUIPASTEENTRY [dndFormatCount];
  652.     BOleFormat f;
  653.     for (short i = 0; i < dndFormatCount; i++) {
  654.         pDropDest->GetFormat (i, &f);
  655.  
  656.         dndFormatList[i].fmtetc.cfFormat = f.fmtId;
  657.         dndFormatList[i].fmtetc.ptd = NULL;
  658.         dndFormatList[i].fmtetc.dwAspect = DVASPECT_CONTENT;
  659.         dndFormatList[i].fmtetc.tymed = f.fmtMedium & ~BOLE_MED_STATIC;
  660.         dndFormatList[i].fmtetc.lindex = -1;
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.         dndFormatList[i].lpstrFormatName = 
  668.             (f.fmtName[0] ? f.fmtName : TEXT("%s"));
  669.         dndFormatList[i].lpstrResultText =
  670.             (f.fmtResultName[0] ? f.fmtResultName : TEXT("%s"));
  671.             
  672.         if (f.fmtId == BOleDocument::oleEmbdObjClipFmt ||
  673.             f.fmtId == BOleDocument::oleLinkSrcClipFmt ||
  674.             f.fmtId == BOleDocument::oleEmbSrcClipFmt)
  675.  
  676.             //    PASTEONLY and ENABLEICON are mutually exclusive
  677.             //
  678.             dndFormatList[i].dwFlags = OLEUIPASTE_PASTE | OLEUIPASTE_ENABLEICON;
  679.         else
  680.             dndFormatList[i].dwFlags = OLEUIPASTE_PASTEONLY;
  681.  
  682.     }
  683.  
  684.     dndFormat = -1; // prime with "no agreed format"
  685.  
  686.  
  687.     // Ask the data object for its object descriptor
  688.     //
  689.     FORMATETC fmtetc;
  690.     STGMEDIUM tymed;
  691.     fmtetc.cfFormat = BOleDocument::oleObjectDescFmt;
  692.     fmtetc.ptd = 0;
  693.     fmtetc.dwAspect = DVASPECT_CONTENT;
  694.     fmtetc.lindex = -1;
  695.     fmtetc.tymed = TYMED_HGLOBAL;
  696.     
  697.     hr = dataObj->GetData (&fmtetc, &tymed);
  698.  
  699.     if (SUCCEEDED(hr)) {
  700.  
  701.         // Keep this around since not all servers give out object
  702.         // descriptor data, and we wouldn't want to send a bogus
  703.         // size back to the client
  704.         //
  705.         dndInfoAvailable = TRUE;
  706.         
  707.         //    Ask the object descriptor how much screen size it needs
  708.         //
  709.         OBJECTDESCRIPTOR *pOD = (OBJECTDESCRIPTOR*) ::GlobalLock(tymed.hGlobal);
  710.  
  711.         // MSGraph5 fix: object descriptor is garbage
  712.         //
  713.         if (!IsBadReadPtr (pOD, sizeof (OBJECTDESCRIPTOR))) {
  714.  
  715.             // These coordinate transformations produce a rectangle, in
  716.             // client pixel coordinates, which represents the position of
  717.             // the dragged object RELATIVE TO THE HIT POSITION in the
  718.             // original server object.
  719.             //
  720.             dndSize.cx = MAP_LOGHIM_TO_PIX (pOD->sizel.cx, BOleService::pixPerIn.x);
  721.             dndSize.cy = MAP_LOGHIM_TO_PIX (pOD->sizel.cy, BOleService::pixPerIn.y);
  722.  
  723.             dndOffset.x = MAP_LOGHIM_TO_PIX (pOD->pointl.x, BOleService::pixPerIn.x);
  724.             dndOffset.y = MAP_LOGHIM_TO_PIX (pOD->pointl.y, BOleService::pixPerIn.y);
  725.         }
  726.         else 
  727.             SetRect (&dndObjPos, -1, -1, -1, -1);
  728.  
  729.         WIN::GlobalUnlock (tymed.hGlobal);
  730.         OLE::ReleaseStgMedium (&tymed);
  731.         pOD = NULL;
  732.         }
  733.     else {
  734.         SetRect (&dndObjPos, -1, -1, -1, -1);
  735.     }
  736.  
  737.     // Make sure the action implied by 'keyState' is supported
  738.     //
  739.     if (ValidateDrag(keyState, &where, effect, dataObj) >= 0) {
  740.  
  741.         // Find out if the user has dragged over the scroll zone
  742.         //
  743.         BOleScroll scrollDirection = (BOleScroll) 0;
  744.         scrollEnterTime = 0;
  745.         scrollLastTime = 0;
  746.         scrollDirection = DragScrollReqd(&where);
  747.         if (scrollDirection) {
  748.             if (GetScode (pDropDest->Scroll (scrollDirection)) == S_OK)
  749.  
  750.                 // If the app did scroll, tell the server so it can
  751.                 // make the cursor into the drag-scroll bitmap
  752.                 //
  753.                 *effect |= DROPEFFECT_SCROLL;
  754.  
  755.         }
  756.  
  757.         // Get the drag position in container window's client coords
  758.         //
  759.         dndLastPos.x = where.x;
  760.         dndLastPos.y = where.y;
  761.         HWND w = pContainer->GetWindow();
  762.         ::ScreenToClient (w, &dndLastPos);
  763.         
  764.         // Ask the container to give appropriate feedback. This first time
  765.         // through draws the feedback so we're ready for the erase-redraw
  766.         // sequence in DragOver
  767.         //
  768.         CalculateDndObjPos ();
  769.  
  770.         HRESULT hr = NOERROR;
  771.         pDropDest->DragFeedback (&dndLastPos, &dndObjPos, BOLE_MOUSEENTER, dndFormat, hr);
  772.         fDrawn = TRUE;
  773.         if (hr != NOERROR) {
  774.             dndLastFeedbackFailed = TRUE;
  775.             *effect = DROPEFFECT_NONE;
  776.             return hr;
  777.         }
  778.         else
  779.             dndLastFeedbackFailed = FALSE;
  780.  
  781.         // We can return 'ok' as long as ValidateDrag succeeded.
  782.         // It isn't important whether the IContainer accepted Scroll
  783.         //
  784.         OLERET(S_OK);
  785.     }
  786.  
  787.     OLERET(E_FAIL);
  788. }
  789.  
  790. HRESULT _IFUNC BOleDocument::DragOver (DWORD keyState, POINTL where, LPDWORD effect)
  791. {
  792. //    *effect = NULL;
  793. //    return ResultFromScode (E_FAIL);
  794.  
  795.     // Make sure the action implied by 'keyState' is supported
  796.     //
  797.     BOOL fValid = ValidateDrag(keyState, &where, effect) >= 0;
  798.  
  799.     if (fValid) {
  800.         // Find out if the user has dragged over the scroll zone
  801.         //
  802.         BOleScroll scrollDirection = (BOleScroll) 0;
  803.         scrollDirection = DragScrollReqd(&where);
  804.         if (scrollDirection) {
  805.             if (GetScode (pDropDest->Scroll (scrollDirection)) == S_OK)
  806.  
  807.                 // If the app did scroll, tell the server so it can
  808.                 // make the cursor into the drag-scroll bitmap
  809.                 //
  810.                 *effect |= DROPEFFECT_SCROLL;
  811.  
  812.         }
  813.     }
  814.  
  815.     // Do drop-target feedback
  816.     //
  817.  
  818.     HWND w = pContainer->GetWindow();
  819.     POINT newPos;
  820.     newPos.x = where.x;
  821.     newPos.y = where.y;
  822.     ::ScreenToClient (w, &newPos);
  823.     
  824.     if ((dndLastPos.x != newPos.x) || (dndLastPos.y != newPos.y)) {
  825.         if (fDrawn) {
  826.             // Once to erase in the old position
  827.             //
  828.             if (dndInfoAvailable) 
  829.                 CalculateDndObjPos ();
  830.             else
  831.                 SetRect (&dndObjPos, -1, -1, -1, -1);
  832.  
  833.             HRESULT hr = NOERROR;
  834.             pDropDest->DragFeedback (&dndLastPos, &dndObjPos, BOLE_MOUSECONTAINED, dndFormat, hr);
  835.             fDrawn = FALSE;
  836.             if (hr != NOERROR) {
  837.                 dndLastFeedbackFailed = TRUE;
  838.                 *effect = DROPEFFECT_NONE;
  839.                 return hr;
  840.             }
  841.             else
  842.                 dndLastFeedbackFailed = FALSE;
  843.  
  844.         }
  845.  
  846.         if (fValid) { // Once to draw in the new position
  847.             //
  848.             dndLastPos = newPos;
  849.             if (dndInfoAvailable)
  850.                 CalculateDndObjPos ();
  851.             else
  852.                 SetRect (&dndObjPos, -1, -1, -1, -1);
  853.  
  854.             HRESULT hr = NOERROR;
  855.             pDropDest->DragFeedback (&dndLastPos, &dndObjPos, BOLE_MOUSECONTAINED, dndFormat, hr);
  856.             fDrawn = TRUE;
  857.             if (hr != NOERROR) {
  858.                 dndLastFeedbackFailed = TRUE;
  859.                 *effect = DROPEFFECT_NONE;
  860.                 return hr;
  861.             }
  862.             else
  863.                 dndLastFeedbackFailed = FALSE;
  864.         }
  865.     }
  866.     else {
  867.         if (dndLastFeedbackFailed) {
  868.             *effect = DROPEFFECT_NONE;
  869.             return ResultFromScode (E_FAIL);
  870.         }
  871.     }
  872.     if (fValid) {
  873.         // We can return 'ok' as long as ValidateDrag succeeded.
  874.         // It isn't important whether the IContainer accepted Scroll
  875.         //
  876.         OLERET (S_OK);
  877.     }
  878.     else {
  879.         *effect = DROPEFFECT_NONE;
  880.         OLERET (E_FAIL);
  881.     }
  882.  
  883. }
  884.  
  885. HRESULT _IFUNC BOleDocument::DragLeave ()
  886. {
  887.     HRESULT hr = NOERROR;
  888.  
  889.     // Erase feedback at last position
  890.     //
  891.     if (!dndInfoAvailable)
  892.         SetRect (&dndObjPos, -1, -1, -1, -1);
  893.  
  894.     if (fDrawn) {
  895.         pDropDest->DragFeedback (&dndLastPos, &dndObjPos, BOLE_MOUSEEXIT, dndFormat, hr);
  896.         fDrawn = FALSE;
  897.         // dndLastFeedbackFailed shouldn't matter here
  898.     }
  899.  
  900.     // This info is invalid after the DnD transaction
  901.     //
  902.     dndSize.cx = dndSize.cy = 0;
  903.     dndLastPos.x = dndLastPos.y = -1;
  904.     dndInfoAvailable = FALSE;
  905.     if (dndFormatList)
  906.         delete [] dndFormatList;
  907.     dndFormatList = NULL;
  908.  
  909.     return hr;
  910. }
  911.  
  912. HRESULT _IFUNC BOleDocument::Drop (IDataObject *dataObj, DWORD keyState, 
  913.                                    POINTL where, LPDWORD effect)
  914. {
  915.     short agreedFormat = ValidateDrag(keyState, &where, effect, dataObj);
  916.     
  917.     if (agreedFormat >= 0) {
  918.  
  919.         // Find out if the format we agreed on can support static objects.
  920.         // This would be a little more convenient if dndFormatList contained
  921.         // BOleFormats rather than FORMATETCs, but this works out.
  922.         //
  923.         BOOL fStaticOK = FALSE;
  924.         BOleFormat f;
  925.         if (SUCCEEDED(pDropDest->GetFormat(agreedFormat, &f)))
  926.             fStaticOK = f.fmtMedium & BOLE_MED_STATIC;
  927.  
  928.         //    Fill in BOleInitInfo fields dependent on the format
  929.         //
  930.          BOleInitInfo bii;
  931.         bii.pContainer = NULL;
  932.         UINT cf = dndFormatList[agreedFormat].fmtetc.cfFormat;
  933.         if (cf == oleEmbdObjClipFmt || cf == oleEmbSrcClipFmt) {
  934.             bii.Where = BOLE_DATAOBJECT;
  935.             bii.pStorage = NULLP;
  936.             bii.whereData.pData = dataObj;
  937.         }
  938.         else if (cf == oleLinkSrcClipFmt) {
  939.             bii.Where = BOLE_DATAOBJECT;
  940.             bii.pStorage = NULLP;
  941.             bii.whereData.pData = dataObj;
  942.         }
  943.         else if (fStaticOK) {
  944.             bii.How = (cf == CF_METAFILEPICT) ? 
  945.                 BOLE_EMBED_ASMETAFILE : BOLE_EMBED_ASBITMAP;
  946.             bii.Where = BOLE_DATAOBJECT;
  947.             bii.whereData.pData = dataObj;
  948.         }
  949.         else {
  950.             STGMEDIUM medium;
  951.             bii.Where = BOLE_HANDLE;
  952.             bii.whereHandle.dataFormat = cf;
  953.             bii.whereHandle.data = pService->GetDataFromDataObject (
  954.                 dataObj, cf, NULL, DVASPECT_CONTENT, &medium);
  955.  
  956.             bii.pStorage = NULL;
  957.         }
  958.  
  959.         // Fields the same for all dropped objects
  960.         //
  961.         bii.hIcon = NULL;
  962.         if (*effect & DROPEFFECT_COPY || *effect & DROPEFFECT_MOVE)
  963.             bii.How = BOLE_EMBED;
  964.         else
  965.             if (*effect & DROPEFFECT_LINK)
  966.                 bii.How = BOLE_LINK;
  967.  
  968.         // Erase feedback at last position
  969.         //
  970.         if (dndInfoAvailable) 
  971.             CalculateDndObjPos ();
  972.         else
  973.             SetRect (&dndObjPos, -1, -1, -1, -1);    
  974.  
  975.         HRESULT hr = NOERROR;
  976.         if (fDrawn) {
  977.             pDropDest->DragFeedback (&dndLastPos, &dndObjPos, BOLE_MOUSECONTAINED, dndFormat, hr);
  978.             fDrawn = FALSE;
  979.             if (hr != NOERROR) {
  980.                 // dndLastFeedbackFailed shouldn't matter here
  981.                 *effect = DROPEFFECT_NONE;
  982.             }
  983.         }
  984.  
  985.         if (hr == NOERROR)
  986.             hr = pDropDest->Drop (&bii, &dndLastPos, &dndObjPos);
  987.  
  988.         // This info is invalid after the DnD transaction
  989.         //
  990.         dndInfoAvailable = FALSE;
  991.         dndSize.cx = dndSize.cy = 0;
  992.         dndLastPos.x = dndLastPos.y = -1;
  993.         if (dndFormatList)
  994.             delete [] dndFormatList;
  995.         dndFormatList = NULL;
  996.         
  997.         SCODE sc = GetScode (hr);
  998.         if (sc == S_OK || sc == DRAGDROP_S_DROP)
  999.             pContainer->BringToFront();
  1000.         else
  1001.             *effect = DROPEFFECT_NONE;
  1002.  
  1003.         return hr;
  1004.     }
  1005.  
  1006.     *effect = DROPEFFECT_NONE;
  1007.     OLERET (E_FAIL);
  1008. }
  1009.  
  1010. /**************************************************************************
  1011.  
  1012.     IOleUILinkContainer implementation
  1013.  
  1014.     This interface isn't part of OLE2 proper -- it's part of OLE2UI, and
  1015.     is implemented here to allow clients to use the default Links dialog
  1016.     box without building their own. If all the clients do build their own,
  1017.     we could take this implementation out and also remove the BrowseLinks
  1018.     member function of IDocument.
  1019.  
  1020.     The DWORD dwLink parameter to these functions seems to be a magic
  1021.     cookie that the OLE2UI Links dialog passes around to refer to an
  1022.     individual link object. We use a BOlePart* and downcast as necessary.
  1023.  
  1024.     The role of these functions is to translate the OLE2UI IOleUILinkContainer
  1025.     calls into Bolero ILinkInfo calls and delegate them to BOlePart.
  1026.  
  1027. */
  1028.  
  1029. DWORD _IFUNC BOleDocument::GetNextLink (DWORD dwLink)
  1030. {
  1031.     BOlePart *pPart = (BOlePart*) dwLink;
  1032.     if (!pPart)
  1033.  
  1034.         // Start looking at the beginning, rather than relative to another part
  1035.         //
  1036.         if (pFirstPart)
  1037.  
  1038.             // Find the next linked part *including* pFirstPart
  1039.             //
  1040.             return (DWORD) pFirstPart->GetNextLink (TRUE);
  1041.         else
  1042.  
  1043.             // We don't have any parts, linked or otherwise
  1044.             //
  1045.             return 0;
  1046.  
  1047.     // Find the next linked part *after* pPart
  1048.     //
  1049.      return (DWORD) pPart->GetNextLink (FALSE);
  1050. }
  1051.  
  1052. HRESULT _IFUNC BOleDocument::SetLinkUpdateOptions
  1053. (
  1054.     DWORD dwLink,
  1055.     DWORD dwUpdateOpt
  1056. )
  1057. {
  1058.     BOlePart *pPart = (BOlePart*) dwLink;
  1059.     return pPart->UpdateSet ((BOleLinkUpdate)dwUpdateOpt);
  1060. }
  1061.  
  1062. HRESULT _IFUNC BOleDocument::GetLinkUpdateOptions
  1063. (
  1064.     DWORD dwLink,
  1065.     DWORD FAR* lpdwUpdateOpt
  1066. )
  1067. {
  1068.     BOlePart *pPart = (BOlePart*) dwLink;
  1069.     BOleLinkUpdate blu;
  1070.     HRESULT hr = pPart->UpdateGet (&blu);
  1071.     *lpdwUpdateOpt = blu;
  1072.     return hr;
  1073. }
  1074.  
  1075. HRESULT _IFUNC BOleDocument::SetLinkSource
  1076. (
  1077.     DWORD       dwLink,
  1078.     LPOLESTR       lpszDisplayName,
  1079.     ULONG       lenFileName,
  1080.     ULONG FAR*  pchEaten,
  1081.     BOOL        fValidateSource
  1082. )
  1083. {
  1084.     BOlePart *pPart = (BOlePart*) dwLink;
  1085.     return pPart->SourceSet( lpszDisplayName );
  1086. }
  1087.  
  1088. HRESULT _IFUNC BOleDocument::GetLinkSource
  1089. (
  1090.     DWORD       dwLink,
  1091.     LPOLESTR FAR*  lplpszDisplayName,
  1092.     ULONG FAR*  lplenFileName,
  1093.     LPOLESTR FAR*  lplpszFullLinkType,
  1094.     LPOLESTR FAR*  lplpszShortLinkType,
  1095.     BOOL FAR*   lpfSourceAvailable,
  1096.     BOOL FAR*   lpfIsSelected
  1097. )
  1098. {
  1099.     BOlePart *pPart = (BOlePart*) dwLink;
  1100.  
  1101.     *lplpszDisplayName = NULL;
  1102.     *lplpszFullLinkType = NULL;
  1103.     *lplpszShortLinkType = NULL;
  1104.     *lplenFileName = NULL;
  1105.     *lpfSourceAvailable = pPart->GetLinkAvailability();
  1106.     if (lpfIsSelected)
  1107.         *lpfIsSelected = pPart == pFirstPart ? TRUE : FALSE;
  1108.  
  1109.     BOOL ok = pPart->SourceGet (lplpszDisplayName, lplpszFullLinkType,
  1110.                 lplpszShortLinkType, lplenFileName);
  1111.  
  1112.     OLERET(ok ? S_OK : E_FAIL);
  1113. }
  1114.  
  1115. HRESULT _IFUNC BOleDocument::OpenLinkSource (DWORD dwLink)
  1116. {
  1117.     BOlePart *pPart = (BOlePart*) dwLink;
  1118.     return pPart->Activate(TRUE);
  1119. }
  1120.  
  1121. HRESULT _IFUNC BOleDocument::UpdateLink
  1122. (
  1123.     DWORD dwLink,
  1124.     BOOL fErrorMessage,
  1125.     BOOL fErrorAction
  1126. )
  1127. {
  1128.     BOlePart *pPart = (BOlePart*) dwLink;
  1129.     return pPart->UpdateNow ();
  1130. }
  1131.  
  1132. HRESULT _IFUNC BOleDocument::CancelLink (DWORD dwLink)
  1133. {
  1134.     BOlePart *pPart = (BOlePart*) dwLink;
  1135.     return pPart->SourceBreak();
  1136. }
  1137.  
  1138. //**************************************************************************
  1139. //
  1140. // Support functions for other Bolero objects
  1141. //
  1142. //**************************************************************************
  1143.  
  1144. // This OnModalDialog is not part of any Bolero interface.
  1145. // It's called by BOleService because the service doesn't know
  1146. // who the active object is, so the service just routes the call
  1147. // to the active doc to let us figure it out
  1148. //
  1149. HRESULT BOleDocument::OnModalDialog (BOOL fDialogComingActive)
  1150. {
  1151.     if (pActivePart)
  1152.         return pActivePart->EnableModeless (!fDialogComingActive);
  1153.     return NOERROR;
  1154. }
  1155.  
  1156. // This TranslateAccel is not part of any Bolero interface.
  1157. // It's called by BOleService because the service doesn't know
  1158. // who the active object is, so the service just routes the call
  1159. // to the active doc to let us figure it out
  1160. //
  1161. HRESULT BOleDocument::TranslateAccel (LPMSG pMsg)
  1162. {
  1163.     HRESULT hRes = ResultFromScode( S_FALSE );
  1164.     if (pActivePart) {
  1165.         hRes = pActivePart->TranslateAccelerator(pMsg);
  1166.     }
  1167.     return hRes;
  1168. }
  1169.  
  1170. // CacheDragRects is called to precompute the 11-pixel zones
  1171. // around the perimeter of the drop target. Normally, this
  1172. // wouldn't be a big deal to compute on the fly, but the traffic
  1173. // already generated during a drag-and-drop is so time consuming
  1174. // it seems worthwhile to give up a few bytes of instance data
  1175. //
  1176. void _IFUNC BOleDocument::CacheDragRects (LPRECT pR)
  1177. {
  1178.     RECT r = *pR;
  1179.     WIN::SetRect (&rcScrollTop, r.left, r.top, r.right, r.top + 11);
  1180.     WIN::SetRect (&rcScrollTop, r.left, r.top, r.right, r.top + 11);
  1181.      WIN::SetRect (&rcScrollLeft, r.left, r.top, r.left + 11, r.bottom);
  1182.     WIN::SetRect (&rcScrollRight, r.right - 11, r.top, r.right, r.bottom);
  1183.     WIN::SetRect (&rcScrollBottom, r.left, r.bottom - 11, r.right, r.bottom);
  1184. }
  1185.  
  1186. // Accessor function used by BOlePart::RemoveFromList to make sure
  1187. // the head of the list (maintained by BOleDocument) is up-to-date
  1188. //
  1189. void BOleDocument::OnRemovePart (BOlePart *pPart)
  1190. {
  1191.     if (pFirstPart == pPart)
  1192.         pFirstPart = pFirstPart->GetNextPart();
  1193. }
  1194.  
  1195.  
  1196. //*************************************************************************
  1197. //
  1198. // IOleInPlaceFrame implementation
  1199. //
  1200. //*************************************************************************
  1201.  
  1202. HRESULT _IFUNC BOleDocument::InsertMenus (HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
  1203. {
  1204.     OLERET (SUCCEEDED(pContainer->InsertContainerMenus (hmenuShared, (BOleMenuWidths*) lpMenuWidths)) ?
  1205.             S_OK : E_FAIL);
  1206. }
  1207.  
  1208. HRESULT _IFUNC BOleDocument::SetMenu (HMENU hmenuShared, HOLEMENU holeMenu, HWND hwndActiveObject)
  1209. {
  1210.     HRESULT hr = NOERROR;
  1211.  
  1212.     // hmenuShared will be null when the call to BOleDocument::SetMenu is
  1213.     // initiated from BOlePart::OnUIDeactivate. hmenuShared will be valid
  1214.     // when the SetMenu call comes from the real server object
  1215.     //
  1216.     if (hmenuShared)
  1217.         hr = pContainer->SetFrameMenu (hmenuShared);
  1218.  
  1219.     // holemenu will be null when the call to BOleDocument::SetMenu is
  1220.     // initiated from BOlePart::OnUIDeactivate. holeMenu will be valid
  1221.     // when the SetMenu call comes from the real server object
  1222.     //
  1223.     if (SUCCEEDED(hr))
  1224.         hr = OleSetMenuDescriptor (holeMenu, pContainer->GetWindow(),
  1225.             hwndActiveObject, NULL, NULL);
  1226.  
  1227.     return hr;
  1228. }
  1229.  
  1230. HRESULT _IFUNC BOleDocument::RemoveMenus (HMENU hmenuShared)
  1231. {
  1232.  
  1233.  
  1234.  
  1235.     BOOL fNoError = TRUE;
  1236.  
  1237.     // Remove container group menus
  1238.     while (GetMenuItemCount(hmenuShared))
  1239.         fNoError &= RemoveMenu(hmenuShared, 0, MF_BYPOSITION);
  1240.  
  1241.     return fNoError ? NOERROR : ResultFromScode(E_FAIL);
  1242. }
  1243.  
  1244. HRESULT _IFUNC BOleDocument::SetStatusText (LPCOLESTR statusText)
  1245. {
  1246.     // Servers use this function to put text in the container's status bar.
  1247.     // The server is not supposed to negotiate tool space to put their own
  1248.     // status bar at the bottom of the frame window.
  1249.     //
  1250.     pContainer->SetStatusText(statusText);
  1251.     OLERET(S_OK);
  1252. }
  1253.  
  1254. HRESULT _IFUNC BOleDocument::EnableModeless (BOOL fEnable)
  1255. {
  1256.     return pApplication->OnModalDialog (!fEnable);
  1257. }
  1258.  
  1259. HRESULT _IFUNC BOleDocument::TranslateAccelerator (MSG FAR* msg, WORD wID)
  1260. {
  1261.     HWND oldhwnd = msg->hwnd;
  1262.     msg->hwnd = pContainer->GetWindow ();
  1263.     HRESULT hr = pContainer->Accelerator(msg);
  1264.     msg->hwnd = oldhwnd;
  1265.     return hr;
  1266. }
  1267.  
  1268.  
  1269.