home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / OWLSRC.PAK / DOCKING.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  70.3 KB  |  2,609 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1995, 1997 by Borland International, All Rights Reserved
  4. //
  5. // $Revision:   10.36  $
  6. //
  7. // Implementation of docking window classes
  8. //----------------------------------------------------------------------------
  9. #pragma hdrignore SECTION
  10. #include <owl/pch.h>
  11. #if !defined(OWL_DOCKING_H)
  12. # include <owl/docking.h>
  13. #endif
  14. #if !defined(OWL_CONTROLB_H)
  15. # include <owl/controlb.h>
  16. #endif
  17. #if !defined(OWL_TOOLTIP_H)
  18. # include <owl/tooltip.h>
  19. #endif
  20. #if !defined(OWL_UIHELPER_H)
  21. # include <owl/uihelper.h>  // for TUIBorder edge painting
  22. #endif
  23. #if !defined(WINSYS_UIMETRIC_H)
  24. # include <winsys/uimetric.h>
  25. #endif
  26. #if !defined(CLASSLIB_ARRAYS_H)
  27. # include <classlib/arrays.h>
  28. #endif
  29.  
  30. //
  31. // Fillin missing defines for some configurations
  32. //
  33. #if !defined(WM_SIZING)
  34. # define WM_SIZING 0x0214
  35. #endif
  36. #if !defined(DS_SETFOREGROUND)
  37. # define DS_SETFOREGROUND    0x200L  /* not in win3.1 */
  38. #endif
  39.  
  40. #if !defined(SECTION) || SECTION == 1
  41.  
  42. //
  43. // Diagnostic group for docking
  44. //
  45. OWL_DIAGINFO;
  46. DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlDocking, 1, 0);
  47. DIAG_DECLARE_GROUP(OwlCmd);
  48.  
  49. //----------------------------------------------------------------------------
  50.  
  51. //
  52. // Constructor to create a dockable gadget window.
  53. // Sets the appropriate styles for the window.
  54. //
  55. TDockableGadgetWindow::TDockableGadgetWindow(TWindow*        parent,
  56.                                              TTileDirection  direction,
  57.                                              TFont*          font,
  58.                                              TModule*        module)
  59. :
  60.   TGadgetWindow(parent, direction, font, module),
  61.   TDockable()
  62. {
  63. #if 0
  64.   Attr.Style |= WS_BORDER;  // Add in border for boundries between dockables
  65. #else
  66.   Attr.Style &= ~WS_BORDER; // Normally no borders
  67. #endif
  68.   Attr.Style |= WS_CLIPSIBLINGS;
  69.  
  70.   // Always shrink wrapped when dockable since slip fills out docking area
  71.   //
  72.   SetShrinkWrap(true, true);
  73. }
  74.  
  75. //
  76. //
  77. //
  78. TDockableGadgetWindow::~TDockableGadgetWindow()
  79. {
  80. }
  81.  
  82. DEFINE_RESPONSE_TABLE1(TDockableGadgetWindow, TGadgetWindow)
  83.   EV_WM_LBUTTONDOWN,
  84. END_RESPONSE_TABLE;
  85.  
  86. //
  87. // Find out how big this dockable would be in a given location, and with a
  88. // given optional size hint
  89. //
  90. TSize
  91. TDockableGadgetWindow::ComputeSize(TAbsLocation loc, TSize* dim)
  92. {
  93.   TTileDirection dir = GetDirectionForLocation(loc);
  94.  
  95.   // Save current settings to restore when done, since this is a info-request
  96.   // only function
  97.   //
  98.   TTileDirection oldDir = GetDirection();
  99.   int oldWidth = RowWidth;
  100.  
  101.   // If rectangular, calcuate adjust size for surrounding parent frame window
  102.   //
  103.   TSize delta(0,0);
  104.   if (dir == Rectangular) {
  105.     // If we currently are in a floating slip, use that window to calc frame delta
  106.     //
  107.     TWindow* parent = GetParentO();
  108.     if (oldDir == Rectangular && parent && parent->GetHandle()) {
  109.       delta = parent->GetWindowRect().Size() - parent->GetClientRect().Size();
  110.       if (TSystem::Has3dUI()) {
  111.         // In Win95, the bottom margin and the bottom sizing border are the
  112.         // same color and not separated by a hard border, so we 'kern' them
  113.         // together to match the size of the top margin.
  114.         //
  115.         int yt = TUIMetric::CySizeFrame - TUIMetric::CyFixedFrame;
  116.         int topM, leftM, bottomM, rightM;
  117.         GetMargins(Margins, leftM, topM, rightM, bottomM);
  118.         delta.cy -= min(yt, bottomM);
  119.       }
  120.     }
  121.     // No floating parent yet. Calculate the delta based on assumed frame
  122.     // styles. (This rect is only used to size the drag frame anyway.)
  123.     //
  124.     else {
  125.       TRect winRect(0, 0, 0, 0);
  126.       AdjustWindowRectEx(winRect,
  127.                          WS_POPUPWINDOW|WS_VISIBLE|WS_CAPTION|WS_THICKFRAME|WS_DLGFRAME,
  128.                          false, WS_EX_TOOLWINDOW|WS_EX_WINDOWEDGE);
  129.       delta = winRect.Size();
  130.     }
  131.   }
  132.  
  133.   TSize sz(0, 0);
  134.   Direction = dir;
  135.  
  136.   // Seek a vertical size if just a non-zero cy is given
  137.   //
  138.   if (dim && dim->cx == 0 && dim->cy != 0) {
  139.  
  140.     int seekCy = dim->cy - delta.cy;  // This is the height to shoot for
  141.  
  142.     TSize testSz;
  143.     GetDesiredSize(testSz);
  144.  
  145.     // Seek the row whose height is less than seekCy
  146.     //
  147.     if (testSz.cy < seekCy) {
  148.       // Seek a taller row by successivly shrinking the width
  149.       //
  150.       while (1) {
  151.         int prevW = RowWidth;
  152.         sz = testSz;
  153.         RowWidth -= 5;
  154.         if (RowWidth <= 0) {     // Limit reached.  Give up
  155.           RowWidth = testSz.cx;
  156.           sz = testSz;
  157.           break;
  158.         }
  159.         GetDesiredSize(testSz);
  160.         if (seekCy < testSz.cy) { // Shrunk too far: use previous size
  161.           RowWidth = prevW;
  162.           break;
  163.         }
  164.       }
  165.     }
  166.     else if (testSz.cy > seekCy) {
  167.       // Seek a shorter row by successivly growing the width
  168.       //
  169.       while (1) {
  170.         int prevW = RowWidth;
  171.         sz = testSz;
  172.         RowWidth += 5;
  173.         if (RowWidth > 1000) {      // CHNOTE: Limit reached. Give up
  174.           RowWidth = testSz.cx;
  175.           sz = testSz;
  176.           break;
  177.         }
  178.         GetDesiredSize(testSz);
  179.         if (seekCy > testSz.cy) {
  180.           RowWidth = prevW;
  181.           break;
  182.         }
  183.       }
  184.     }
  185.     else // seekCy == testSz.cy
  186.       sz = testSz;
  187.   }
  188.   // Change to a given horizontal size, or just the natural size for the
  189.   // direction
  190.   //
  191.   else {
  192.     if (dim)      // Change the row width here because we are changing size.
  193.       RowWidth = dim->cx - delta.cx;
  194.     GetDesiredSize(sz);
  195.   }
  196.  
  197.   Direction = oldDir;
  198.   RowWidth = oldWidth;
  199.   sz += delta;
  200.  
  201.   return sz;
  202. }
  203.  
  204. //
  205. // Get this dockable's screen rect
  206. //
  207. void
  208. TDockableGadgetWindow::GetRect(TRect& rect)
  209. {
  210.   GetWindowRect(rect);
  211. }
  212.  
  213. //
  214. // Return a gadget window tile direction code given a docking absolute location
  215. // code.
  216. //
  217. TGadgetWindow::TTileDirection
  218. TDockableGadgetWindow::GetDirectionForLocation(TAbsLocation loc)
  219. {
  220.   switch(loc) {
  221.     case alTop:
  222.     case alBottom:
  223.       return Horizontal;
  224.     case alLeft:
  225.     case alRight:
  226.       return Vertical;
  227.     case alNone:
  228.     default:
  229.       return Rectangular;
  230.   }
  231. }
  232.  
  233. //
  234. // Cause this dockable to lay itself out vertical, horizontal or rectangular
  235. //
  236. void
  237. TDockableGadgetWindow::Layout(TAbsLocation loc, TSize* dim)
  238. {
  239.   TGadgetWindow::TTileDirection dir = GetDirectionForLocation(loc);
  240.   if (GetDirection() != dir || dim) {
  241.     if (dim)
  242.       SetRectangularDimensions(dim->cx, dim->cy); // Sets RowWidth mostly
  243.  
  244.     // Set the layout direction, which ends up invoking LayoutSession &
  245.     // SetWindowPos
  246.     //
  247.     SetDirection(dir);
  248.   }
  249. }
  250.  
  251. //
  252. // Return true if the mouse click point is in a spot that should move this
  253. // dockable around.
  254. //
  255. bool
  256. TDockableGadgetWindow::ShouldBeginDrag(TPoint& pt)
  257. {
  258.   TGadget* g = GadgetFromPoint(pt);
  259.   if (g)
  260. #if 1
  261.     return !g->IsVisible(); // Allow hidden gadgets to begin a drag; separators are hidden
  262. #else
  263.     return !g->GetEnabled(); // Allow disabled gadgets to begin a drag
  264. #endif
  265.  
  266.   return true;
  267. }
  268.  
  269. //
  270. // Return the TWindow part of this dockable object. In this case it is just
  271. // this window
  272. //
  273. TWindow*
  274. TDockableGadgetWindow::GetWindow()
  275. {
  276.   return this;
  277. }
  278.  
  279. //
  280. // When laying out a dockable gadget window, if the gadget window changes size
  281. // make sure we tell the dock about it so the dock can resize too.
  282. //
  283. void
  284. TDockableGadgetWindow::LayoutSession()
  285. {
  286.   TSize sz;
  287.   GetDesiredSize(sz);           // Find out how big we'd like to be
  288.  
  289.   Attr.W = sz.cx;               // This will allow LayoutSession to work as if
  290.   Attr.H = sz.cy;               // we were the right size.
  291.  
  292.   TGadgetWindow::LayoutSession();
  293.  
  294.   // If we are in a slip that has been created properly, then tell it to adjust
  295.   // to our new layout
  296.   //
  297.   if (Parent) {
  298.     TFloatingSlip* slip = TYPESAFE_DOWNCAST(Parent,TFloatingSlip);
  299.     if (slip) {
  300.       // If this is a null-sized gadget window, hide our floating frame.
  301.       //
  302.       bool hideSlip = Attr.W == 0 || Attr.H == 0;
  303.       Parent->ShowWindow(hideSlip ? SW_HIDE : SW_SHOWNA);
  304.  
  305.       // Make sure the slip will adjust to our new size
  306.       //
  307.       bool stc = Parent->IsFlagSet(wfShrinkToClient);
  308.       if (!stc)
  309.         Parent->SetFlag(wfShrinkToClient);
  310.       SetWindowPos(0, 0, 0, Attr.W, Attr.H, SWP_NOACTIVATE | SWP_NOMOVE);
  311.       if (!stc)
  312.         Parent->ClearFlag(wfShrinkToClient);
  313.     }
  314.     // If we aren't in a slip (because we haven't been inserted yet) resize
  315.     // anyway.
  316.     //
  317.     else {
  318.       SetWindowPos(0, 0, 0, Attr.W, Attr.H, SWP_NOACTIVATE | SWP_NOMOVE);
  319.     }
  320.   }
  321. }
  322.  
  323. //
  324. // Return the harbor containing the dockable object.
  325. //
  326. THarbor*
  327. TDockableGadgetWindow::GetHarbor()
  328. {
  329.   // If parent is a slip, return its harbor.
  330.   // If parent is the harbor (because the dockable is hidden), return the harbor.
  331.   //
  332.   if (!Parent)
  333.     return 0;
  334.   TDockingSlip* slip = TYPESAFE_DOWNCAST(Parent,TDockingSlip);
  335.   if (slip)
  336.     return slip->GetHarbor();
  337.   THarbor* harbor = TYPESAFE_DOWNCAST(Parent,THarbor);
  338.   return harbor;
  339. }
  340.  
  341. //
  342. // Forward event to slip to allow use to move gadget within the slip
  343. //
  344. void
  345. TDockableGadgetWindow::EvLButtonDown(uint modKeys, TPoint& point)
  346. {
  347.   TPoint pt = point;
  348.   TGadgetWindow::EvLButtonDown(modKeys, point);
  349.  
  350.   // Forward the message to the parent, this is for the docking areas.  To
  351.   // allow moving of the controlbars within the docking areas.
  352.   //
  353.   TWindow* w = GetParentO();
  354.  
  355.   // Check that we're parented to the right window. In OLE server situation,
  356.   // the toolbar could have been reparented to another HWND [i.e. container's
  357.   // window]
  358.   //
  359.   if (w && ::GetParent(*this) != w->GetHandle())
  360.     return;
  361.  
  362.   // Forward to parent [which is a slip]
  363.   //
  364.   if (w && w->GetHandle()) {
  365.     MapWindowPoints(*w, &point, 1);   // Make to parents coordinate system.
  366.     w->HandleMessage(WM_LBUTTONDOWN, modKeys, MkParam2(int16(point.x), int16(point.y)));
  367.   }
  368.  
  369.   // At this point, our window may be in drag mode [i.e. being docked or
  370.   // undocked]. This means that the harbor has invoked 'SetCapture' which in
  371.   // turn implies that we won't see subsequent WM_MOUSEMOVE/WM_LBUTTONDOWN
  372.   // messages. This will leave the tooltip the impression [strong impression
  373.   // if I may add] that the mouse button never came up. The net side-effect
  374.   // will be that the tooltip will not activate until it sees an LBUTTONUP
  375.   // message. So to avoid this side-effect we fake an LBUTTONUP message here.
  376.   //
  377.   if (GetCapture() == *GetHarbor()) {
  378.     TTooltip* tooltip = GetTooltip();
  379.     if (tooltip && tooltip->IsWindow()) {
  380.       MSG msg;
  381.       msg.hwnd = *this;
  382.       msg.message = WM_LBUTTONUP;
  383.       msg.wParam = modKeys;
  384.       msg.lParam = MkUint32(int16(pt.x), int16(pt.y));
  385.       tooltip->RelayEvent(msg);
  386.     }
  387.   }
  388. }
  389.  
  390. //----------------------------------------------------------------------------
  391.  
  392. IMPLEMENT_CASTABLE(TDockableControlBar);
  393.  
  394. //
  395. // Construct a dockable control bar.
  396. //
  397. TDockableControlBar::TDockableControlBar(TWindow*        parent,
  398.                                          TTileDirection  direction,
  399.                                          TFont*          font,
  400.                                          TModule*        module)
  401. :
  402.   TDockableGadgetWindow(parent, direction, font, module)
  403. {
  404.   Margins.Units = TMargins::BorderUnits;
  405.  
  406.   if (Direction == Horizontal) {
  407.     Margins.Left = Margins.Right = TUIMetric::CxSizeFrame + TUIMetric::CxFixedFrame;
  408.     Margins.Top = Margins.Bottom = TUIMetric::CySizeFrame;
  409.   }
  410.   else {
  411.     Margins.Left = Margins.Right = TUIMetric::CxSizeFrame;
  412.     Margins.Top = Margins.Bottom = TUIMetric::CySizeFrame + TUIMetric::CyFixedFrame;
  413.   }
  414.  
  415.   WantTooltip = true;
  416. }
  417.  
  418. //----------------------------------------------------------------------------
  419.  
  420. DEFINE_RESPONSE_TABLE1(TFloatingSlip, TFloatingFrame)
  421.   EV_WM_NCLBUTTONDOWN,
  422.   EV_WM_LBUTTONDOWN,
  423.   EV_WM_CLOSE,
  424.   EV_WM_SIZING,             // Win 4.0 message only.
  425.   EV_WM_WINDOWPOSCHANGING,
  426.   EV_WM_WINDOWPOSCHANGED,
  427.   EV_WM_WININICHANGE,       // WM_SETTINGCHANGE is same ID, new args in 4.x
  428.   EV_WM_GETMINMAXINFO,
  429. END_RESPONSE_TABLE;
  430.  
  431. //
  432. // Construct a floating slip.
  433. // Sets the appropriate style for the window.
  434. //
  435. TFloatingSlip::TFloatingSlip(TWindow*        parent,
  436.                              int x, int y,
  437.                              TWindow*        clientWnd,
  438.                              bool            shrinkToClient,
  439.                              int             captionHeight,
  440.                              bool            popupPalette,
  441.                              TModule*        module)
  442. :
  443.   TFloatingFrame(parent, clientWnd->Title, 0/*clientWnd*/, shrinkToClient,
  444.                  captionHeight, popupPalette, module)
  445. {
  446.   SetDragFrame(false);
  447.  
  448.   Attr.Style &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_SYSMENU);
  449.   Attr.Style &= ~(WS_VISIBLE);    // Create initially hidden, show later
  450.   Attr.Style |= WS_CLIPCHILDREN;  // Dont womp on children
  451.   Attr.Style |= WS_BORDER | WS_THICKFRAME; // Sizeable frame to allow reshaping
  452.  
  453.   // Let tiny caption recalc margin & border dimensions using new styles. Tiny
  454.   // caption will adjust these some more as needed.
  455.   //
  456.   EnableTinyCaption(TFloatingFrame::DefaultCaptionHeight, true);
  457.  
  458.   // Calculate a real initial position, & an estimated size. May resize later
  459.   // when client has reshaped.
  460.   //
  461.   TRect winRect(x, y, x+clientWnd->Attr.W, y+clientWnd->Attr.H);
  462.   AdjustWindowRectEx(winRect, Attr.Style, false, Attr.ExStyle);
  463.  
  464. #if 1
  465.   Attr.X = x;            // Places this frame at given screen x,y
  466.   Attr.Y = y;
  467. #else
  468.   Attr.X = winRect.left; // Places client rect at given screen x,y
  469.   Attr.Y = winRect.top;
  470. #endif
  471.   Attr.W = winRect.Width();
  472.   Attr.H = winRect.Height();
  473. }
  474.  
  475. //
  476. // Handle lbutton down in the caption to begin a dockable drag operation
  477. //
  478. void
  479. TFloatingSlip::EvNCLButtonDown(uint hitTest, TPoint& point)
  480. {
  481.   if (hitTest == HTCAPTION) {
  482.     // Call the dockable dragging setup function in Harbor, passing this
  483.     // window as the dragged dockable and as the docking slip too
  484.     //
  485.     if (Harbor && Harbor->DockDraggingBegin(*this, point, alNone, this))
  486.       return;
  487.   }
  488.   TWindow::EvNCLButtonDown(hitTest, point);
  489. }
  490.  
  491. //
  492. // Handle lbutton down bubbled up from the client to begin a dockable drag
  493. // operation
  494. //
  495. void
  496. TFloatingSlip::EvLButtonDown(uint modKeys, TPoint& point)
  497. {
  498.   // Is the mouseDown in a area where we can move the docked window?
  499.   //
  500.   TWindow* cw = GetClientWindow();
  501.   TDockable* d = TYPESAFE_DOWNCAST(cw, TDockable);
  502.  
  503.   TPoint childPt = point;
  504.   MapWindowPoints(*cw, &childPt, 1);
  505.  
  506.   if (d && d->ShouldBeginDrag(childPt)) {
  507.     MapWindowPoints(0, &point, 1);
  508.     if (Harbor && Harbor->DockDraggingBegin(*this, point, alNone, this))
  509.       return;   // Successfully started
  510.   }
  511.   TWindow::EvLButtonDown(modKeys, point);
  512. }
  513.  
  514. //
  515. // When closing the floating slip, remove any dockable first so it is not
  516. // destroyed.  Dockables are owned by the harbor, not the slip, and must not be
  517. // destroyed when the slip is destroyed.
  518. //
  519. void
  520. TFloatingSlip::EvClose()
  521. {
  522.   TDockable* dd = TYPESAFE_DOWNCAST(GetClientWindow(), TDockable);
  523.   if (dd)
  524.     Harbor->Remove(*dd);  // Will cause a close in DockableRemoved()
  525.   else
  526.     TWindow::EvClose();
  527. }
  528.  
  529. //
  530. // Handle this Win 4.0 message for the best resize user feedback
  531. //
  532. #if !defined(BI_PLAT_WIN16)
  533. bool
  534. TFloatingSlip::EvSizing(uint side, TRect& rect)
  535. #else
  536. bool
  537. TFloatingSlip::EvSizing(uint, TRect&)
  538. #endif
  539. {
  540.  
  541. #if !defined(BI_PLAT_WIN16)
  542.   // Look for size changes & make them track the dockable
  543.   //
  544.   TWindow* w = GetClientWindow();
  545.   if (w) {
  546.     TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
  547.     if (dockable) {
  548.       TSize trackSize(rect.Size());
  549.       switch (side) {
  550.         case WMSZ_BOTTOM:
  551.         case WMSZ_TOP:
  552.            trackSize.cx = 0;
  553.            break;
  554.         case WMSZ_LEFT:
  555.         case WMSZ_RIGHT:
  556.            trackSize.cy = 0;
  557.            break;
  558.       }
  559.       TSize dsz = dockable->ComputeSize(alNone, &trackSize);
  560.  
  561.       // Center the fixed axis
  562.       //
  563.       switch (side) {
  564.         case WMSZ_BOTTOM:
  565.         case WMSZ_TOP:
  566.           rect.left = rect.left + (rect.Width() - dsz.cx)/2;
  567.           break;
  568.         case WMSZ_LEFT:
  569.         case WMSZ_RIGHT:
  570.           rect.top = rect.top + (rect.Height() - dsz.cy)/2;
  571.           break;
  572.       }
  573.  
  574.       // Change the window size, keeping it pinned on the opposite corner
  575.       //
  576.       switch (side) {
  577.         case WMSZ_TOP:
  578.         case WMSZ_TOPRIGHT:
  579.           rect.top = rect.bottom - dsz.cy;
  580.           rect.right = rect.left + dsz.cx;
  581.           break;
  582.         case WMSZ_TOPLEFT:
  583.           rect.left = rect.right - dsz.cx;
  584.           rect.top = rect.bottom - dsz.cy;
  585.           break;
  586.         case WMSZ_BOTTOMLEFT:
  587.         case WMSZ_LEFT:
  588.           rect.left = rect.right - dsz.cx;
  589.           rect.bottom = rect.top + dsz.cy;
  590.           break;
  591.         case WMSZ_BOTTOM:
  592.         case WMSZ_BOTTOMRIGHT:
  593.         case WMSZ_RIGHT:
  594.           rect.right = rect.left + dsz.cx;
  595.           rect.bottom = rect.top + dsz.cy;
  596.           break;
  597.       }
  598.     }
  599.   }
  600. #endif
  601.   return true;
  602. }
  603.  
  604. //
  605. // Handle WM_WINDOWPOSCHANGING to make sure that this frame is properly
  606. // constrained by the dimensions of the dockable client
  607. //
  608. void
  609. TFloatingSlip::EvWindowPosChanging(WINDOWPOS far& windowPos)
  610. {
  611.   //
  612.   //  if (!TSystem::Has3dUI()) {  
  613.  
  614.   TFloatingFrame::EvWindowPosChanging(windowPos);
  615.  
  616.   // Look for size changes & make them track the dockable
  617.   //
  618.   if (!(windowPos.flags&SWP_NOSIZE)) {
  619.     TWindow* w = GetClientWindow();
  620.     if (w) {
  621.       TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
  622.       if (dockable) {
  623.         TSize dsz = dockable->ComputeSize(alNone,
  624.                                           &TSize(windowPos.cx, 0));
  625.  
  626.         // Center the non-sizing axis
  627.         //
  628.         if (!(windowPos.flags&SWP_NOMOVE)) {
  629.             windowPos.y = windowPos.y + (windowPos.cy - dsz.cy)/2;
  630.         }
  631.         windowPos.cx = dsz.cx;
  632.         windowPos.cy = dsz.cy;
  633.       }
  634.     }
  635.   }
  636.   windowPos.flags |= SWP_NOACTIVATE;  // Not very effective, but worth a try
  637. }
  638.  
  639. //
  640. // Handle WM_WINDOWPOSCHANGED to make sure that the dockable client gets a
  641. // chance to do final layout
  642. //
  643. void
  644. TFloatingSlip::EvWindowPosChanged(WINDOWPOS far& windowPos)
  645. {
  646.   TFloatingFrame::EvWindowPosChanged(windowPos);
  647.  
  648.   if (!(windowPos.flags&SWP_NOSIZE)) {
  649.     TWindow* w = GetClientWindow();
  650.     if (w) {
  651.       TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
  652.       if (dockable) {
  653.         TSize dsz = dockable->ComputeSize(alNone,
  654.                                           &TSize(windowPos.cx, 0));
  655.  
  656.         dockable->Layout(alNone, &dsz);
  657.       }
  658.     }
  659.   }
  660. }
  661.  
  662. //
  663. // Event handler for the .INI file changed message.
  664. // Possibly resize because the user has changed the caption size.
  665. //
  666. void
  667. TFloatingSlip::EvWinIniChange(char far* /*section*/)
  668. {
  669.   TWindow* w = GetClientWindow();
  670.  
  671.   // Resize, since our sizing borders may have changed size
  672.   //
  673.   if (w) {
  674.     TRect newWinRect = w->GetWindowRect();
  675.     AdjustWindowRectEx(newWinRect, Attr.Style, false, Attr.ExStyle);
  676.  
  677.     TRect winRect = GetWindowRect();
  678.  
  679.     if (winRect.Height() != newWinRect.Height() ||
  680.         winRect.Width() != newWinRect.Width()) {
  681.       SetWindowPos(0, newWinRect, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
  682.     }
  683.   }
  684. }
  685.  
  686. //
  687. // Respond to WM_GETMINMAXINFO messages to ensure that the minTrackSize
  688. // used by Windows is not greater than the size would be for a vertical
  689. // floating control bar.
  690. //
  691. void
  692. TFloatingSlip::EvGetMinMaxInfo(MINMAXINFO far& info)
  693. {
  694.    TSize sz(0,0);
  695.    ComputeSize( alLeft, &sz );
  696.    info.ptMinTrackSize.x = sz.cx;
  697.    info.ptMinTrackSize.y = sz.cy;
  698. }
  699.  
  700. //
  701. // Overriden TDockingSlip virtual. Called by mouseup handler after a dockable
  702. // is dropped into this docking window.
  703. //
  704. void
  705. TFloatingSlip::DockableInsert(TDockable& dockable,
  706.                               const TPoint* /*topLeft*/,
  707.                               TRelPosition /*position*/, TDockable* /*relDockable*/)
  708. {
  709.   dockable.Layout(alNone);                // No specific dimensions suggested
  710.   SetClientWindow(dockable.GetWindow());  // Set dockable as client & resize
  711.  
  712.   // Retrieve window object of dockable
  713.   //
  714.   TWindow* dockableWindow = dockable.GetWindow();
  715.   CHECK(dockableWindow);
  716.  
  717.   // Let window know it was docked [well, undocked]
  718.   //
  719.   if (dockableWindow->IsWindow())
  720.     dockableWindow->SendMessage(WM_OWLWINDOWDOCKED, TParam1(rpNone),
  721.                                 TParam2((TDockingSlip*)this));
  722. }
  723.  
  724. //
  725. // Overriden TDockingSlip virtual. Called by lbutton up handler after a drag
  726. // within this docking window. This floating slip just moves its whole self
  727. //
  728. void
  729. TFloatingSlip::DockableMove(TDockable& /*dockable*/, const TPoint* topLeft,
  730.                             TRelPosition /*position*/, TDockable* /*relDockable*/)
  731. {
  732.   if (topLeft)
  733.     SetWindowPos(0, topLeft->x, topLeft->y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
  734. }
  735.  
  736. //
  737. // Overriden TDockingSlip virtual. Called by lbutton up handler after a
  738. // dockable has been removed from this docking slip
  739. //
  740. void
  741. TFloatingSlip::DockableRemoved(const TRect& /*orgDragRect*/)
  742. {
  743.   // Clear client window
  744.   //
  745.   SetClientWindow(0);
  746.  
  747.   SendMessage(WM_CLOSE);
  748. }
  749.  
  750. //
  751. // Forward these first four TDockable virtuals to our client dockable
  752. //
  753. TSize
  754. TFloatingSlip::ComputeSize(TAbsLocation loc, TSize* dim)
  755. {
  756.   TWindow* w = GetClientWindow();
  757.   if (w) {
  758.     TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
  759.     if (dockable)
  760.       return dockable->ComputeSize(loc, dim);
  761.   }
  762.   CHECK(w);
  763.   return TSize(0, 0);
  764. }
  765.  
  766. //
  767. // Get this dockable's screen rect
  768. //
  769. void
  770. TFloatingSlip::GetRect(TRect& rect)
  771. {
  772.   GetWindowRect(rect);
  773. }
  774.  
  775. //
  776. // Forward the layout message over to the dockable object.
  777. //
  778. void
  779. TFloatingSlip::Layout(TAbsLocation loc, TSize* dim)
  780. {
  781.   TWindow* w = GetClientWindow();
  782.   if (w) {
  783.     TDockable* dockable = TYPESAFE_DOWNCAST(w, TDockable);
  784.     if (dockable)
  785.       dockable->Layout(loc, dim);  
  786.   }
  787.   CHECK(w);
  788. }
  789.  
  790. //
  791. // Should a given mouse down begin a drag? Never for this dockable.
  792. //
  793. bool
  794. TFloatingSlip::ShouldBeginDrag(TPoint& /*pt*/)
  795. {
  796.   return false;
  797. }
  798.  
  799. //
  800. // Return the TWindow part of this dockable object. In this case it is actually
  801. // the client window
  802. //
  803. TWindow*
  804. TFloatingSlip::GetWindow()
  805. {
  806.   CHECK(GetClientWindow());
  807.   return GetClientWindow();
  808. }
  809.  
  810. //
  811. // Return the associated harbor.
  812. //
  813. THarbor*
  814. TFloatingSlip::GetHarbor()
  815. {
  816.   return Harbor;
  817. }
  818.  
  819. //
  820. // Return the location of the object.
  821. //
  822. TAbsLocation
  823. TFloatingSlip::GetLocation() const
  824. {
  825.   return alNone;
  826. }
  827.  
  828. //----------------------------------------------------------------------------
  829.  
  830. //
  831. // Create a linked list of layouts.
  832. //
  833. template <class T>
  834. TFastList<T>::TFastList(int initSize, int deltaGrowth, bool sorted, bool unique)
  835. :
  836.   SpaceCount(initSize),
  837.   Delta(deltaGrowth),
  838.   EntryCount(0),
  839.   SortEntries(sorted),
  840.   UniqueEntries(unique)
  841. {
  842.   DataPtr = new TDatum[SpaceCount];
  843. }
  844.  
  845. //
  846. // Destructor.
  847. // Cleans up the previously allocated memory.
  848. //
  849. template <class T>
  850. TFastList<T>::~TFastList()
  851. {
  852.   for (int i = 0; i < EntryCount; i++)
  853.     delete DataPtr[i].Object;
  854.   delete[] DataPtr;
  855. }
  856.  
  857. //
  858. // Grow the list by Delta if it is not as big as a given minimum size
  859. //
  860. template <class T> void
  861. TFastList<T>::Grow(int minNewSize)
  862. {
  863.   if (minNewSize > SpaceCount) {
  864.     SpaceCount += Delta;
  865.     TDatum* newPtr = new TDatum[SpaceCount];
  866.     memcpy(newPtr, DataPtr, sizeof(TDatum) * SpaceCount);
  867.  
  868.     delete[] DataPtr;
  869.     DataPtr = newPtr;
  870.   }
  871. }
  872.  
  873. //
  874. // Open up space at a given index (may be past end) and increase the entry
  875. // count
  876. //
  877. template <class T> void
  878. TFastList<T>::OpenUpSpace(int index)
  879. {
  880.   Grow(EntryCount+1);
  881.  
  882.   for (int i = EntryCount-1; i >= index; i--)
  883.     DataPtr[i + 1] = DataPtr[i];
  884.   EntryCount++;
  885. }
  886.  
  887. //
  888. // Close up space at a a given index and reduce the entry count
  889. //
  890. template <class T> void
  891. TFastList<T>::CloseUpSpace(int index)
  892. {
  893.   for (int i = index; i < EntryCount - 1; i++)
  894.     DataPtr[i] = DataPtr[i + 1];
  895.   EntryCount--;
  896. }
  897.  
  898. //
  899. // Add a layout object into the list.
  900. // Makes a copy of the object.
  901. //
  902. template <class T> bool
  903. TFastList<T>::Add(uint32 comparison, T object)
  904. {
  905.   T* newObject = new T(object);  
  906.   return Add(comparison, newObject);
  907. }
  908.  
  909. //
  910. // Add a layout object into the list.
  911. //
  912. template <class T> bool
  913. TFastList<T>::Add(uint32 comparison, T* object)
  914. {
  915.   int i;             // Index of slot to add at
  916.   if (SortEntries) {
  917.     i = 0;
  918.     while (i < EntryCount) {   
  919.       if (comparison >= DataPtr[i].CompareItem)
  920.         i++;
  921.       else
  922.         break;
  923.     }
  924.  
  925.     // Already in list & duplicates not wanted
  926.     //
  927.     if (UniqueEntries && i < EntryCount && DataPtr[i].CompareItem == comparison) {
  928.       return true;  
  929.     }
  930.   }
  931.   else
  932.     i = EntryCount;  // Append to the end
  933.  
  934.   // Open up one space at this location
  935.   //
  936.   OpenUpSpace(i);
  937.  
  938.   DataPtr[i].CompareItem = comparison;
  939.   DataPtr[i].Object = object;
  940.  
  941.   return true;
  942. }
  943.  
  944. //
  945. // Remove the item from the list.
  946. //
  947. template <class T> T*
  948. TFastList<T>::Remove(uint32 comparison)
  949. {
  950.   int i = FindEntry(comparison);
  951.   return (i >= 0) ? RemoveEntry(i) : 0;
  952. }
  953.  
  954. //
  955. // Remove an indexed entry from the list.
  956. //
  957. template <class T> T*
  958. TFastList<T>::RemoveEntry(int index)
  959. {
  960.   if (index < EntryCount) {
  961.     T* object = DataPtr[index].Object;
  962.     CloseUpSpace(index);
  963.     return object;  
  964.   }
  965.   return 0;
  966. }
  967.  
  968. //
  969. // Find a particular entry.
  970. //
  971. template <class T> int
  972. TFastList<T>::FindEntry(uint32 comparison)
  973. {
  974.   for (int i = 0; i < EntryCount; i++)   
  975.     if (DataPtr[i].CompareItem == comparison)
  976.       return i;
  977.   return -1;
  978. }
  979.  
  980. //
  981. // Retrieve the indexed entry.
  982. //
  983. template <class T> T&
  984. TFastList<T>::GetEntry(int index)
  985. {
  986. //  if (index >= EntryCount)
  987.   return *DataPtr[index].Object;
  988. }
  989.  
  990. //
  991. // Clear the entries in the list.
  992. //
  993. template <class T> void
  994. TFastList<T>::Clear()
  995. {
  996.   EntryCount = 0;
  997. }
  998.  
  999. //
  1000. // Fill the list with items of where they should be.
  1001. //
  1002. template <class T> void
  1003. TFastList<T>::Fill(TWindow* parent, TGridType gridType)
  1004. {
  1005.   TWindow* first = parent->GetFirstChild();
  1006.   if (first) {
  1007.     TWindow* w = first;
  1008.     do {
  1009.       T* deco = new T(w,gridType);
  1010.       Add(deco->GetSortKey(), deco);
  1011.       w = w->Next();
  1012.     }
  1013.     while (w != first);
  1014.   }
  1015. }
  1016.  
  1017. //----------------------------------------------------------------------------
  1018.  
  1019. DEFINE_RESPONSE_TABLE1(TEdgeSlip, TWindow)
  1020.   EV_WM_LBUTTONDOWN,
  1021.   EV_WM_LBUTTONDBLCLK,  
  1022.   EV_WM_NCCALCSIZE,
  1023.   EV_WM_NCPAINT,
  1024.   EV_WM_ERASEBKGND,
  1025.   EV_WM_PARENTNOTIFY,
  1026.   EV_WM_WINDOWPOSCHANGING,
  1027. END_RESPONSE_TABLE;
  1028.  
  1029. //
  1030. // Construct an edge slip.
  1031. // Set the approriate styles for the window.
  1032. //
  1033. TEdgeSlip::TEdgeSlip(TDecoratedFrame& parent, TAbsLocation location, TModule* module)
  1034. :
  1035.   TWindow(&parent, "EdgeSlip", module),
  1036.   Location(location),
  1037.   GridType(Location == alTop || Location == alBottom ? YCoord : XCoord)
  1038. {
  1039. #if defined(__TRACE) || defined(__WARN)
  1040.   // Make slip dark blue for debugging purposes in diagnostic build
  1041.   //
  1042.   SetBkgndColor(TColor(0, 0, 128));
  1043. #else
  1044.   SetBkgndColor(TColor::Sys3dFace);
  1045. #endif
  1046.   Attr.Style = (WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
  1047.  
  1048. }
  1049.  
  1050. //
  1051. // Type of decoration
  1052. //
  1053. struct TEachDecoration {
  1054.   int       Top;      // or Left if XCoord
  1055.   int       Bottom;   // or Right if XCoord
  1056.   TWindow*  Window;
  1057.  
  1058.   TEachDecoration(TWindow*, TGridType);
  1059.   TEachDecoration(int Top, int Bottom, TWindow* w);
  1060.   uint32 GetSortKey() const;
  1061. };
  1062.  
  1063. //
  1064. // Create a decoration from the grid.
  1065. //
  1066. TEachDecoration::TEachDecoration(TWindow* w, TGridType gridType)
  1067. :
  1068.   Window(w)
  1069. {
  1070.   TRect rect = w->GetWindowRect();
  1071.   if (gridType == YCoord) {
  1072.     Top = rect.top;
  1073.     Bottom = rect.bottom;
  1074.   }
  1075.   else {
  1076.     Top = rect.left;
  1077.     Bottom = rect.right;
  1078.   }
  1079. }
  1080.  
  1081. //
  1082. // Create the decoration from a specific location.
  1083. //
  1084. TEachDecoration::TEachDecoration(int top, int bottom, TWindow* w)
  1085. :
  1086.   Window(w),
  1087.   Top(top),
  1088.   Bottom(bottom)
  1089. {
  1090. }
  1091.  
  1092. //
  1093. // Return the sort key for the object.
  1094. // It currently uses either the left or the top coordinate.
  1095. //
  1096. uint32
  1097. TEachDecoration::GetSortKey() const
  1098. {
  1099.   return Top;
  1100. }
  1101.  
  1102. //
  1103. // Insure that all decorations in the docking window are abutted against each
  1104. // other (both horizontally and vertically) there should be no gapping holes.
  1105. //
  1106. void
  1107. TEdgeSlip::SetupWindow()
  1108. {
  1109.   TWindow::SetupWindow();                         // Create all children.
  1110.  
  1111.   TFastList<TEachDecoration>  decoList(20, 10, true, true);
  1112.   decoList.Fill(this, GridType);
  1113.  
  1114.   // Look for any gaping holes between the dockable windows and collapse them.
  1115.   // Normally, this doesn't occur unless the to be created size is different
  1116.   // from the actual created size of a gadget. This, currently, only occurs
  1117.   // for TControlGadgets where the gadget is a combobox the created size
  1118.   // specifies the editclass area and the drop-down area, however, the window
  1119.   // rect (when the GetHandle() is valid) is just the editclass area.
  1120.   //
  1121.   int diff = 0;
  1122.   int startWindowChange = -1;
  1123.   int rowHeight = 0;
  1124.   for (int i = 1; i < decoList.Count(); i++) {
  1125.     // Are the two decorations we're going to look at on the same vertical?
  1126.     //
  1127.     if (decoList[i-1].Top != decoList[i].Top) {
  1128.       // No, so compute possible difference.
  1129.       //
  1130.       if (decoList[i-1].Bottom+diff != decoList[i].Top+diff)  {
  1131.         // Remember the first window which is adjusted, we'll want to delay the
  1132.         // SetWindowPos until all windows from this point to the end have been
  1133.         // completely adjusted.
  1134.         //
  1135.         if (startWindowChange == -1)
  1136.           startWindowChange = i;
  1137.  
  1138.         // Found a spot. Compute the offset.
  1139.         //
  1140.         diff += decoList[i-1].Top + rowHeight - decoList[i].Top;
  1141.       }
  1142.       rowHeight = decoList[i].Bottom - decoList[i].Top;
  1143.     }
  1144.     else {
  1145.       rowHeight = max(rowHeight, decoList[i].Bottom - decoList[i].Top);
  1146.     }
  1147.     if (diff) {
  1148.       TEachDecoration& decoItem = decoList[i];
  1149.       decoItem.Top += diff;
  1150.       decoItem.Bottom += diff;
  1151.     }
  1152.   }
  1153.  
  1154.   // Re-adjust all windows which have changed location.
  1155.   //
  1156.   if (startWindowChange != -1) {
  1157.     for (int i = startWindowChange; i < decoList.Count(); i++) {
  1158.       TEachDecoration& decoItem = decoList[i];
  1159.       TRect r;
  1160.       decoItem.Window->GetWindowRect(r);
  1161.  
  1162.       TPoint pt(r.left, r.top);
  1163.       if (GridType == YCoord)
  1164.         pt.y = decoItem.Top;
  1165.       else
  1166.         pt.x = decoItem.Top;
  1167.  
  1168.       // Map from global to local coord (the parent's coordinate space).
  1169.       //
  1170.       ::MapWindowPoints(0, *this, (TPoint*)&pt, 1);
  1171.       decoItem.Window->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1172.                                     SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  1173.       i++;
  1174.     }
  1175.   }
  1176.  
  1177.   // Make sure that our size is updated
  1178.   //
  1179.   ReLayout(true);
  1180. }
  1181.  
  1182. //
  1183. // Change the reciever to be the framewindow not the docking window. The
  1184. // reciever window is normally setup in Activate upon the create of
  1185. // TButtonGadgetEnabler.
  1186. //
  1187. void
  1188. TEdgeSlip::EvCommandEnable(TCommandEnabler& commandEnabler)
  1189. {
  1190.   if (Parent) {
  1191.     // Already being processed?
  1192.     //
  1193.     if (!commandEnabler.IsReceiver(Parent->GetHandle())) {
  1194.       // No, so forward it up to our parent
  1195.       //
  1196.       commandEnabler.SetReceiver(Parent->GetHandle());
  1197.       Parent->EvCommandEnable(commandEnabler);
  1198.     }
  1199.   }
  1200. }
  1201.  
  1202. //
  1203. // Forward command messages to the parent.
  1204. //
  1205. TResult
  1206. TEdgeSlip::EvCommand(uint id, THandle hWndCtl, uint notifyCode)
  1207. {
  1208.   TRACEX(OwlCmd, 1, "TEdgeSlip::EvCommand - id(" << id << "), ctl(" <<\
  1209.                      hex << uint(hWndCtl) << "), code(" << notifyCode  << ")");
  1210.  
  1211.   if (notifyCode == 0 && Parent)
  1212.     return Parent->EvCommand(id, hWndCtl, notifyCode);
  1213.  
  1214.   return TWindow::EvCommand(id, hWndCtl, notifyCode);
  1215. }
  1216.  
  1217. //
  1218. // Forward the left button down message to the dockable object.
  1219. //
  1220. void
  1221. TEdgeSlip::EvLButtonDown(uint modKeys, TPoint& point)
  1222. {
  1223.   TWindow* cw = GetWindowPtr(ChildWindowFromPoint(point));
  1224.  
  1225.   // Only allow immediate children of the docking window to be clicked on.
  1226.   //
  1227.   if (cw && cw->Parent == this) {
  1228.     // Is the mouseDown in a area where we can move the docked window?
  1229.     //
  1230.     TPoint childPt = point;
  1231.     MapWindowPoints(*cw, &childPt, 1);
  1232.     TDockable* d = TYPESAFE_DOWNCAST(cw, TDockable);
  1233.     if (d && d->ShouldBeginDrag(childPt)) {
  1234.       MapWindowPoints(0, &point, 1);
  1235.       if (Harbor && Harbor->DockDraggingBegin(*d, point, Location, this)) {
  1236.         return;          // Successfully started
  1237.       }
  1238.     }
  1239.   }
  1240.   TWindow::EvLButtonDown(modKeys, point);
  1241. }
  1242.  
  1243. //
  1244. // Forward edge slip WM_LBUTONDBLCLICK in case frame wants to customize
  1245. //
  1246. void
  1247. TEdgeSlip::EvLButtonDblClk(uint, TPoint&)
  1248. {
  1249.   Parent->HandleMessage(WM_OWLSLIPDBLCLK);
  1250. }
  1251.  
  1252. //
  1253. // Return the size of our client area, leaving room for the etched separators
  1254. //
  1255. uint
  1256. TEdgeSlip::EvNCCalcSize(bool calcValidRects, NCCALCSIZE_PARAMS far& calcSize)
  1257. {
  1258.   uint ret = TWindow::EvNCCalcSize(calcValidRects, calcSize);
  1259.   if (IsIconic())
  1260.     return ret;
  1261.  
  1262.   // Only add in space if this slip is not shrunk to nothing
  1263.   //
  1264.   if (calcSize.rgrc[0].bottom - calcSize.rgrc[0].top > 0) {
  1265.     if (!(Attr.Style & WS_BORDER)) {
  1266.       if (Location != alBottom)
  1267.         calcSize.rgrc[0].top += 2;
  1268.       if (Location != alTop)
  1269.         calcSize.rgrc[0].bottom -= 2;
  1270.     }
  1271.   }
  1272.   return 0;
  1273. }
  1274.  
  1275. //
  1276. // Erase the background & draw in etched 'borders' within the client area.
  1277. //
  1278. #if defined(BI_PLAT_WIN32)
  1279. void TEdgeSlip::EvNCPaint(HRGN)
  1280. #else
  1281. void TEdgeSlip::EvNCPaint()
  1282. #endif
  1283. {
  1284.   // Non-3d style
  1285.   //
  1286.   if (Attr.Style & WS_BORDER) {
  1287.     DefaultProcessing();
  1288.   }
  1289.   // Use 3-d style
  1290.   //
  1291.   else {
  1292.     TWindowDC dc(*this);
  1293.  
  1294.     // Paint etched line along the top for left, top & right slips, and along
  1295.     // the bottom for bottom, left & right slips to separate from the menubar,
  1296.     // statusbar & eachother.
  1297.     //
  1298.     int height(GetWindowRect().Height());
  1299.     if (Location != alBottom)
  1300.       TUIBorder(TRect(0,0,9999,2), TUIBorder::EdgeEtched, TUIBorder::Top).Paint(dc);
  1301.     if (Location != alTop)
  1302.       TUIBorder(TRect(0,height-2,9999,height), TUIBorder::EdgeEtched, TUIBorder::Bottom).Paint(dc);
  1303.   }
  1304. }
  1305.  
  1306. //
  1307. // Erase the background & draw in etched 'borders' within the client area.
  1308. //
  1309. bool
  1310. TEdgeSlip::EvEraseBkgnd(HDC hDC)
  1311. {
  1312.   TWindow::EvEraseBkgnd(hDC);  // Let TWindow erase everything
  1313.  
  1314.  
  1315.   return true;
  1316. }
  1317.  
  1318. //
  1319. // Make sure that our slip size is updated when a child changes size
  1320. //
  1321. void
  1322. TEdgeSlip::EvParentNotify(uint event,
  1323.                           uint /*childHandleOrX*/, uint /*childIDOrY*/)
  1324. {
  1325.   if (event == WM_SIZE)
  1326.     ReLayout(false);
  1327.   else
  1328.     DefaultProcessing();
  1329. }
  1330.  
  1331. //
  1332. //
  1333. //
  1334. class TDecorationSpan {
  1335. public:
  1336.   int      Left;
  1337.   int      Right;
  1338.   int      Top;
  1339.   int      Bottom;
  1340.   TWindow* Window;
  1341.   bool     Moved;
  1342.  
  1343.   TDecorationSpan(TWindow* w, TGridType);
  1344.   uint32 GetSortKey() const;
  1345.   void Move(TGridType gridType);
  1346. };
  1347.  
  1348. //
  1349. // Create the decoration span from the grid.
  1350. //
  1351. TDecorationSpan::TDecorationSpan(TWindow* w, TGridType gridType)
  1352. {
  1353.   TRect rect = w->GetWindowRect();
  1354.   ::MapWindowPoints(0, w->GetParentH(), (TPoint*)&rect, 2); // map to slip coordinates
  1355.  
  1356.   Window = w;
  1357.   if (gridType == YCoord) {
  1358.     Top = rect.top;
  1359.     Left = rect.left;
  1360.     Right = rect.right;
  1361.     Bottom = rect.bottom;
  1362.   }
  1363.   else {
  1364.     Top = rect.left;
  1365.     Left = rect.top;
  1366.     Right = rect.bottom;
  1367.     Bottom = rect.right;
  1368.   }
  1369. }
  1370.  
  1371. //
  1372. // Return the sorting key of the object.
  1373. //
  1374. uint32
  1375. TDecorationSpan::GetSortKey() const
  1376. {
  1377.   return (Top<<16)+Left;
  1378. }
  1379.  
  1380. //
  1381. // When the slip shrinks, squeeze dockables where needed
  1382. //
  1383. void
  1384. TEdgeSlip::EvWindowPosChanging(WINDOWPOS far& windowPos)
  1385. {
  1386.   if (!(windowPos.flags & SWP_NOSIZE)) {
  1387.     CompressParallel(windowPos.cx);
  1388.   }
  1389.   TWindow::EvWindowPosChanging(windowPos);
  1390. }
  1391.  
  1392. //
  1393. // Squash or expand dockables perpendicular to the grid line
  1394. //
  1395. void
  1396. TEdgeSlip::CompressGridLines()
  1397. {
  1398.   TFastList<TDecorationSpan>  decoList(20, 10, true, true);
  1399.   decoList.Fill(this, GridType);
  1400.  
  1401.   // Tile dockables perpendicular to grid lines
  1402.   //
  1403.   int gridLine = 0;
  1404.   int t;
  1405.   int lastTop = -1;
  1406.   int delta, i;
  1407.   for (i=0; i < decoList.Count(); i++) {
  1408.     decoList[i].Moved = false;
  1409.     t = decoList[i].Top;
  1410.     if (t != lastTop) {
  1411.       delta = gridLine-t;
  1412.       gridLine = 0;
  1413.       lastTop = t;
  1414.     }
  1415.     if (delta != 0) {
  1416.       decoList[i].Top += delta;
  1417.       decoList[i].Bottom += delta;
  1418.       decoList[i].Moved = true;
  1419.     }
  1420.     gridLine = max(gridLine,decoList[i].Bottom);
  1421.   }
  1422.  
  1423.   // Move all dockables that have changed location.
  1424.   //
  1425.   for (i = 0; i < decoList.Count(); i++) {
  1426.     TPoint pt;
  1427.     TDecorationSpan& decoItem = decoList[i];
  1428.     if (decoItem.Moved) {
  1429.       if (GridType == YCoord) {
  1430.         pt.x = decoItem.Left;
  1431.         pt.y = decoItem.Top;
  1432.       }
  1433.       else {
  1434.         pt.x = decoItem.Top;
  1435.         pt.y = decoItem.Left;
  1436.       }
  1437.       decoItem.Window->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1438.                                     SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  1439.     }
  1440.   }
  1441.  
  1442.   ReLayout(false);
  1443. }
  1444.  
  1445. //
  1446. // Compress empty space along Grid line if there is empty space and if
  1447. // any dockables hang out past the end.
  1448. //
  1449. void
  1450. TEdgeSlip::CompressParallel(int width)
  1451. {
  1452.   // scan for dockables that stick off the right side, where there is space to close
  1453.   // up the row.
  1454.   TFastList<TDecorationSpan>  decoList(20, 10, true, true);
  1455.   decoList.Fill(this, GridType);
  1456.  
  1457.   // Scan each row looking for a decoration that hangs off the end.
  1458.   // If possible, pull that decoration in by collapsing empty space between
  1459.   // decorations on that row.
  1460.   //
  1461.   int firstOnRow = 0;
  1462.   int lastOnRow = 0;
  1463.  
  1464.   while (firstOnRow < decoList.Count()) {
  1465.     int spaceOnRow = decoList[firstOnRow].Left;
  1466.     int rowTop = decoList[firstOnRow].Top;
  1467.     while (lastOnRow+1 < decoList.Count() && decoList[lastOnRow+1].Top == rowTop) {
  1468.       spaceOnRow += decoList[lastOnRow+1].Left-(decoList[lastOnRow].Right+1);
  1469.       lastOnRow ++;
  1470.     }
  1471.  
  1472.     int delta = decoList[lastOnRow].Right - width;
  1473.     if (delta > 0 && spaceOnRow > 0) {
  1474.       if (spaceOnRow > delta) {
  1475.         // if there is extra empty space, collapse from the right
  1476.         //
  1477.         int r = width;
  1478.         int i=lastOnRow;
  1479.         while (i >= firstOnRow && (decoList[i].Right+1) > r) {
  1480.           int dx = (decoList[i].Right+1) - r;
  1481.           decoList[i].Left -= dx;
  1482.           decoList[i].Right -= dx;
  1483.           decoList[i].Moved = true;
  1484.           r = decoList[i].Left;
  1485.           i--;
  1486.         }
  1487.       }
  1488.       else {
  1489.         // if there is not extra empty space, just collapse the space
  1490.         //
  1491.         int r = 0;
  1492.         for (int i=firstOnRow; i<=lastOnRow; i++) {
  1493.           if (decoList[i].Left > r) {
  1494.             int dx = decoList[i].Left - r;
  1495.             decoList[i].Left -= dx;
  1496.             decoList[i].Right -= dx;
  1497.             decoList[i].Moved = true;
  1498.             r = decoList[i].Right+1;
  1499.           }
  1500.           else
  1501.             break;
  1502.         }
  1503.       }
  1504.     }
  1505.     firstOnRow = lastOnRow+1;
  1506.   }
  1507.  
  1508.   // Move all dockables that have changed location.
  1509.   //
  1510.   for (int i = 0; i < decoList.Count(); i++) {
  1511.     TPoint pt;
  1512.     TDecorationSpan& decoItem = decoList[i];
  1513.     if (decoItem.Moved) {
  1514.       if (GridType == YCoord) {
  1515.         pt.x = decoItem.Left;
  1516.         pt.y = decoItem.Top;
  1517.       }
  1518.       else {
  1519.         pt.x = decoItem.Top;
  1520.         pt.y = decoItem.Left;
  1521.       }
  1522.       decoItem.Window->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1523.                                     SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  1524.     }
  1525.   }
  1526. }
  1527.  
  1528. //
  1529. // Change the child metrics of this docking window within its decorated frame.
  1530. // Call when the rectangle area of the docking area is different from the
  1531. // computed area of the dockable windows within the docking area.
  1532. //
  1533. void
  1534. TEdgeSlip::ReLayout(bool forcedLayout)
  1535. {
  1536.   // Has the docking area changed in size?
  1537.   //
  1538.   TSize area = ComputeDockingArea();
  1539.  
  1540.   bool sizeChanged;
  1541.   if (Location == alTop || Location == alBottom) {
  1542.     sizeChanged = Attr.H != area.cy;
  1543.     Attr.H = area.cy;
  1544.   }
  1545.   else {
  1546.     sizeChanged = Attr.W != area.cx;
  1547.     Attr.W = area.cx;
  1548.   }
  1549.  
  1550.   // If the size has change or we need to force a layout, then do so
  1551.   //
  1552.   if (sizeChanged || forcedLayout) {
  1553.     TDecoratedFrame* df = TYPESAFE_DOWNCAST(Parent, TDecoratedFrame);
  1554.     CHECK(df);
  1555.  
  1556.     // Set the same metrics just to dirty the layout plan & force a rebuild
  1557.     //
  1558.     TLayoutMetrics  metrics;
  1559.     df->GetChildLayoutMetrics(*this, metrics);
  1560.     df->SetChildLayoutMetrics(*this, metrics);
  1561.  
  1562.     df->Layout();
  1563.   }
  1564. }
  1565.  
  1566. //
  1567. // Overriden TDockingSlip virtual to insert a new dockable into this slip.
  1568. // Called by mouseup handler after a dockable
  1569. // is dropped into this docking window.
  1570. //
  1571. void
  1572. TEdgeSlip::DockableInsert(TDockable& dockable, const TPoint* topLeft,
  1573.                           TRelPosition position, TDockable* relDockable)
  1574. {
  1575.   // Get dockable's window & hide it in case we have to toss it around a bit
  1576.   // Reparent the window to the edge slip
  1577.   //
  1578.   TWindow* dockableWindow = dockable.GetWindow();
  1579.   CHECK(dockableWindow);
  1580.   dockableWindow->ShowWindow(SW_HIDE);
  1581.   dockableWindow->SetParent(this);
  1582.  
  1583.   // Let window know it was docked...
  1584.   //
  1585.   if (dockableWindow->IsWindow())
  1586.     dockableWindow->SendMessage(WM_OWLWINDOWDOCKED, TParam1(position),
  1587.                                 TParam2((TDockingSlip*)this));
  1588.  
  1589.   // Slam dockable over to 0,0 since Move assumes we own it already
  1590.   //
  1591.   dockableWindow->SetWindowPos(0, 0, 0, 0, 0,
  1592.                                SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  1593.  
  1594.   // Make sure that the dockable is oriented the right way--horizontal layout
  1595.   // for Y-gridded slips
  1596.   //
  1597.   dockable.Layout(GridType == YCoord ? alTop : alLeft);
  1598.  
  1599.   DockableMove(dockable, topLeft, position, relDockable);
  1600.   dockableWindow->ShowWindow(SW_SHOWNA);
  1601. }
  1602.  
  1603. //
  1604. // Overriden TDockingSlip virtual. Called by mouseup handler after a drag
  1605. // within this docking window.
  1606. //
  1607. void
  1608. TEdgeSlip::DockableMove(TDockable& dockable, const TPoint* topLeft,
  1609.                         TRelPosition position, TDockable* relDockable)
  1610. {
  1611.   TWindow* dockableWindow = dockable.GetWindow();
  1612.   TRect dockableRect = dockableWindow->GetWindowRect();
  1613.   TSize dockableSize = dockableRect.Size();
  1614.  
  1615.   TPoint dockPos;
  1616.  
  1617.   // If no dockable topleft given, calculate based on relative dockable &
  1618.   // location
  1619.   //
  1620.   if (topLeft) {
  1621.     dockPos = *topLeft;
  1622.   }
  1623.   else {
  1624.     // Figure out the best relative position if none given
  1625.     //
  1626.     if (position == rpNone) switch (Location) {
  1627.       case alTop:
  1628.         position = rpBelow;
  1629.         break;
  1630.       case alBottom:
  1631.         position = rpAbove;
  1632.         break;
  1633.       case alLeft:
  1634.         position = rpRightOf;
  1635.         break;
  1636.       case alRight:
  1637.         position = rpLeftOf;
  1638.         break;
  1639.     }
  1640.  
  1641.     // Get the last child inserted into this docking window and use it as the
  1642.     // relative window if none was given
  1643.     //
  1644.     TWindow* relWindow = 0;
  1645.     if (relDockable)
  1646.       relWindow = relDockable->GetWindow();
  1647.     if (!relWindow)
  1648.       relWindow = GetLastChild();
  1649.  
  1650.     // Get its rect if it exists, or the screen coord of this slip's client area
  1651.     //
  1652.     TRect relWindowRect(0,0,0,0);
  1653.     if (relWindow)
  1654.       relWindowRect = relWindow->GetWindowRect();
  1655.     else
  1656.       ::MapWindowPoints(0, *this, (TPoint*)&relWindowRect, 2);
  1657.  
  1658.     TSize dockableSize = dockableWindow->GetWindowRect().Size();
  1659.     switch (position) {
  1660.       case rpAbove:
  1661.         dockPos = TPoint(relWindowRect.left, relWindowRect.top-dockableSize.cy);
  1662.         break;
  1663.       case rpBelow:
  1664.         dockPos = TPoint(relWindowRect.left, relWindowRect.bottom);
  1665.         break;
  1666.       case rpLeftOf:
  1667.         dockPos = TPoint(relWindowRect.left-dockableSize.cx, relWindowRect.top);
  1668.         break;
  1669.       case rpRightOf:
  1670.         dockPos = TPoint(relWindowRect.right, relWindowRect.top);
  1671.         break;
  1672.     }
  1673.   }
  1674.  
  1675.   // Make a list of the dockables, sorted by y-coord
  1676.   //
  1677.   TFastList<TEachDecoration>  GridList(20, 10, true, true);
  1678.   GridList.Fill(this, GridType);
  1679.  
  1680.   // Find a gridline on which to put the dockable
  1681.   //
  1682.   int dockableEdge;
  1683.   int bottomEdge = 0;
  1684.   if (Location == alTop || Location == alBottom)
  1685.     dockableEdge = dockPos.y;
  1686.   else
  1687.     dockableEdge = dockPos.x;
  1688.  
  1689.   for (int i = 0; i < GridList.Count(); i++) {
  1690.     if (dockableEdge <= (GridList[i].Top+GridList[i].Bottom)/2)
  1691.       break;
  1692.     if (GridList[i].Window != dockableWindow)
  1693.       bottomEdge = max(bottomEdge,GridList[i].Bottom);
  1694.   }
  1695.  
  1696.   // Adjust perpendicular coord to newly found spot
  1697.   //
  1698.   TPoint newPos(dockPos);
  1699.   if (Location == alTop || Location == alBottom) {
  1700.     newPos.y = bottomEdge;
  1701.   }
  1702.   else {
  1703.     newPos.x = bottomEdge;
  1704.   }
  1705.  
  1706.   // Adjust from screen coords, but make sure rect topleft is > 0
  1707.   //
  1708.   ::MapWindowPoints(0, *this, &newPos, 1);
  1709.   if (newPos.x < 0)
  1710.     newPos.x = 0;
  1711.   if (newPos.y < 0)
  1712.     newPos.y = 0;
  1713.  
  1714.   // Position & show the dockable window
  1715.   //
  1716.   dockableWindow->SetWindowPos(0, newPos.x, newPos.y, 0, 0,
  1717.                                SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1718.   dockableWindow->ShowWindow(SW_SHOWNA);
  1719.  
  1720.   // Adjust all windows on the grid line
  1721.   //
  1722.   MoveAllOthers(dockableWindow, MoveDraggedWindow(dockableWindow));
  1723.  
  1724.   // Squash down dockable's previous area if needed
  1725.   //
  1726.   CompressGridLines();
  1727. }
  1728.  
  1729. //
  1730. // Overriden TDockingSlip virtual. Called by mouseup handler after a dockable
  1731. // has been removed from this docking slip
  1732. //
  1733. void
  1734. TEdgeSlip::DockableRemoved(const TRect& /*orgRect*/)
  1735. {
  1736.   CompressGridLines();
  1737. }
  1738.  
  1739. //
  1740. // All terminology in this function is referenced to the following two windows
  1741. // Drag and LeftEdge.  The LeftEdge window is only valid if the Drag windows's
  1742. // left side is intersecting the LeftEdge window's right side.
  1743. //
  1744. //                 ======================
  1745. //    -------------------  Drag Window  |
  1746. //    | LeftEdge Window |================
  1747. //    -------------------
  1748. //
  1749. // The goal of this function is to anchor down the Drag window's left side.
  1750. // This is accomplished by one of three ways:
  1751. //
  1752. //   1. Leave the Drag window where it is; if the Drag window's left side does
  1753. //      not intersect with any other windows (no LeftEdge window).
  1754. //
  1755. //   2. Slide the Drag window to the right of the LeftEdge window being
  1756. //      intersected. This is only done if the intersection is not beyond the
  1757. //      midpoint (less than 50% of the LeftEdge window) of the LeftEdge window.
  1758. //
  1759. //   3. Slide the Drag window to the left side position of the LeftEdge window
  1760. //      (the Drag window's left side is equal to the LeftEdge window's left
  1761. //      side).  The LeftEdge window is then slid to the right side of the Drag
  1762. //      window; if the intersection is beyond the mid point of the left edge
  1763. //      window (more than 50% of the LeftEdge window).
  1764. //
  1765. TWindow*
  1766. TEdgeSlip::MoveDraggedWindow(TWindow* draggedWindow)
  1767. {
  1768.   // Do we want to move the draggedWindow, if so do that.
  1769.   //
  1770.   TRect draggedRect = draggedWindow->GetWindowRect();
  1771.  
  1772.   TWindow* first = GetFirstChild();
  1773.   if (first) {
  1774.     TWindow* w = first;
  1775.     do {
  1776.       if (w != draggedWindow) {
  1777.         TRect currRect = w->GetWindowRect();
  1778.         TPoint pt;
  1779.  
  1780.         if (Location == alTop || Location == alBottom) {
  1781.           if (draggedRect.left >= currRect.left && draggedRect.left <= currRect.right &&
  1782.               currRect.top == draggedRect.top) {
  1783.  
  1784.             int midPt = (currRect.right - currRect.left) / 2;
  1785.  
  1786.             // If the dragged window is obscuring the window underneath by more
  1787.             // than 50% then the dragged window will keep its position and the
  1788.             // underneath window must moved.  Otherwise, the dragged window
  1789.             // will move and the window underneath will move.
  1790.             //
  1791.             if (currRect.left + midPt > draggedRect.left) {
  1792.               // Move the draggedWindow to the same left as the window
  1793.               // underneath.
  1794.               //
  1795.               pt.x = currRect.left;
  1796.               pt.y = draggedRect.top;
  1797.               ::MapWindowPoints(0, *this, &pt, 1);
  1798.               draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1799.                                           SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1800.  
  1801.               // Move the current window to the right of the dragged window.
  1802.               //
  1803.               draggedRect = draggedWindow->GetWindowRect();
  1804.               pt.x = draggedRect.right;
  1805.               pt.y = currRect.top;
  1806.               ::MapWindowPoints(0, *this, &pt, 1);
  1807.               w->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1808.                               SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1809.               return w;
  1810.             }
  1811.             else {
  1812.               // Move the dragged window to the right of the current window (w).
  1813.               //
  1814.               pt.x = currRect.right;
  1815.               pt.y = draggedRect.top;
  1816.               ::MapWindowPoints(0, *this, &pt, 1);
  1817.               draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1818.                                           SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1819.               return draggedWindow;
  1820.             }
  1821.           }
  1822.         }
  1823.         else {
  1824.           // Left and right docking area.
  1825.           //
  1826.           if (draggedRect.top >= currRect.top && draggedRect.top <= currRect.bottom &&
  1827.               currRect.left == draggedRect.left) {
  1828.  
  1829.             int midPt = (currRect.bottom - currRect.top) / 2;
  1830.  
  1831.             // If the dragged window is obscuring the window underneath by more
  1832.             // than 50% then the dragged window will keep its position and the
  1833.             // underneath window must moved. Otherwise, the dragged window will
  1834.             // move and the window underneath will move.
  1835.             //
  1836.             if (currRect.top+midPt > draggedRect.top) {
  1837.               // Move the draggedWindow to the same left as the window underneath.
  1838.               //
  1839.               pt.x = currRect.left;
  1840.               pt.y = draggedRect.top;
  1841.               ::MapWindowPoints(0, *this, &pt, 1);
  1842.               draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1843.                                           SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1844.  
  1845.               // Move the current window to the right of the dragged window.
  1846.               //
  1847.               draggedRect = draggedWindow->GetWindowRect();
  1848.               pt.x = draggedRect.right;
  1849.               pt.y = currRect.top;
  1850.               ::MapWindowPoints(0, *this, &pt, 1);
  1851.               w->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1852.                               SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1853.               return w;
  1854.             }
  1855.             else {
  1856.               // Move the dragged window to the right of the current window (w).
  1857.               //
  1858.               pt.x = currRect.right;
  1859.               pt.y = draggedRect.top;
  1860.               ::MapWindowPoints(0, *this, &pt, 1);
  1861.               draggedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1862.                                           SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1863.               return draggedWindow;
  1864.             }
  1865.           }
  1866.         }
  1867.       }
  1868.  
  1869.       w = w->Next();
  1870.     } while (w != first);
  1871.   }
  1872.  
  1873.   return draggedWindow;
  1874. }
  1875.  
  1876. //
  1877. // Structure to manage the list of windows to slide.
  1878. //
  1879. struct TWindowItem {
  1880.   TWindow* DockedWindow;
  1881.   int      LeftSide;
  1882.  
  1883.   TWindowItem(TWindow* dockedW = 0, int left = 0) : DockedWindow(dockedW),
  1884.                                                     LeftSide(left)
  1885.   {}
  1886.  
  1887.   // operator to satisfy TIArrayAsVector
  1888.   //
  1889.   bool operator == (const TWindowItem& other) {
  1890.     return (other.DockedWindow == DockedWindow) &&
  1891.            (other.LeftSide == LeftSide);
  1892.   }
  1893. };
  1894.  
  1895. typedef TIArrayAsVector<TWindowItem> TWindowItemArray;
  1896. typedef TIArrayAsVectorIterator<TWindowItem> TWindowItemArrayIter;
  1897.  
  1898. //
  1899. // Build a sorted list (sorted by left coordinate) of windows on the same horz.
  1900. // (top/bottom docking) or vert. (left/right docking) of windows to the right
  1901. // or below the dragged window.  Then slide all windows from the first one
  1902. // intersecting the dragged window to the last one in the list.
  1903. //
  1904. void
  1905. TEdgeSlip::MoveAllOthers(TWindow* draggedWindow, TWindow* movedWindow)
  1906. {
  1907.   TWindowItemArray windowList(5, 0, 5);
  1908.  
  1909.   // Include the (upto) two windows which were anchored in MoveDraggedWindow.
  1910.   //
  1911.   TRect draggedRect = draggedWindow->GetWindowRect() | movedWindow->GetWindowRect();
  1912.  
  1913.   TWindow* first = GetFirstChild();
  1914.   if (first) {
  1915.     TWindow* w = first;
  1916.     do {
  1917.       // If the current window is the moved window or the draggedWindow then
  1918.       // grab another window.
  1919.       //
  1920.       if (w != draggedWindow) {
  1921.         TRect currRect = w->GetWindowRect();
  1922.  
  1923.         // Build a list of windows on the same horizontal or vertical after
  1924.         // the dragged window.
  1925.         //
  1926.         if (currRect.top == draggedRect.top && currRect.left >= draggedRect.left) {
  1927.           uint   i = 0;
  1928.           while (i < windowList.GetItemsInContainer()) {
  1929.             if (currRect.left <= windowList[i]->LeftSide)
  1930.               break;
  1931.             i++;
  1932.           }
  1933.  
  1934.           TWindowItem* wItem = new TWindowItem(w, currRect.left); 
  1935.           windowList.AddAt(wItem, i);
  1936.         }
  1937.       }
  1938.  
  1939.       w = w->Next();
  1940.     } while (w != first);
  1941.   }
  1942.  
  1943.   // Are there windows after the dragged window to adjust?
  1944.   //
  1945.   if (windowList.GetItemsInContainer()) {
  1946.     uint i = 0;
  1947.  
  1948.     // Find the first one, all subsequent ones (plus this one) will be slid.
  1949.     //
  1950.     while (i < windowList.GetItemsInContainer()) {
  1951.       if (windowList[i]->LeftSide >= draggedRect.left &&
  1952.           windowList[i]->LeftSide < draggedRect.right)
  1953.         break;
  1954.       i++;
  1955.     }
  1956.  
  1957.     // Slide all windows after the dragged window.
  1958.     //
  1959.     if (i == 0 || i != windowList.GetItemsInContainer()) {
  1960.       TWindow* w1 = draggedWindow;
  1961.       int      offset;
  1962.       TRect    r1, r2;
  1963.       TPoint   pt;
  1964.  
  1965.       while (i < windowList.GetItemsInContainer()) {
  1966.         offset = w1->GetWindowRect().right - windowList[i]->LeftSide;
  1967.  
  1968.         pt.x = windowList[i]->LeftSide + offset;
  1969.         pt.y = windowList[i]->DockedWindow->GetWindowRect().top;
  1970.         ::MapWindowPoints(0, *this, &pt, 1);
  1971.         windowList[i]->DockedWindow->SetWindowPos(0, pt.x, pt.y, 0, 0,
  1972.                                                   SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  1973.         w1 = windowList[i++]->DockedWindow;
  1974.       }
  1975.     }
  1976.   }
  1977.  
  1978. // Iterators cannot be used when modifying the underlying container
  1979. //
  1980. //  TWindowItemArrayIter iter(windowList);
  1981. //  while (iter) {
  1982. //    TWindowItem* item = iter.Current();
  1983. //    windowList.Detach(item);
  1984. //    delete item;
  1985. //    iter++;
  1986. //  }
  1987.  
  1988.   windowList.Flush();
  1989. }
  1990.  
  1991. #if 0
  1992. //
  1993. // Pull every child beyond a given coord in by a given delta (positive),
  1994. //  or push out every child beyond the given coord by the given delta (negative)
  1995. //
  1996. void
  1997. TEdgeSlip::Collapse(int topCoord, int delta)
  1998. {
  1999.   TWindow* first = GetFirstChild();
  2000.   if (first) {
  2001.     TWindow* w = first;
  2002.     do {
  2003.       TRect rect = w->GetWindowRect();
  2004.       if (Location == alTop || Location == alBottom) {
  2005.         if (rect.top > topCoord) {
  2006.           rect.Offset(0, -delta);
  2007.           ::MapWindowPoints(0, *this, (LPPOINT)&rect, 1);
  2008.           w->SetWindowPos(0, rect.left, rect.top, 0, 0,
  2009.                           SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  2010.         }
  2011.       }
  2012.       else {
  2013.         if (rect.left > topCoord) {
  2014.           rect.Offset(-delta, 0);
  2015.           ::MapWindowPoints(0, *this, (LPPOINT)&rect, 1);
  2016.           w->SetWindowPos(0, rect.left, rect.top, 0, 0,
  2017.                           SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
  2018.         }
  2019.       }
  2020.       w = w->Next();
  2021.     } while (w != first);
  2022.   }
  2023. }
  2024. #endif
  2025.  
  2026. //
  2027. // Compute the docking area size based on the dockables inside (total height
  2028. // if top/bottom docking area or total width if left/right docking area).
  2029. // Includes the non-client area etched borders or real window borders
  2030. //
  2031. TSize
  2032. TEdgeSlip::ComputeDockingArea()
  2033. {
  2034.   TRect rect(0, 0, 0, 0);
  2035.  
  2036.   TWindow* first = GetFirstChild();
  2037.   if (first) {
  2038.     TWindow* w = first;
  2039.     do {
  2040.       // Union of all visible areas.
  2041.       //
  2042.       if (w->Attr.Style & WS_VISIBLE)
  2043.         rect |= w->GetWindowRect();
  2044.       w = w->Next();
  2045.     } while (w != first);
  2046.   }
  2047.  
  2048.   // Only add in space if this slip is not shrunk to nothing
  2049.   //
  2050.   if (rect.bottom > rect.top && rect.right > rect.left) {
  2051.     if (!(Attr.Style & WS_BORDER)) { // if 3d style, add space for the etchings
  2052.       if (Location != alBottom)
  2053.         rect.bottom += 2;
  2054.       if (Location != alTop)
  2055.         rect.bottom += 2;
  2056.     }
  2057.     else {
  2058.       AdjustWindowRectEx(rect, Attr.Style, false, Attr.ExStyle);
  2059.     }
  2060.   }
  2061.  
  2062.   return rect.Size();  
  2063. }
  2064.  
  2065. //
  2066. // Return the perpendicular size of the grid line with the given baseCoord.
  2067. //    ... 0 if there are not dockables on the given grid line.
  2068. //
  2069. int
  2070. TEdgeSlip::GridSize(int baseCoord)
  2071. {
  2072.   TWindow* first = GetFirstChild();
  2073.   int h = 0;
  2074.   if (first) {
  2075.     TWindow* w = first;
  2076.     do {
  2077.       if (w->Attr.Style & WS_VISIBLE) {
  2078.         TRect r = w->GetWindowRect();
  2079.  
  2080.         if (GridType == YCoord) {
  2081.           if (baseCoord == r.top) {
  2082.             h = max(h, r.Height());
  2083.           }
  2084.         }
  2085.         else {
  2086.           if (baseCoord == r.left) {
  2087.             h = max(h, r.Width());
  2088.           }
  2089.         }
  2090.       }
  2091.       w = w->Next();
  2092.     } while (w != first);
  2093.   }
  2094.   return h;
  2095. }
  2096.  
  2097. //
  2098. // Retrieve the location of the slip.
  2099. //
  2100. TAbsLocation
  2101. TEdgeSlip::GetLocation() const
  2102. {
  2103.   return Location;
  2104. }
  2105.  
  2106. //----------------------------------------------------------------------------
  2107.  
  2108. DEFINE_RESPONSE_TABLE1(THarbor, TWindow)
  2109.   EV_WM_MOUSEMOVE,
  2110.   EV_WM_LBUTTONDBLCLK,
  2111.   EV_WM_LBUTTONUP,
  2112. END_RESPONSE_TABLE;
  2113.  
  2114. //
  2115. // Create the harbor.
  2116. // The harbor is where the slips can go dock.
  2117. //
  2118. THarbor::THarbor(TDecoratedFrame& df)
  2119. :
  2120.   TWindow(&df, "Harbor"),
  2121.   DecFrame(df)
  2122. {
  2123.   Attr.W = 0;
  2124.   Attr.H = 0;
  2125.   Attr.Style = WS_CHILD;  // not visible
  2126.  
  2127.   SlipTop    = 0;
  2128.   SlipLeft   = 0;
  2129.   SlipBottom = 0;
  2130.   SlipRight  = 0;
  2131.  
  2132.   DragDC        = 0;
  2133.   DragDockable  = 0;
  2134. }
  2135.  
  2136. //
  2137. // Destructor.
  2138. // Currently does nothing.
  2139. //
  2140. THarbor::~THarbor()
  2141. {
  2142. }
  2143.  
  2144. //
  2145. //
  2146. void
  2147. THarbor::SetupWindow()
  2148. {
  2149.   TWindow::SetupWindow();
  2150. }
  2151.  
  2152. //
  2153. // Create an edge slip at a location.
  2154. //
  2155. TEdgeSlip*
  2156. THarbor::ConstructEdgeSlip(TDecoratedFrame& df, TAbsLocation location)
  2157. {
  2158.   return new TEdgeSlip(df, location);
  2159. }
  2160.  
  2161. //
  2162. // Construct a floating slip with a particular window at a location.
  2163. //
  2164. TFloatingSlip*
  2165. THarbor::ConstructFloatingSlip(TDecoratedFrame& df, int x, int y, TWindow* dockableWindow)
  2166. {
  2167.   return new TFloatingSlip(&df, x, y, dockableWindow);
  2168. }
  2169.  
  2170. //
  2171. // Return the slip at the location.
  2172. //
  2173. TEdgeSlip*
  2174. THarbor::GetEdgeSlip(TAbsLocation location)
  2175. {
  2176.   switch (location) {
  2177.     case alTop:
  2178.       return SlipTop;
  2179.     case alBottom:
  2180.       return SlipBottom;
  2181.     case alLeft:
  2182.       return SlipLeft;
  2183.     case alRight:
  2184.       return SlipRight;
  2185.   }
  2186.   return 0;
  2187. }
  2188.  
  2189. //
  2190. // Set a new edge slip for a given location. Also lets the edge slip know
  2191. // who we are.
  2192. //
  2193. void
  2194. THarbor::SetEdgeSlip(TAbsLocation location, TEdgeSlip* slip)
  2195. {
  2196.   slip->SetHarbor(this);
  2197.  
  2198.   switch (location) {
  2199.     case alTop:
  2200.       SlipTop = slip;
  2201.       break;
  2202.     case alBottom:
  2203.       SlipBottom = slip;
  2204.       break;
  2205.     case alLeft:
  2206.       SlipLeft = slip;
  2207.       break;
  2208.     case alRight:
  2209.       SlipRight = slip;
  2210.       break;
  2211.   }
  2212. }
  2213.  
  2214. //
  2215. // Return the default docking relatve position for a given slip location
  2216. //
  2217. TRelPosition
  2218. THarbor::GetSlipPosition(TAbsLocation location)
  2219. {
  2220.   switch (location) {
  2221.     case alTop:
  2222.       return rpBelow;
  2223.     case alBottom:
  2224.       return rpAbove;
  2225.     case alLeft:
  2226.       return rpRightOf;
  2227.     case alRight:
  2228.       return rpLeftOf;
  2229.   }
  2230.   return rpNone;
  2231. }
  2232.  
  2233. //
  2234. // Draw a fast window-type frame rect
  2235. //
  2236. static int fastWindowFrame(TDC& dc, const TRect& r, bool thick, uint32 rop)
  2237. {
  2238.   int thickness = thick ? TUIMetric::CxSizeFrame // a thick
  2239.                         : TUIMetric::CxBorder;   // or a thin frame
  2240.  
  2241.   int x = r.left;
  2242.   int y = r.top;
  2243.   int cx = r.right - x - thickness;
  2244.   int cy = r.bottom - y - thickness;
  2245.  
  2246.   static THatch8x8Brush ditherBrush(THatch8x8Brush::Hatch11F1);
  2247.   if (thick)
  2248.     dc.SelectObject(ditherBrush);
  2249.   else
  2250.     dc.SelectStockObject(WHITE_BRUSH);
  2251.  
  2252.   dc.PatBlt(x, y, thickness, cy, rop);                  // Left
  2253.   dc.PatBlt(x + thickness, y, cx, thickness, rop);      // Top
  2254.   dc.PatBlt(x, y + cy, cx, thickness, rop);             // Bottom
  2255.   dc.PatBlt(x + cx, y + thickness, thickness, cy, rop); // Right
  2256.  
  2257.   return true;
  2258. }
  2259.  
  2260. //
  2261. // Begin a dockable window tracking session. Return true if started OK.
  2262. //
  2263. bool
  2264. THarbor::DockDraggingBegin(TDockable& dockable, TPoint& point,
  2265.                            TAbsLocation location, TDockingSlip* dragNotificatee)
  2266. {
  2267.   // Get dockable to drag, its frame rectangle & the mouse anchor point
  2268.   // Compute relative position of mouse from top/left corner of window.
  2269.   //
  2270.   DragDockable = &dockable;
  2271.   DragDockable->GetRect(DragFrame);
  2272.   DragAnchor = point - DragFrame.TopLeft();
  2273.  
  2274.   // Remember the docking area & original docking area too. Also remember which
  2275.   // docking slip to notify when drag is done.
  2276.   //
  2277.   DragOrgSlipLoc = DragSlipLoc = location;
  2278.   DragNotificatee = dragNotificatee;
  2279.  
  2280.   // Find out the sizes of the dockable in the three orientations:
  2281.   //   Vertical, Horizontal & Natural
  2282.   //
  2283.   DragHSize = DragDockable->ComputeSize(alTop, 0);
  2284.   DragVSize = DragDockable->ComputeSize(alLeft, 0);
  2285.   DragNSize = DragDockable->ComputeSize(alNone, 0);
  2286.  
  2287.   // Calculate the screen relative rectangles for the four docking areas based
  2288.   // on the dec frame's client area.
  2289.   //
  2290.   TRect frameR = DecFrame.GetClientWindow()->GetWindowRect();
  2291.  
  2292.   if (SlipTop)
  2293.     SlipTR = SlipTop->GetWindowRect();
  2294.   else
  2295.     SlipTR.Set(frameR.left, frameR.top, frameR.right, frameR.top);
  2296.  
  2297.   if (SlipBottom)
  2298.     SlipBR = SlipBottom->GetWindowRect();
  2299.   else
  2300.     SlipBR.Set(frameR.left, frameR.bottom, frameR.right, frameR.bottom);
  2301.  
  2302.   if (SlipLeft)
  2303.     SlipLR = SlipLeft->GetWindowRect();
  2304.   else
  2305.     SlipLR.Set(frameR.left, frameR.top, frameR.left, frameR.bottom);
  2306.  
  2307.   if (SlipRight)
  2308.     SlipRR = SlipRight->GetWindowRect();
  2309.   else
  2310.     SlipRR.Set(frameR.right, frameR.top, frameR.right, frameR.bottom);
  2311.  
  2312.   // Capture the mouse & create a screen DC to use while dragging
  2313.   //
  2314.   SetCapture();
  2315.   DragDC = new TScreenDC;
  2316.  
  2317.   // Update dragged window here to avoid asynchronous repaint uglies when
  2318.   // exposed by click. Then paint frame.
  2319.   //
  2320.   DragDockable->GetWindow()->UpdateWindow();
  2321.   fastWindowFrame(*DragDC, DragFrame, DragSlipLoc == alNone, PATINVERT);
  2322.  
  2323.   return true;
  2324. }
  2325.  
  2326. //
  2327. // Handle MouseMove to perform dockable dragging if a drag is in progress
  2328. //
  2329. void
  2330. THarbor::EvMouseMove(uint modKeys, TPoint& point)
  2331. {
  2332.   if (DragDockable && DragDC) {
  2333.     MapWindowPoints(0, &point, 1);
  2334.  
  2335.     // Save un-painting the frame until the end to reduce flicker
  2336.     //
  2337.     TRect        oldDragFrame = DragFrame;
  2338.     TAbsLocation oldDragSlipLoc = DragSlipLoc;
  2339.  
  2340.     // Compute the new horz, vert & natural frames to test for touching of
  2341.     // edge slips & to eventualy update DragRect. Make sure that horz & vert
  2342.     // rects surround mouse, shift as needed.
  2343.     //
  2344.     DragFrame.MoveTo(point.x - DragAnchor.x, point.y - DragAnchor.y);
  2345.  
  2346.     TRect hFrame(DragFrame.TopLeft(), DragHSize);
  2347.     if (!hFrame.Contains(point))
  2348.       hFrame.MoveTo(hFrame.left, point.y - DragHSize.cy/2);
  2349.  
  2350.     TRect vFrame(DragFrame.TopLeft(), DragVSize);
  2351.     if (!vFrame.Contains(point))
  2352.       vFrame.MoveTo(point.x - DragVSize.cx/2, vFrame.top);
  2353.  
  2354.     TRect nFrame(DragFrame.TopLeft(), DragNSize);
  2355.     if (!nFrame.Contains(point))
  2356.       nFrame.Offset(point.x - DragNSize.cx/2 - nFrame.left,
  2357.                     point.y - DragNSize.cy/2 - nFrame.top);
  2358.  
  2359.     if (hFrame.Touches(SlipTR))
  2360.       DragSlipLoc = alTop;
  2361.     else if (hFrame.Touches(SlipBR))
  2362.       DragSlipLoc = alBottom;
  2363.     else if (vFrame.Touches(SlipLR))
  2364.       DragSlipLoc = alLeft;
  2365.     else if (vFrame.Touches(SlipRR))
  2366.       DragSlipLoc = alRight;
  2367.     else
  2368.       DragSlipLoc = alNone;
  2369.  
  2370.     // If over a docking area and it is not the same as the last one, then
  2371.     // re-compute the frame rectangle
  2372.     //
  2373.     if (DragSlipLoc == alNone)
  2374.       DragFrame = nFrame;
  2375.     else if (DragSlipLoc == alTop || DragSlipLoc == alBottom)
  2376.       DragFrame = hFrame;
  2377.     else
  2378.       DragFrame = vFrame;
  2379.  
  2380.  
  2381.     // Unpaint old frame, & paint new frame
  2382.     //
  2383.     fastWindowFrame(*DragDC, oldDragFrame, oldDragSlipLoc == alNone, PATINVERT);
  2384.     fastWindowFrame(*DragDC, DragFrame, DragSlipLoc == alNone, PATINVERT);
  2385.   }
  2386.   else
  2387.     TWindow::EvMouseMove(modKeys, point);
  2388. }
  2389.  
  2390. //
  2391. // Handle mouse up to possibly drop a dockable window being dragged
  2392. //
  2393. void
  2394. THarbor::EvLButtonUp(uint modKeys, TPoint& point)
  2395. {
  2396.   if (DragDockable && DragDC) {
  2397.  
  2398.     // Unpaint frame.
  2399.     //
  2400.     fastWindowFrame(*DragDC, DragFrame, DragSlipLoc == alNone, PATINVERT);
  2401.  
  2402.     ReleaseCapture();
  2403.  
  2404.     TRect orgDragRect;
  2405.     DragDockable->GetRect(orgDragRect);
  2406.  
  2407.     TDockable* dragDockable = DragDockable;
  2408.     DragDockable = 0;
  2409.  
  2410.     delete DragDC;
  2411.     DragDC = 0;
  2412.  
  2413.     // We're docking into a new slip--call the Move function to do the
  2414.     // work.
  2415.     //
  2416.     if (DragOrgSlipLoc != DragSlipLoc) {
  2417.       // Get the real dockable interface for the actual dockable window--
  2418.       // working dockable may have been a helper.
  2419.       //
  2420.       TDockable* dd = TYPESAFE_DOWNCAST(dragDockable->GetWindow(), TDockable);
  2421.       CHECK(dd);
  2422.       Move(*dd, DragSlipLoc, &DragFrame.TopLeft());
  2423.     }
  2424.     // Notifiy docking slip that the dragging resulted in a dockable move.
  2425.     //
  2426.     else {
  2427.       if (DragNotificatee)
  2428.         DragNotificatee->DockableMove(*dragDockable, &DragFrame.TopLeft());
  2429.     }
  2430.   }
  2431.   else
  2432.     TWindow::EvLButtonUp(modKeys, point);
  2433. }
  2434.  
  2435. //
  2436. // Handle the left button double click and forward the message to the
  2437. // dockable window.
  2438. //
  2439. void
  2440. THarbor::EvLButtonDblClk(uint modKeys, TPoint& point)
  2441. {
  2442.   // We get this message when a slip is in drag mode, and mouse clicks are
  2443.   // captured by the harbor.
  2444.   //
  2445.   if (DragDockable) {
  2446.     // If dragging, stop dragging and pass the message to the dragged window.
  2447.     //
  2448.     TWindow* win = DragDockable->GetWindow();
  2449.     EvLButtonUp(modKeys, point);
  2450.     win->HandleMessage(WM_LBUTTONDBLCLK, modKeys, MkParam2(point.x, point.y));
  2451.   }
  2452.   else {
  2453.     TWindow::EvLButtonDblClk(modKeys, point);
  2454.   }
  2455. }
  2456.  
  2457. //
  2458. // Insert a dockable into the appropriate docking area indicate by the given
  2459. // location and either a point or a relative dockable&position
  2460. //
  2461. TDockingSlip*
  2462. THarbor::Insert(TDockable& dockable, TAbsLocation location,
  2463.                 const TPoint* where,
  2464.                 TRelPosition position, TDockable* relDockable)
  2465. {
  2466.   // Get the actual window associated with the dockable
  2467.   //
  2468.   TWindow* dw = dockable.GetWindow();
  2469.  
  2470.   // Making the dockable Floating.
  2471.   //
  2472.   if (location == alNone) {
  2473.     // The 'where' is relative to the decframe's client. Make absolute when
  2474.     // the floatFrame is a popup
  2475.     //
  2476.     TPoint floatWhere;
  2477.     if (where) {
  2478.       floatWhere = *where;
  2479.     }
  2480.     else {
  2481.       // Don't know where to put the floating palette, use CW_USEDEFAULT
  2482.       //
  2483.       floatWhere = TPoint(CW_USEDEFAULT, CW_USEDEFAULT);
  2484.     }
  2485.  
  2486.     // Call out to the virtual to contruct a hidden floating slip
  2487.     //
  2488.     TFloatingSlip* slip = ConstructFloatingSlip(DecFrame,
  2489.                                                 floatWhere.x, floatWhere.y,
  2490.                                                 dw);
  2491.     // Give slip pointer to harbor
  2492.     //
  2493.     slip->SetHarbor(this);
  2494.  
  2495.     // If decorated frame is already created, the create floating slip
  2496.     //
  2497.     if (DecFrame.GetHandle()) {
  2498.       slip->Create();
  2499.       slip->DockableInsert(dockable, where, rpNone, 0);
  2500.       slip->ShowWindow(SW_SHOWNA);
  2501.  
  2502.     } else {
  2503.  
  2504.       // Slip will be created latter (auto-creation) - make sure it's visible
  2505.       //
  2506.       slip->Attr.Style |= WS_VISIBLE;
  2507.       slip->DockableInsert(dockable, where, rpNone, 0);
  2508.     }
  2509.  
  2510.     // Return floating slip
  2511.     //
  2512.     return slip;
  2513.   }
  2514.   else {
  2515.     TEdgeSlip* slip = GetEdgeSlip(location);
  2516.     if (!slip) {
  2517.       // Make an edge slip if not one already for the given edge
  2518.       //
  2519.       slip = ConstructEdgeSlip(DecFrame, location);
  2520.       SetEdgeSlip(location, slip);
  2521.       DecFrame.Insert(*slip, (TDecoratedFrame::TLocation)location);
  2522.  
  2523.       // If frame is created, go ahead and create slip
  2524.       //
  2525.       if (DecFrame.GetHandle())
  2526.         slip->Create();
  2527.     }
  2528.  
  2529.     // Insert the dockable into the slip, based on args given
  2530.     //
  2531.     slip->DockableInsert(dockable, where, position, relDockable);
  2532.  
  2533.     // Return edge slip
  2534.     //
  2535.     return slip;
  2536.   }
  2537. }
  2538.  
  2539. //
  2540. // Move a dockable from one slip to another
  2541. //
  2542. void
  2543. THarbor::Move(TDockable& dockable, TAbsLocation location, const TPoint* where,
  2544.               TRelPosition position, TDockable* relDockable)
  2545. {
  2546.   // Get current slip, assuming it is the parent of the dockable's window
  2547.   //
  2548.   TDockingSlip* previousSlip = 0;
  2549.   TWindow* dockableWindow = dockable.GetWindow();
  2550.   if (dockableWindow) {
  2551.     previousSlip = TYPESAFE_DOWNCAST(dockableWindow->Parent, TDockingSlip);
  2552.   }
  2553.  
  2554.   // If the location hasn't changes, just tweak the position within the slip,
  2555.   // otherwise insert into new & remove from old.
  2556.   //
  2557.   if (previousSlip && previousSlip->GetLocation() == location) {
  2558.     if (where)
  2559.       previousSlip->DockableMove(dockable, where);
  2560.     else
  2561.       ;
  2562.   }
  2563.   else {
  2564.     TRect previousRect;
  2565.     dockable.GetRect(previousRect);
  2566.  
  2567.     // Remove from the previous slip
  2568.     //
  2569.     Remove(dockable);
  2570.  
  2571.     // Insert into the new slip
  2572.     //
  2573.     Insert(dockable, location, where, position, relDockable);
  2574.   }
  2575. }
  2576.  
  2577. //
  2578. // Remove a dockable from the harbor
  2579. //
  2580. void
  2581. THarbor::Remove(TDockable& dockable)
  2582. {
  2583.   // Get the actual window associated with the dockable
  2584.   //
  2585.   TWindow* dockableWindow = dockable.GetWindow();
  2586.  
  2587.   if (dockableWindow) {
  2588.     TRect previousRect;
  2589.     dockable.GetRect(previousRect);
  2590.  
  2591.     // Find the slip containing the dockable
  2592.     //
  2593.     TDockingSlip* previousSlip = TYPESAFE_DOWNCAST(dockableWindow->Parent,
  2594.                                                    TDockingSlip);
  2595.  
  2596.     // Remove the dockable from the slip and reparent it to the harbor
  2597.     //
  2598.     dockableWindow->ShowWindow(SW_HIDE);
  2599.     dockableWindow->SetParent(this);
  2600.  
  2601.     // Notify original docking slip that its dockable was removed.
  2602.     //
  2603.     if (previousSlip)
  2604.       previousSlip->DockableRemoved(previousRect);
  2605.   }
  2606. }
  2607.  
  2608. #endif  // SECTION == 1
  2609.