home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / BOCOLE.PAK / BOLEDOC.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  39.5 KB  |  1,305 lines

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