dockbar.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 103k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "priv.h"
  2. #include "sccls.h"
  3. #include "resource.h"
  4. #include "mshtmhst.h"
  5. #include "inpobj.h"
  6. #include "desktopp.h"   // DTRF_RAISE etc.
  7. #define WANT_CBANDSITE_CLASS
  8. #include "bandsite.h"
  9. #include "deskbar.h"
  10. #include "multimon.h"
  11. #include "mmhelper.h"
  12. #include "theater.h"
  13. #include "mluisupp.h"
  14. #define SUPERCLASS CBaseBar
  15. #define DM_PERSIST      0               // trace IPS::Load, ::Save, etc.
  16. #define DM_POPUI        0               // expando-UI (proto)
  17. #define DM_MENU         0               // trace menu code
  18. #define DM_DRAG         0               // drag move/size (terse)
  19. #define DM_DRAG2        0               // ... (verbose)
  20. #define DM_API          0               // trace API calls
  21. #define DM_HIDE         DM_TRACE               // autohide
  22. #define DM_HIDE2        DM_TRACE               // autohide (verbose)
  23. #define DM_APPBAR       0               // SHAppBarMessage calls
  24. #define DM_OLECT        0               // IOleCommandTarget calls
  25. #define DM_FOCUS        0               // focus change
  26. #define DM_RES          DM_WARNING      // resolution
  27. #define ABS(i)  (((i) < 0) ? -(i) : (i))
  28. #define RECTGETWH(uSide, prc)   (ABE_HORIZ(uSide) ? RECTHEIGHT(*prc) : RECTWIDTH(*prc))
  29. //***   CDB_INITED -- has CDockingBar::_Initialize been called
  30. //
  31. #define CDB_INITED()   (_eInitLoaded && _fInitSited && _fInitShowed)
  32. enum ips_e {
  33.     IPS_FALSE,    // reserved, must be 0 (FALSE)
  34.     IPS_LOAD,
  35.     IPS_LOADBAG,
  36.     IPS_INITNEW,
  37.     IPS_LAST
  38. };
  39. CASSERT(IPS_FALSE == 0);
  40. CASSERT(((IPS_LAST - 1) & 0x03) == (IPS_LAST - 1)); // 2-bit _eInitLoaded
  41. //***   CXFLOAT -- distance from edge to 'float' zone 
  42. // NOTES
  43. //  pls forgive the lousy hungarian...
  44. #define CXFLOAT()   GetSystemMetrics(SM_CXICON)
  45. #define CYFLOAT()   GetSystemMetrics(SM_CYICON)
  46. #define CXYHIDE(uSide)  2       // BUGBUG GetSystemMetrics(xxx)
  47. #ifdef DEBUG
  48. #if 0   // turn on to debug autohide boundary cases
  49. int g_cxyHide = 8;
  50. #undef  CXYHIDE
  51. #define CXYHIDE(uSide)  g_cxyHide
  52. #endif
  53. #endif
  54. #define CXSMSIZE()  GetSystemMetrics(SM_CXSMSIZE)
  55. #define CYSMSIZE()  GetSystemMetrics(SM_CYSMSIZE)
  56. #ifdef DEBUG
  57. extern unsigned long DbStreamTell(IStream *pstm);
  58. extern BOOL DbCheckWindow(HWND hwnd, RECT *prcExp, HWND hwndClient);
  59. TCHAR *DbMaskToMneStr(UINT uMask, TCHAR *szMnemonics);
  60. #else
  61. #define DbStreamTell(pstm) 0
  62. #define DbCheckWindow(hwnd, prcExp, hwndClient) 0
  63. #define DbMaskToMneStr(uMask, szMnemonics) szMnemonics
  64. #endif
  65. //***   autohide -- design note
  66. //
  67. // here's an overview of how we do autohide.  see the code for details.
  68. //
  69. // only a few routines really know about it.  their behavior is driven
  70. // by '_fHiding'.  when FALSE, they behave normally.  when TRUE, they
  71. // do alternate 'fake' behavior.
  72. //
  73. // a 'real' or 'normal' rect is the full-size rect we display when not hidden.
  74. // a 'fake' or 'tiny' rect is the very thin rect we display when hidden.
  75. // (plus there's a '0-width' rect we register w/ the system when we're hidden).
  76. //
  77. // more specifically,
  78. //
  79. // when fHiding is TRUE, a few routines have alternate 'fake' behavior:
  80. //      _ProtoRect      returns a 'tiny' rect rather than the 'real' rect
  81. //      _NegotiateRect  is a NOOP (so we don't change the 'tiny' rect) 
  82. //      _SetVRect       is a NOOP (so we don't save   the 'tiny' rect)
  83. //      AppBarSetPos    is a NOOP (so we don't set    the 'tiny' rect)
  84. // plus, a few routines handle transitions (and setup):
  85. //      _DoHide         hide/unhide helper
  86. //      _MoveSizeHelper detects and handles transitions
  87. //      _HideReg        register autohide appbar w/ 0-width rect
  88. // and finally, a few messages trigger the transitions:
  89. //      unhide          WM_NCHITTEST on the 'tiny' rect starts the unhide.
  90. //              actually it starts a timer (IDT_AUTOUNHIDE) so there's a
  91. //              bit of hysteresis.
  92. //      hide            WM_ACTIVATE(deact) starts a timer (IDT_AUTOHIDE)
  93. //              which we use to poll for mouse leave events.  again, there
  94. //              is some hysteresis, plus some additional heuristics for hiding.
  95. //              WM_ACTIVATE(act) stops the timer.
  96. //
  97. // #if 0
  98. // we also have 'manual hide'.  manual hide differs from autohide as follows:
  99. //     autohide never negotiates space(*)   , manual hide always does
  100. //     autohide is focus- and cursor- driven, manual hide is UI-driven
  101. //     (*) actually it negotiates space of '0'.
  102. // e.g. 'manual hide' is used for the BrowserBar (e.g. search results).
  103. // however for now at least 'manual hide' is simply a ShowDW(FALSE).
  104. // #endif
  105. void CDockingBar::_AdjustToChildSize()
  106. {
  107.     if (_szChild.cx)
  108.     {
  109.         RECT rc, rcChild;
  110.     
  111.         GetWindowRect(_hwnd, &rc);
  112.         GetClientRect(_hwndChild, &rcChild);
  113.         // we need to change rc by the delta of prc-rcChild
  114.         rc.right += _szChild.cx - RECTWIDTH(rcChild);
  115.         rc.bottom += _szChild.cy - RECTHEIGHT(rcChild);
  116.         _SetVRect(&rc);
  117.     
  118.         _Recalc();  // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  119.         _szChild.cx = 0;
  120.     }
  121. }
  122. void CDockingBar::_OnPostedPosRectChange()
  123. {
  124.     if (_ptbSite) {
  125.         if (!_fDragging) {
  126.             _AdjustToChildSize();
  127.         }
  128.     }
  129. }
  130. HMENU CDockingBar::_GetContextMenu()
  131. {
  132.     HMENU hmenu = LoadMenuPopup(MENU_WEBBAR);
  133.     if (hmenu) {
  134.         // _eMode
  135.         if (!ISWBM_DESKTOP())
  136.         {
  137.             EnableMenuItem(hmenu, IDM_AB_TOPMOST, MF_BYCOMMAND | MF_GRAYED);
  138.             EnableMenuItem(hmenu, IDM_AB_AUTOHIDE, MF_BYCOMMAND | MF_GRAYED);
  139.         }
  140.         CheckMenuItem(hmenu, IDM_AB_TOPMOST, WBM_IS_TOPMOST() ? (MF_BYCOMMAND | MF_CHECKED) : (MF_BYCOMMAND | MF_UNCHECKED));
  141.         // hide
  142.         // we use _fWantHide (not _fCanHide) to reflect what user asked
  143.         // for, not what he got.  o.w. you can't tell what the state is
  144.         // unless you actually get it.
  145.         CheckMenuItem(hmenu, IDM_AB_AUTOHIDE,
  146.             MF_BYCOMMAND | (_fWantHide ? MF_CHECKED : MF_UNCHECKED));
  147.         CASSERT(PARENT_XTOPMOST == HWND_DESKTOP);   // for WM_ACTIVATE
  148.         CASSERT(PARENT_BTMMOST() == HWND_DESKTOP);  // for WM_ACTIVATE
  149.         if (_eMode & WBM_FLOATING)
  150.         {
  151.             // (for now) only desktop btm/topmost does autohide
  152.             EnableMenuItem(hmenu, IDM_AB_AUTOHIDE, MF_BYCOMMAND | MF_GRAYED);
  153.         }
  154. #ifdef DEBUG
  155.         // BUGBUG temporary until we make browser tell us about activation
  156.         CheckMenuItem(hmenu, IDM_AB_ACTIVATE,
  157.             MF_BYCOMMAND | (_fActive ? MF_CHECKED : MF_UNCHECKED));
  158. #endif
  159.     }
  160.     return hmenu;
  161. }
  162. HRESULT CDockingBar::_TrackPopupMenu(const POINT* ppt)
  163. {
  164.     HRESULT hres = S_OK;
  165.     HMENU hmenu = _GetContextMenu();
  166.     if (hmenu)
  167.     {
  168.         TrackPopupMenu(hmenu, /*TPM_LEFTALIGN|*/TPM_RIGHTBUTTON,
  169.             ppt->x, ppt->y, 0, _hwnd, NULL);
  170.         DestroyMenu(hmenu);
  171.     }
  172.     else
  173.     {
  174.         hres = E_OUTOFMEMORY;
  175.     }
  176.     return hres;
  177. }
  178. void CDockingBar::_HandleWindowPosChanging(LPWINDOWPOS pwp)
  179. {
  180. }
  181. /***
  182.  */
  183. LRESULT CDockingBar::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  184. {
  185.     LRESULT lres = 0;
  186.     POINT pt;
  187.     DWORD pos;
  188.     RECT rc;
  189.     switch (uMsg) {
  190.     case WM_CLOSE:
  191.         _AppBarOnCommand(IDM_AB_CLOSE);   // _RemoveToolbar(0)
  192.         break;
  193.     case WM_DESTROY:
  194.         if (_fAppRegistered)
  195.             _AppBarRegister(FALSE);
  196.         break;
  197.     case APPBAR_CALLBACK:
  198.         _AppBarCallback(hwnd, uMsg, wParam, lParam);
  199.         return 0;
  200.     case WM_CONTEXTMENU:
  201.         if (_CheckForwardWinEvent(uMsg, wParam, lParam, &lres))
  202.             break;
  203.             
  204.         if ((LPARAM)-1 == lParam)
  205.         {
  206.             GetClientRect(_hwnd, &rc);
  207.             MapWindowRect(_hwnd, HWND_DESKTOP, &rc);
  208.             pt.x = rc.left + (rc.right - rc.left) / 2;
  209.             pt.y = rc.top + (rc.bottom - rc.top) / 2;
  210.         }
  211.         else
  212.         {
  213.             pt.x = GET_X_LPARAM(lParam);
  214.             pt.y = GET_Y_LPARAM(lParam);
  215.         }
  216.         _TrackPopupMenu(&pt);
  217.         break;
  218.     case WM_ENTERSIZEMOVE:
  219.         ASSERT(_fDragging == 0);
  220.         _fDragging = 0;         // reset if busted
  221.         _xyPending = XY_NIL;
  222. #if XXX_CANCEL
  223.         GetWindowRect(hwnd, &_rcCapture);      // to detect cancel
  224. #endif
  225.         break;
  226.     case WM_SYSCHAR:
  227.         if (wParam == TEXT(' '))
  228.         {
  229.             HMENU hmenu;
  230.             hmenu = GetSystemMenu(hwnd, FALSE);
  231.             if (hmenu) {
  232.                 EnableMenuItem(hmenu, SC_RESTORE, MFS_GRAYED | MF_BYCOMMAND);
  233.                 EnableMenuItem(hmenu, SC_MAXIMIZE, MFS_GRAYED | MF_BYCOMMAND);
  234.                 EnableMenuItem(hmenu, SC_MINIMIZE, MFS_GRAYED | MF_BYCOMMAND);
  235.             }
  236.         }
  237.         goto DoDefault;
  238.     case WM_SIZING:     // BUGBUG goto DoDefault?
  239.     case WM_MOVING:
  240.         {
  241.             LPRECT prc = (RECT*)lParam;
  242.             pos = GetMessagePos();
  243.             if (_fDragging == 0)
  244.             {
  245.                 // 1st time
  246.                 _DragEnter(uMsg, GET_X_LPARAM(pos), GET_Y_LPARAM(pos), prc);
  247.                 ASSERT(_fDragging != 0);
  248.             }
  249.             else
  250.             {
  251.                 // 2nd..Nth time
  252.                 _DragTrack(uMsg, GET_X_LPARAM(pos), GET_Y_LPARAM(pos), prc, 0);
  253.             }
  254.         }
  255.         return 1;
  256.     case WM_MOVE:       // xLeft , yTop
  257.     case WM_SIZE:       // xWidth, yHeight
  258.         if (_fDragging)
  259.         {
  260.             RECT rcTmp;
  261.             CopyRect(&rcTmp, &_rcPending);
  262.             _DragTrack(uMsg, GET_X_LPARAM(_xyPending), GET_Y_LPARAM(_xyPending),
  263.                 &rcTmp, 1);
  264.         }
  265.         _OnSize();    // BUGBUG only needed for WM_SIZE?
  266.         break;
  267.         
  268.     case WM_EXITSIZEMOVE:
  269.         _DragLeave(-1, -1, TRUE);
  270.         ASSERT(_fDragging == 0);
  271.         break;
  272.     case WM_CHILDACTIVATE:
  273.         if (_eMode == WBM_BFLOATING)
  274.             SendMessage(_hwnd, WM_MDIACTIVATE, (WPARAM)_hwnd, 0);
  275.         goto DoDefault;
  276.         
  277.     case WM_WINDOWPOSCHANGING:
  278.         _HandleWindowPosChanging((LPWINDOWPOS)lParam);
  279.         break;
  280.     case WM_WINDOWPOSCHANGED:
  281. Lfwdappbar:
  282.         if (_fAppRegistered)
  283.             _AppBarOnWM(uMsg, wParam, lParam);
  284.         goto DoDefault;        // fwd on so we'll get WM_SIZE etc.
  285.     case WM_TIMER:
  286.         switch (wParam) {
  287.         case IDT_AUTOHIDE:
  288.             {
  289.                 ASSERT(_fWantHide && _fCanHide);
  290.                 GetCursorPos(&pt);
  291.                 GetWindowRect(hwnd, &rc);
  292.                 // add a bit of fudge so we don't hide when trying to grab the edge
  293.                 InflateRect(&rc, GetSystemMetrics(SM_CXEDGE) * 4,
  294.                     GetSystemMetrics(SM_CYEDGE)*4);
  295.                 HWND hwndAct = GetActiveWindow();
  296.                 if (!PtInRect(&rc, pt) && hwndAct != hwnd &&
  297.                   (hwndAct == NULL || ::GetWindowOwner(hwndAct) != hwnd))
  298.                   {
  299.                     // to hide, we need to be outside the inflated window,
  300.                     // and we can't be active (for keyboard users).
  301.                     // (heuristics stolen from tray.c)
  302.                     // BUGBUG tray.c also checks TM_SYSMENUCOUNT == 0
  303.                     _DoHide(AHO_KILLDO|AHO_MOVEDO);
  304.                 }
  305.             }
  306.             break;
  307.         case IDT_AUTOUNHIDE:    // BUGBUG share code w/ IDT_AUTOHIDE
  308.             ASSERT(_fWantHide && _fCanHide);
  309.             if (_fHiding)
  310.             {
  311.                 ASSERT(_fHiding == HIDE_AUTO);
  312.                 GetCursorPos(&pt);
  313.                 GetWindowRect(hwnd, &rc);
  314.                 if (PtInRect(&rc, pt))
  315.                     _DoHide(AHO_KILLUN|AHO_MOVEUN|AHO_SETDO);
  316.                 else
  317.         Lkillun:
  318.                     _DoHide(AHO_KILLUN);
  319.             }
  320.             else
  321.             {
  322.                 // if we mouse-over and then TAB very quickly, we can end
  323.                 // up getting a WM_ACT followed by a WM_TIMER (despite the
  324.                 // KillTimer inside OnAct).  if so we need to be careful
  325.                 // not to do an AHO_SETDO.  just to be safe we do an
  326.                 // AHO_KILLUN as well.
  327.                 TraceMsg(DM_HIDE, "cwb.WM_T: !_fHiding (race!) => AHO_KILLUN");
  328.                 goto Lkillun;
  329.             }
  330.             break;
  331.         default:
  332.             goto DoDefault;
  333.         }
  334.         break;
  335.     case WM_NCLBUTTONDOWN:
  336.     case WM_LBUTTONDOWN:
  337.         goto DoDefault;
  338.     case WM_ACTIVATE:
  339.         _OnActivate(wParam, lParam);
  340.         goto Lfwdappbar;
  341.     case WM_GETMINMAXINFO:  // prevent it from getting too small
  342.         // n.b. below stuff works for scheme 'win standard large'
  343.         // but not for v. large edges.  not sure why, but we'll
  344.         // have to fix it or the original bug will still manifest
  345.         // on accessibility-enabled machines.
  346.         // nt5:149535: resize/drag of v. small deskbar.
  347.         // BUGBUG workaround USER hittest bug for v. small windows.
  348.         // DefWndProc(WM_NCHITTEST) gives wrong result (HTLEFT) when
  349.         // window gets too small.  so stop it from getting v. small.
  350.         //
  351.         // the below calc actually gives us slightly *more* than the
  352.         // min size, but what the heck.  e.g. it gives 8+15+1=24,
  353.         // whereas empirical tests give 20.  not sure why there's a
  354.         // diff, but we'll use the bigger # to be safe.
  355.         {
  356.             RECT rcTmp = {100,100,100,100}; // arbitrary 0-sized rect
  357.             LONG ws, wsx;
  358.             HWND hwndTmp;
  359.             _GetStyleForMode(_eMode, &ws, &wsx, &hwndTmp);
  360.             AdjustWindowRectEx(&rcTmp, ws, FALSE, wsx);
  361.             ((MINMAXINFO *)lParam)->ptMinTrackSize.x = RECTWIDTH(rcTmp)  + CXSMSIZE() + 1;
  362.             ((MINMAXINFO *)lParam)->ptMinTrackSize.y = RECTHEIGHT(rcTmp) + CYSMSIZE() + 1;
  363.             if (ISWBM_FLOAT(_eMode))
  364.             {
  365.                 // nt5:169734 'close' button on v. small floating deskbar.
  366.                 // BUGBUG workaround USER 'close' button bug for v. small windows.
  367.                 // the button on a v. small TOOLWINDOW doesn't work.
  368.                 // empirically the below adjustment seems to work.
  369.                 ((MINMAXINFO *)lParam)->ptMinTrackSize.x += (CXSMSIZE() + 1) * 3 / 2;
  370.                 ((MINMAXINFO *)lParam)->ptMinTrackSize.y += (CYSMSIZE() + 1) * 3 / 2;
  371.             }
  372.             TraceMsg(DM_TRACE, "cwb.GMMI: x=%d", ((MINMAXINFO *)lParam)->ptMinTrackSize.x);
  373.         }
  374.         break;
  375.     case WM_NCHITTEST:
  376.         return _OnNCHitTest(wParam, lParam);
  377.     case WM_WININICHANGE:
  378.         // Active Desktop *broadcasts* a WM_WININICHANGE SPI_SETDESKWALLPAPER
  379.         // message when starting up. If this message gets processed during
  380.         // startup at just the right time, then the bands will notify their
  381.         // preferred state, and we lose the persisted state.  Since the desktop
  382.         // wallpaper changing is really of no interest to us, we filter it out here.
  383.         //
  384.         // REVIEW CDTURNER: Would we get a perf win by punting a larger class
  385.         // of these wininichange messages? It seems like most won't affect
  386.         // the contents of a deskbar...
  387.         //
  388.         if (SPI_SETDESKWALLPAPER == wParam)
  389.             break;
  390.         goto DoDefault;
  391.         
  392.     default:
  393. DoDefault:
  394.         return SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
  395.     }
  396.     return lres;
  397. }
  398. LRESULT CDockingBar::_OnNCHitTest(WPARAM wParam, LPARAM lParam)
  399. {
  400.     if (_fHiding)
  401.         _DoHide(AHO_SETUN);
  402.     // get 'pure' hittest...
  403.     LRESULT lres = _CalcHitTest(wParam, lParam);
  404.     // ... and perturb it based on where we're docked
  405.     BOOL fSizing = FALSE;
  406.     if (ISWBM_FLOAT(_eMode)) {
  407.         // standard sizing/moving behavior
  408.         return lres;
  409.     }
  410.     else {
  411.         // opposing edge sizes; any other edge moves
  412.         ASSERT(ISABE_DOCK(_uSide));
  413.         switch (_uSide) {
  414.         case ABE_LEFT:
  415.             //  
  416.             // Mirror the edges (since we are dealing with screen coord)
  417.             // if the docked-window parent is mirrored. [samera]
  418.             //
  419.             if (IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd)))
  420.                 fSizing = (lres==HTLEFT);
  421.             else
  422.                 fSizing = (lres==HTRIGHT);
  423.             break;
  424.         case ABE_RIGHT:
  425.             //  
  426.             // Mirror the edges (since we are dealing with screen coord)
  427.             // if the docked-window parent is mirrored.
  428.             //
  429.             if (IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd)))
  430.                 fSizing = (lres==HTRIGHT);
  431.             else
  432.                 fSizing = (lres==HTLEFT);
  433.             break;
  434.         case ABE_TOP:
  435.             fSizing = (lres==HTBOTTOM);
  436.             break;
  437.         case ABE_BOTTOM:
  438.             fSizing = (lres==HTTOP);
  439.             break;
  440.         default: 
  441.             ASSERT(0); 
  442.             break;
  443.         }
  444.     }
  445.     if (!fSizing) {
  446.         lres = HTCAPTION;
  447.     }
  448.     return lres;
  449. }
  450. //***   _OnActivate --
  451. //
  452. void CDockingBar::_OnActivate(WPARAM wParam, LPARAM lParam)
  453. {
  454.     TraceMsg(DM_HIDE, "cwb.WM_ACTIVATE wParam=%x", wParam);
  455.     if (_fCanHide) {
  456.         ASSERT(_fHiding != HIDE_MANUAL);
  457.         if (LOWORD(wParam) != WA_INACTIVE) {
  458.             // activate
  459.             TraceMsg(DM_HIDE, "cdb._oa:  WM_ACT(act) _fHiding=%d", _fHiding);
  460.             // turn off timers for perf
  461.             // nash:40992: unhide if hidden (e.g. TABed to hidden)
  462.             _DoHide(AHO_KILLDO|AHO_MOVEUN);
  463.         }
  464.         else {
  465.             // deactivate
  466.             _DoHide(AHO_SETDO);         // restore
  467.         }
  468.     }
  469.     return;
  470. }
  471. /***
  472.  */
  473. CDockingBar::CDockingBar() : _eMode(WBM_NIL), _uSide(ABE_RIGHT)
  474. {
  475.     ASSERT(_fIdtUnHide == FALSE);
  476.     ASSERT(_fIdtDoHide == FALSE);
  477.     _ptIdtUnHide.x = _ptIdtUnHide.y = -1;
  478.     // set up worst-case defaults.  we'll end up using them for:
  479.     //     - some of them for Load(bag)
  480.     //     - all  of them for InitNew()
  481.     // note that we might call _InitPos4 again in SetSite.
  482.     _InitPos4(TRUE);
  483.     return;
  484. }
  485. //***   _Initialize -- 2nd-phase ctor
  486. // NOTES
  487. //  we need any IPS::Load settings and also a site before we can init
  488. //  ourself, so most initialization waits until here.
  489. void CDockingBar::_Initialize()
  490. {
  491.     ASSERT(!_fInitShowed);
  492.     ASSERT(_fInitSited && _eInitLoaded);
  493.     ASSERT(!CDB_INITED());
  494.     _fInitShowed = TRUE;
  495.     // warning: delicate phase-ordering here...
  496.     UINT eModeNew = _eMode;
  497.     _eMode = WBM_NIL;
  498.     UINT uSideNew = _uSide;
  499.     _uSide = ABE_NIL;
  500.     HMONITOR hMonNew = _hMon;
  501.     _hMon = NULL;
  502.     // 48463: beta reports fault on boot when we have deskbar+taskbar on
  503.     // same edge (non-merged).  i'm guessing (no proof) that shdocvw isn't
  504.     // init'ed enough early on during boot to handle doing a MergeBS, or
  505.     // alternately that there's a race btwn the tray and desktop threads.
  506.     //
  507.     // plus in any case we shouldn't do the merge just because the guy did
  508.     // a logoff/logon!
  509.     _SetModeSide(eModeNew, uSideNew, hMonNew, /*fNoMerge*/_eInitLoaded == IPS_LOAD);
  510.     _NotifyModeChange(0);
  511.     // if we have a bar on the right and we drag a band from it to
  512.     // the top, we end up getting a sequence:
  513.     //      create deskbar; AddBand; SetSite; _Initialize
  514.     // the AddBand of the (1st) band tries to do an autosize but
  515.     // there's no site yet, so nothing happens.
  516.     //
  517.     // so we need to force it here.
  518.     _AdjustToChildSize();
  519.     if (_fWantHide) {
  520.         _fWantHide = FALSE;
  521.         _AppBarOnCommand(IDM_AB_AUTOHIDE);
  522.     }
  523.     ASSERT(CDB_INITED());
  524.     return;
  525. }
  526. /***
  527.  */
  528. CDockingBar::~CDockingBar()
  529. {
  530.     ASSERT(!_fAppRegistered);   // make sure _ChangeTopMost(WBM_NIL) was called
  531.     // make sure SetSite(NULL); was called
  532.     ASSERT(!_ptbSite);
  533.     return;
  534. }
  535. void CDockingBar::_GetChildPos(LPRECT prc)
  536. {
  537.     GetClientRect(_hwnd, prc);
  538. }
  539. //***   _OnSize -- compute size for OC, leaving room for toolbar (caption?)
  540. //
  541. void CDockingBar::_OnSize(void)
  542. {
  543.     RECT rc;
  544.     if (!_hwndChild || !_eInitLoaded)
  545.         return;
  546.     ASSERT(IsWindow(_hwndChild));
  547.     // don't resize on a hide (it's temporary and we don't want things
  548.     // to jerk around or worse still do a destructive reformat)
  549.     // BUGBUG: should suppress resizing here in theater mode autohide
  550.     // too (see theater.cpp)
  551.     if (_fHiding)
  552.         return;
  553.     _GetChildPos(&rc);
  554.     // (used to do ISWBM_EDGELESS 'fake edge' adjustments here, someone
  555.     // nuked them, but should be o.k. now that visuals are frozen *provided*
  556.     // we don't go back to edgeless)
  557.     SetWindowPos(_hwndChild, 0,
  558.             rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
  559.             SWP_NOACTIVATE|SWP_NOZORDER);
  560.     //ASSERT(DbCheckWindow(_hwndChild, &rc, xxx));
  561. }
  562. //***   _CalcHitTest --
  563. // NOTES
  564. //      really only has to return an int (win16?)
  565. LRESULT CDockingBar::_CalcHitTest(WPARAM wParam, LPARAM lParam)
  566. {
  567.     LRESULT lRet;
  568.     if (!(ISWBM_BOTTOM(_eMode) && ISWBM_EDGELESS(_eMode))) {
  569.         // For non-btmmost, we can ask USER to perform the default
  570.         // hit testing.
  571.         lRet = DefWindowProcWrap(_hwnd, WM_NCHITTEST, wParam, lParam);
  572.     } else {
  573.         // For btmmost, we need to do it.
  574.         // (possibly dead code if bottom is never edgeless)
  575.         // (if so, compiler should optimize it out)
  576.         //TraceMsg(DM_WARNING, "cdb.ro: edgeless!");
  577.         RECT rc;
  578.         GetWindowRect(_hwnd, &rc);
  579.         UINT x = GET_X_LPARAM(lParam);
  580.         UINT y = GET_Y_LPARAM(lParam);
  581.         // actually SM_C?SIZEFRAME is too big, but we get away w/ it
  582.         // since we've been initiated by a WM_NCHITTEST so we know
  583.         // we're on *some* edge
  584.         UINT cx = GetSystemMetrics(SM_CXSIZEFRAME);
  585.         UINT cy = GetSystemMetrics(SM_CYSIZEFRAME);
  586.         if (_eMode == WBM_BBOTTOMMOST)
  587.             cx *= 2;
  588.         lRet = HTCAPTION;
  589.         if (x > rc.right-cx) {
  590.             lRet = HTRIGHT;
  591.         } else if (x < rc.left+cx) {
  592.             lRet = HTLEFT;
  593.         } else if (y < rc.top+cy) {
  594.             lRet = HTTOP;
  595.         } else if (y > rc.bottom-cy) {
  596.             lRet = HTBOTTOM;
  597.         }
  598.     }
  599.     return lRet;
  600. }
  601. //***
  602. //
  603. void CDockingBar::_DragEnter(UINT uMsg, int x, int y, RECT* rcFeed)
  604. {
  605.     ASSERT(_fDragging == 0);
  606.     if ((!(_eMode & WBM_FLOATING)) && uMsg == WM_MOVING)
  607.     {
  608.         // BUGBUG workaround USER non-full-drag drag rect bug
  609.         // by forcing rcFeed back to exact current location (rather than
  610.         // leaving at initial offset that USER gave us).
  611.         //
  612.         // w/o this code a drag from right to top in non-full-drag mode
  613.         // will leave drag-rect droppings at the top of the original
  614.         //
  615.         // BUGBUG but, this seems to make things *worse* if !ISWBM_DESKTOP(),
  616.         // so we don't do it in that case...  (sigh).
  617.         _MoveSizeHelper(_eMode, _uSide, _hMon, NULL, rcFeed, FALSE, FALSE);
  618.     }
  619.     _eModePending = _eMode;
  620.     _uSidePending = _uSide;
  621.     _xyPending = MAKELPARAM(x, y);
  622.     _hMonPending = _hMon;
  623.     ASSERT(rcFeed != 0);
  624.     if (rcFeed != 0)
  625.         CopyRect(&_rcPending, rcFeed);
  626. #if XXX_CANCEL
  627.     RECT rcTmp;
  628.     GetWindowRect(_hwnd, &rcTmp);
  629.     TraceMsg(DM_DRAG2,
  630.         "cwb.de: rcTmp=(%d,%d,%d,%d) (%dx%d) _rcCapture=(%d,%d,%d,%d) (%dx%d)",
  631.         rcTmp.left, rcTmp.top, rcTmp.right, rcTmp.bottom,
  632.         RECTWIDTH(rcTmp), RECTHEIGHT(rcTmp),
  633.         _rcCapture.left, _rcCapture.top, _rcCapture.right, _rcCapture.bottom,
  634.         RECTWIDTH(_rcCapture), RECTHEIGHT(_rcCapture));
  635. #endif
  636.     switch (uMsg) {
  637.     case WM_MOVING: _fDragging = DRAG_MOVE; break;
  638.     case WM_SIZING: _fDragging = DRAG_SIZE; break;
  639.     default: ASSERT(0); break;
  640.     }
  641.     if (_fDragging == DRAG_MOVE) {
  642.         // turn off size negotiation to prevent horz/vert pblms.
  643.         //
  644.         // e.g. when we drag a floating guy to horz/vert, there's
  645.         // a period of time during which we have a horz/vert size,
  646.         // but still think we're floating, which screws up size
  647.         // negotiation royally.
  648.         _ExecDrag(DRAG_MOVE);
  649.     }
  650.     return;
  651. }
  652. //***
  653. //
  654. void CDockingBar::_DragTrack(UINT uMsg, int x, int y, RECT* rcFeed, int eState)
  655. {
  656. #if DM_API
  657.     TraceMsg(DM_DRAG2,
  658.         "cwb.dt: API s=%d xy=(%d,%d) rc=(%d,%d,%d,%d) (%dx%d)",
  659.         eState, x, y,
  660.         _PM(rcFeed,left), _PM(rcFeed,right), _PM(rcFeed,bottom), _PM(rcFeed,right),
  661.         _PX(rcFeed,RECTWIDTH(*rcFeed)), _PX(rcFeed,RECTHEIGHT(*rcFeed)));
  662. #endif
  663.     ASSERT(_fDragging != 0);
  664.     switch (eState) {
  665.     case 0:     // WM_MOVING
  666.         {
  667.             BOOL fImmediate = ((!_fDesktop) && uMsg == WM_SIZING) ? TRUE:FALSE;
  668.             // remember for eventual commit
  669.             _xyPending = MAKELPARAM(x, y);
  670.             ASSERT(rcFeed != NULL);
  671.             CopyRect(&_rcPending, rcFeed);
  672.             // snap and give feedback
  673.             _TrackSliding(x, y, rcFeed, fImmediate, fImmediate);
  674.             break;
  675.         }
  676.     case 1:     // WM_MOVE
  677.         TraceMsg(DM_DRAG2,
  678.             "cwb.dt: %s _xyPend=(%d,%d) xy=(%d,%d)",
  679.             (_xyPending != MAKELPARAM(x, y)) ? "noop/cancel" : "commit",
  680.             GET_X_LPARAM(_xyPending), GET_Y_LPARAM(_xyPending), x, y);
  681.         break;
  682.     default: ASSERT(0); break;
  683.     }
  684.     return;
  685. }
  686. //***
  687. //
  688. void CDockingBar::_DragLeave(int x, int y, BOOL fCommit)
  689. {
  690. #if DM_API
  691.     TraceMsg(DM_DRAG,
  692.         "cwb.dl: API xy=(%d,%d) fCommit=%d",
  693.         x, y, fCommit);
  694. #endif
  695.     if (_fDragging == 0) {
  696.         // when we're inside a browser and you move the browser window
  697.         // we get WM_ENTERSIZEMOVE/ WM_EXITSIZEMOVE but never any
  698.         // WM_MOVING/WM_MOVE/WM_SIZING/WM_SIZE
  699.         return;
  700.     }
  701.     switch (_fDragging) {
  702.     case DRAG_MOVE:
  703.     case DRAG_SIZE:
  704.         break;
  705.     default: ASSERT(0); break;
  706.     }
  707. #if XXX_CANCEL
  708.     RECT rcTmp;
  709.     GetWindowRect(_hwnd, &rcTmp);
  710.     TraceMsg(DM_DRAG2,
  711.         "cwb.dl: rcTmp=(%d,%d,%d,%d) (%dx%d) _rcCapture=(%d,%d,%d,%d) (%dx%d)",
  712.         rcTmp.left, rcTmp.top, rcTmp.right, rcTmp.bottom,
  713.         RECTWIDTH(rcTmp), RECTHEIGHT(rcTmp),
  714.         _rcCapture.left, _rcCapture.top, _rcCapture.right, _rcCapture.bottom,
  715.         RECTWIDTH(_rcCapture), RECTHEIGHT(_rcCapture));
  716.     TraceMsg(DM_DRAG2, "cwb.dl: %s",
  717.         EqualRect(&rcTmp, &_rcCapture) ? "noop/cancel" : "commit");
  718. #endif
  719.     BOOL fCancel = FALSE;       // BUGBUG todo: cancel NYI
  720.     if (!fCancel) {
  721.         if (_fDragging == DRAG_MOVE) {
  722.             // nt5:187720 do this *before* the final move.
  723.             // o.w. addr band ends up w/ 80-high default rather than
  724.             // snapped to correct/negotiated size.
  725.             //
  726.             // why are we able to turn this on here when in general it
  727.             // had to be off during the drag?  well, the preview of the
  728.             // drag went thru MoveSizeHelper which did a NotifyModeChange
  729.             // which told our client what its orientation really is.  so
  730.             // by now things should be in sync.
  731.             // size negotiation had been turned off (to prevent horz/vert
  732.             // pblms).  turn it on before the final move so that we'll
  733.             // recalc correctly.
  734.             _ExecDrag(0);
  735.         }
  736.         // (we're done w/ _rcPending so o.k. to pass it in and trash it)
  737.         // fMove==TRUE even though USER has already done the move for us,
  738.         // since it's only done the move not the resize (?).  if we use
  739.         // fMove==FALSE we end up in the new location but w/ the old size,
  740.         // despite the fact that rcFeed has been updated along the way.
  741.         // this is because USER sets SWP_NOSIZE when it does the move.
  742.         _TrackSliding(GET_X_LPARAM(_xyPending), GET_Y_LPARAM(_xyPending),
  743.             &_rcPending, TRUE, TRUE);
  744.         // if we got a preferred child sizewhild dragging, set ourselves to that now.
  745.         // sizing up   (cx > min), _szChild.cx == 0 and call a noop.
  746.         // sizing down (cx < min), _szChild.cx != 0 and call does something.
  747.         //ASSERT(_szChild.cx == 0);   // 0 => _AdjustToChildSize is nop
  748.         _AdjustToChildSize();
  749.     }
  750.     else {
  751.         _MoveSizeHelper(_eMode, _uSide, _hMon, NULL, NULL, TRUE, FALSE);   // BUGBUG fMove?
  752.     }
  753.     _fDragging = 0;
  754.     
  755.     return;
  756. }
  757. #ifdef DEBUG
  758. int g_dbNoExecDrag = 0;     // to play w/ ExecDrag w/o recompiling
  759. #endif
  760. void DBC_ExecDrag(IUnknown *pDBC, int eDragging)
  761. {
  762.     VARIANTARG vaIn = {0};      // VariantInit
  763.     ASSERT(eDragging == DRAG_MOVE || eDragging == 0);
  764. #ifdef DEBUG
  765.     if (g_dbNoExecDrag)
  766.         return;
  767. #endif
  768.     vaIn.vt = VT_I4;
  769.     vaIn.lVal = eDragging;      // n.b. currently only 0/1 is supported 
  770.     IUnknown_Exec(pDBC, &CGID_DeskBarClient, DBCID_ONDRAG, OLECMDEXECOPT_DONTPROMPTUSER, &vaIn, NULL);
  771.     // VariantClear
  772.     return;
  773. }
  774. void CDockingBar::_ExecDrag(int eDragging)
  775. {
  776.     DBC_ExecDrag(_pDBC, eDragging);
  777.     return;
  778. }
  779. //***   _Recalc -- force recalc using current settings
  780. //
  781. void CDockingBar::_Recalc(void)
  782. {
  783.     _MoveSizeHelper(_eMode, _uSide, _hMon, NULL, NULL, TRUE, TRUE);
  784.     return;
  785. }
  786. //***   _MoveSizeHelper -- shared code for menu and dragging forms of move/size
  787. //
  788. void CDockingBar::_MoveSizeHelper(UINT eModeNew, UINT eSideNew, HMONITOR hMonNew,
  789.     POINT* ptTrans, RECT* rcFeed, BOOL fCommit, BOOL fMove)
  790. {
  791.     UINT eModeOld, eSideOld;
  792.     RECT rcNew;
  793.     // only desktop guys can go to TOPMOST
  794.     ASSERT(eModeNew != WBM_TOPMOST || ISWBM_DESKTOP());
  795.     eModeOld = _eMode;
  796.     eSideOld = _uSide;
  797.     ASSERT(CHKWBM_CHANGE(eModeNew, _eMode));
  798.     _eModePending = eModeNew;   // for drag feedback, before commit
  799.     _uSidePending = eSideNew;
  800.     _hMonPending = hMonNew;
  801.     if (fCommit)
  802.     {
  803.         // we need to be careful when we call _ChangeHide or we'll recurse
  804.         BOOL fChangeHide = (_fWantHide &&
  805.             (eSideNew != _uSide || eModeNew != _eMode || hMonNew != _hMon));
  806.         if (fChangeHide)
  807.             _DoHide(AHO_KILLDO|AHO_UNREG);
  808.         _SetModeSide(eModeNew, eSideNew, hMonNew, FALSE);
  809.         if (fChangeHide)
  810.         {
  811.             // don't do AHO_SETDO now, wait for WM_ACTIVATE(deactivate)
  812.             _DoHide(AHO_REG);
  813.         }
  814.     }
  815.     // negotiate (and possibly commit to negotiation)
  816.     _ProtoRect(&rcNew, eModeNew, eSideNew, hMonNew, ptTrans);
  817.     _NegotiateRect(eModeNew, eSideNew, hMonNew, &rcNew, fCommit);
  818.     // commit
  819.     if (fCommit)
  820.         _SetVRect(&rcNew);
  821.     // feedback
  822.     if (rcFeed != 0)
  823.     {
  824.         CopyRect(rcFeed, &rcNew);
  825.     }
  826.     
  827.     if (fMove)
  828.     {
  829.         // If we're in theater mode, out parent manages our width and
  830.         // horizontal position, unless we're being forced to a new
  831.         // size by szChild.
  832.         if (_fTheater && !_fDragging)
  833.         {
  834.             RECT rcCur;
  835.             GetWindowRect(_hwnd, &rcCur);
  836.             rcNew.left = rcCur.left;
  837.             rcNew.right = rcCur.right;
  838.         }
  839.         // aka ScreenToClient
  840.         MapWindowPoints(HWND_DESKTOP, GetParent(_hwnd), (POINT*) &rcNew, 2);
  841.         
  842.         if (_fCanHide && eModeNew == eModeOld && eSideNew == eSideOld)
  843.         {
  844.             // if we're [un]hiding to the same state, we can do SlideWindow
  845.             ASSERT(ISWBM_HIDEABLE(eModeNew));
  846.             DAD_ShowDragImage(FALSE);   // unlock the drag sink if we are dragging.
  847.             SlideWindow(_hwnd, &rcNew, _hMon, !_fHiding);
  848.             DAD_ShowDragImage(TRUE);    // restore the lock state.
  849.         }
  850.         else
  851.         {
  852.             MoveWindow(_hwnd, rcNew.left, rcNew.top,
  853.                        RECTWIDTH(rcNew), RECTHEIGHT(rcNew), TRUE);
  854.         }
  855.     }
  856.     // WARNING: rcNew is no longer in consistent coords! (ScreenToClient)
  857.     // notify the child of changes
  858.     _NotifyModeChange(0);
  859. }
  860. void CDockingBar::_NotifyModeChange(DWORD dwMode)
  861. {
  862.     UINT eMode, uSide;
  863.     eMode = ((_fDragging == DRAG_MOVE) ? _eModePending : _eMode);
  864.     uSide = ((_fDragging == DRAG_MOVE) ? _uSidePending : _uSide);
  865.     //hMon = ((_fDragging == DRAG_MOVE) ? _hMonPending : _hMon);
  866.     if (ISWBM_FLOAT(eMode))
  867.         dwMode |= DBIF_VIEWMODE_FLOATING;
  868.     else if (!ABE_HORIZ(uSide))
  869.         dwMode |= DBIF_VIEWMODE_VERTICAL;
  870.     SUPERCLASS::_NotifyModeChange(dwMode);
  871.     
  872. }
  873. void CDockingBar::_TrackSliding(int x, int y, RECT* rcFeed,
  874.     BOOL fCommit, BOOL fMove)
  875. {
  876.     TraceMsg(DM_DRAG2,
  877.         "cwb.ts: _TrackSliding(x=%d, y=%d, rcFeed=(%d,%d,%d,%d)(%dx%d), fCommit=%d, fMove=%d)",
  878.         x, y,
  879.         _PM(rcFeed,left), _PM(rcFeed,top), _PM(rcFeed,right), _PM(rcFeed,bottom),
  880.         _PX(rcFeed,RECTWIDTH(*rcFeed)), _PX(rcFeed,RECTHEIGHT(*rcFeed)),
  881.         fCommit, fMove);
  882.     POINT pt = { x, y };
  883.     UINT eModeNew, uSideNew;
  884.     HMONITOR hMonNew;
  885.     if (_fDragging == DRAG_MOVE) {
  886.         // moving...
  887.         if (fCommit) {
  888.             // use last feedback position.
  889.             // o.w. (if we recompute) we end up in the wrong place since
  890.             // WM_MOVE gives us the (left,top), which often is in another
  891.             // docking zone.
  892.             ASSERT(x == GET_X_LPARAM(_xyPending) && y == GET_Y_LPARAM(_xyPending));
  893.             //eModeNew = _eModePending;
  894.             //uSideNew = _uSidePending;
  895.         }
  896.         //
  897.         // figure out snap position,
  898.         // and do a few special-case hacks to fix it up if necessary
  899.         //
  900.         uSideNew = _CalcDragPlace(pt, &hMonNew);
  901.         if (uSideNew == ABE_XFLOATING) {
  902.             // dock->float or float->float
  903.             eModeNew = _eMode | WBM_FLOATING;
  904.             uSideNew = _uSide;          // BUGBUG _uSidePending?
  905.         }
  906.         else {
  907.             // float->dock or dock->dock
  908.             eModeNew = _eMode & ~WBM_FLOATING;
  909.         }
  910.         TraceMsg(DM_DRAG2,
  911.             "cwb.ts: (m,s) _x=(%d,%d) _xPend=(%d,%d) xNew=(%d,%d)",
  912.             _eMode, _uSide, _eModePending, _uSidePending, eModeNew, uSideNew);
  913.         // 970725: we now allow bottom->float (for the desktop, not browser)
  914.         if (ISWBM_FLOAT(eModeNew) && ISWBM_BOTTOM(_eMode) && !ISWBM_DESKTOP()) {
  915.             // special case: don't allow switch from BTMMOST to FLOATING
  916.             ASSERT(CHKWBM_CHANGE(eModeNew, _eMode));
  917.             eModeNew = _eModePending;   // the dead zone...
  918.             uSideNew = _uSidePending;   // BUGBUG init case?
  919.             hMonNew = _hMonPending;
  920.             TraceMsg(DM_DRAG2,
  921.                 "cwb.ts: (m,s) btm->flt override     xNew=(%d,%d)",
  922.                 eModeNew, uSideNew);
  923.             ASSERT(!ISWBM_FLOAT(eModeNew));
  924.         }
  925.         //
  926.         // smooth things out so we don't jump around
  927.         //
  928.         _SmoothDragPlace(eModeNew, uSideNew, hMonNew, &pt, rcFeed);
  929.         //
  930.         // now do the move
  931.         //
  932.         // | with _eMode & WBMF_BROWSER because dragging around doesn't change the
  933.         // browser owned bit
  934.         _MoveSizeHelper(eModeNew | (_eMode & WBMF_BROWSER), uSideNew, hMonNew, 
  935.             ISWBM_FLOAT(eModeNew) ? &pt : NULL, rcFeed, fCommit, fMove);
  936.     }
  937.     else {
  938.         ASSERT(_fDragging == DRAG_SIZE);
  939.         // truncate to max size if necessary
  940.         _SmoothDragPlace(_eMode, _uSide, _hMon, NULL, rcFeed);
  941.         if (!fCommit) {
  942.             // USER does everything for us
  943.             return;
  944.         }
  945.         ASSERT(MAKELPARAM(x, y) != XY_NIL);
  946.         // BUGBUG hack: we're gonna commit so just blast it in here...
  947.         RECT rcNew;
  948.         GetWindowRect(_hwnd, &rcNew);   // BUGBUG already set?
  949.         _SetVRect(&rcNew);
  950.         _MoveSizeHelper(_eMode, _uSide, _hMon,
  951.             NULL, NULL,                 // BUGBUG &rcNew?
  952.             fCommit, fMove);
  953.     }
  954.     return;
  955. }
  956. /***    _CalcDragPlace -- compute where drag will end up
  957.  * NOTES
  958.  *      BUGBUG prelim version
  959.  */
  960. UINT CDockingBar::_CalcDragPlace(POINT& pt, HMONITOR * phMon)
  961. {
  962.     TraceMsg(DM_DRAG2,
  963.         "cwb.cdp: _CalcDragPlace(pt=(%d,%d))",
  964.         pt.x, pt.y);
  965.     SIZE screen, error;
  966.     UINT uHorzEdge, uVertEdge, uPlace;
  967.     RECT rcDisplay;
  968.     // Get the correct hMonitor.
  969.     ASSERT(phMon);
  970.     *phMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  971.     // BUGBUG todo: make hwndSite and rcDisplay args, then this can be
  972.     // a generic helper func
  973.     _GetBorderRect(*phMon, &rcDisplay);
  974.     // if we're outside our 'parent' (browser or desktop), we float.
  975.     // this really only applies to the browser case, since we'll never
  976.     // be outside the desktop (BUGBUG what about multi-monitor?).
  977.     if (!PtInRect(&rcDisplay, pt) || !_ptbSite) {
  978.         TraceMsg(DM_DRAG2,
  979.             "cwb.cdp: pt=(%d,%d) uSideNew=%u",
  980.             pt.x, pt.y, ABE_XFLOATING);
  981.         return ABE_XFLOATING;
  982.     }
  983.     // if we're not w/in min threshhold from edge, we float
  984.     {
  985.         RECT rcFloat;   // BUGBUG can just use rcDisplay
  986.         int cx = CXFLOAT();
  987.         int cy = CYFLOAT();
  988.         CopyRect(&rcFloat, &rcDisplay);
  989.         InflateRect(&rcFloat, -cx, -cy);
  990.         if (PtInRect(&rcFloat, pt)) {
  991.             TraceMsg(DM_DRAG2,
  992.                 "cwb.cdp: pt=(%d,%d) uSideNew=%u",
  993.                 pt.x, pt.y, ABE_XFLOATING);
  994.             return ABE_XFLOATING;
  995.         }
  996.     }
  997.     //
  998.     // re-origin at zero to make calculations simpler
  999.     //
  1000.     screen.cx =  RECTWIDTH(rcDisplay);
  1001.     screen.cy = RECTHEIGHT(rcDisplay);
  1002.     pt.x -= rcDisplay.left;
  1003.     pt.y -= rcDisplay.top;
  1004.     //
  1005.     // are we closer to the left or right side of this display?
  1006.     //
  1007.     if (pt.x < (screen.cx / 2)) {
  1008.         uVertEdge = ABE_LEFT;
  1009.         error.cx = pt.x;
  1010.     }
  1011.     else {
  1012.         uVertEdge = ABE_RIGHT;
  1013.         error.cx = screen.cx - pt.x;
  1014.     }
  1015.     //
  1016.     // are we closer to the top or bottom side of this display?
  1017.     //
  1018.     if (pt.y < (screen.cy / 2)) {
  1019.         uHorzEdge = ABE_TOP;
  1020.         error.cy = pt.y;
  1021.     }
  1022.     else {
  1023.         uHorzEdge = ABE_BOTTOM;
  1024.         error.cy = screen.cy - pt.y;
  1025.     }
  1026.     //
  1027.     // closer to a horizontal or vertical edge?
  1028.     //
  1029.     uPlace = ((error.cy * screen.cx) > (error.cx * screen.cy))?
  1030.         uVertEdge : uHorzEdge;
  1031.     TraceMsg(DM_DRAG2,
  1032.         "cwb.cdp: pt=(%d,%d) uSideNew=%u",
  1033.         pt.x, pt.y, uPlace);
  1034.     return uPlace;
  1035. }
  1036. //***   _SmoothDragPlace -- do some magic to smooth out dragging
  1037. // ENTRY/EXIT
  1038. //      eModeNew        where we're snapping to
  1039. //      eSideNew        ...
  1040. //      [_eModePending] where we're snapping from
  1041. //      [_eSidePending] ...
  1042. //      pt              INOUT cursor position
  1043. //      rcFeed          USER's original drag feedback rect
  1044. // NOTES
  1045. //      this is the place to put excel-like heuristics.  e.g. when coming
  1046. //      back off the right side we could put the cursor at the top right of
  1047. //      the floating rect (rather than the top left) to allow us to float
  1048. //      as close as possible to the other side w/o docking.  hmm, but how
  1049. //      would we tell USER where to put the cursor...
  1050. //
  1051. void CDockingBar::_SmoothDragPlace(UINT eModeNew, UINT eSideNew, HMONITOR hMonNew,
  1052.     INOUT POINT* pt, RECT* rcFeed)
  1053. {
  1054.     if (_fDragging == DRAG_MOVE) {
  1055.         if (ISWBM_FLOAT(eModeNew) && ISWBM_FLOAT(_eModePending) && rcFeed != 0) {
  1056.             // use the feedback rect from USER to keep things smooth.
  1057.             // o.w. if we use the cursor position we'll jump at the
  1058.             // beginning (to move the left-top corner to the starting
  1059.             // cursor position).
  1060.             pt->x = rcFeed->left;
  1061.             pt->y = rcFeed->top;
  1062.         }
  1063.     }
  1064.     else {
  1065.         ASSERT(_fDragging == DRAG_SIZE);
  1066.         ASSERT(eModeNew == _eMode && eSideNew == _uSide && hMonNew == _hMon);
  1067.         if (!ISWBM_FLOAT(_eMode)) {
  1068.             // truncate to max size (1/2 of screen) if necessary
  1069.             int iWH;
  1070.             RECT rcScreen;
  1071.             // we'd like to use 1/2 of browser, not 1/2 of screen.  however
  1072.             // this causes pblms if you maximize, grow to 1/2, and restore.
  1073.             // then the 1st time you resize the bar it 'jumps' down to 1/2
  1074.             // of the *current* size from 1/2 of the old size.  kind of a
  1075.             // hack, sigh...
  1076.             //
  1077.             // also note that there's still a bug here: if you size down
  1078.             // the browser gradually, we don't go thru this logic, so you
  1079.             // end up w/ a bar width > browser width so you the right edge
  1080.             // is clipped and there's no way to size it down.  probably
  1081.             // when the browser resize is done we should re-smooth the bar.
  1082.             //_GetBorderRect(_hMon, &rcScreen);
  1083.             GetMonitorRect(_hMon, &rcScreen);   // aka GetSystemMetrics(SM_CXSCREEN)
  1084.             iWH = RECTGETWH(_uSide, &rcScreen);
  1085.             iWH /= 2;
  1086.             if (RECTGETWH(_uSide, rcFeed) > iWH) {
  1087.                 TraceMsg(DM_TRACE, "cwb.ts: truncate iWH'=%d", iWH);
  1088.                 RectXform(rcFeed, RX_OPPOSE, rcFeed, NULL, iWH, _uSide, NULL);
  1089.             }
  1090.         }
  1091.     }
  1092.     return;
  1093. }
  1094. /***
  1095.  */
  1096. LRESULT CDockingBar::_OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1097. {
  1098.     LRESULT lres = 0;
  1099.     if (_CheckForwardWinEvent(uMsg, wParam, lParam, &lres))
  1100.         return lres;
  1101.     
  1102.     if ((Command_GetID(wParam) <= IDM_AB_LAST) &&
  1103.         (Command_GetID(wParam) >= IDM_AB_FIRST)) {
  1104.         _AppBarOnCommand(Command_GetID(wParam));
  1105.     }
  1106.     return lres;
  1107. }
  1108. /***    CDockingBar::_AppBarRegister -- register/unregister AppBar
  1109.  * DESCRIPTION
  1110.  *      updates _fAppRegistered
  1111.  *      does nothings if it's already regisrered or unregistered
  1112.  */
  1113. void CDockingBar::_AppBarRegister(BOOL fRegister)
  1114. {
  1115.     APPBARDATA abd;
  1116.     abd.cbSize = sizeof(APPBARDATA);
  1117.     abd.hWnd = _hwnd;
  1118.     if (fRegister && !_fAppRegistered) {
  1119.         abd.uCallbackMessage = APPBAR_CALLBACK;
  1120.         TraceMsg(DM_APPBAR, "cwb.abr: call ABM_NEW");
  1121.         BOOL bT = SHAppBarMessage(ABM_NEW, &abd);
  1122.         ASSERT(bT);
  1123.         if (bT) {
  1124.             _fAppRegistered = TRUE;
  1125.             // fake a callback to set initial state
  1126.             // #if XXX_TASKMAN
  1127.             TraceMsg(DM_APPBAR, "cwb.abr: fake ABN_STATECHANGE");
  1128.             _AppBarCallback(_hwnd, APPBAR_CALLBACK, ABN_STATECHANGE, 0);
  1129.             // #endif
  1130.         }
  1131.     }
  1132.     else if (!fRegister && _fAppRegistered) {
  1133.         TraceMsg(DM_APPBAR, "cwb.abr: call ABM_REMOVE");
  1134.         // n.b. sensitive phase ordering, must set flag before send message
  1135.         // since the message causes a bunch of callbacks
  1136.         _fAppRegistered = FALSE;
  1137.         SHAppBarMessage(ABM_REMOVE, &abd);
  1138.     }
  1139. }
  1140. //***   _SetVRect -- set our 'virtual rect' to reflect window state
  1141. //
  1142. void CDockingBar::_SetVRect(RECT* rcNew)
  1143. {
  1144.     UINT eModeNew, uSideNew;
  1145.     //ASSERT(_fDragging == 0);  // o.w. we should look at _xxxPending
  1146.     eModeNew = _eMode;
  1147.     uSideNew = _uSide;
  1148.     if (_fHiding && ISWBM_HIDEABLE(eModeNew)) {
  1149.         TraceMsg(DM_HIDE, "cwb.svr: _fHiding => suppress rcNew=(%d,%d,%d,%d)",
  1150.             rcNew->left, rcNew->top, rcNew->right, rcNew->bottom);
  1151.         return;
  1152.     }
  1153.     if (ISWBM_FLOAT(eModeNew)) {
  1154.         CopyRect(&_rcFloat, rcNew);
  1155.     }
  1156.     else {
  1157.         _adEdge[uSideNew] = ABE_HORIZ(uSideNew) ? RECTHEIGHT(*rcNew) : RECTWIDTH(*rcNew);
  1158.     }
  1159.     return;
  1160. }
  1161. //***   _ChangeTopMost -- switch back and forth btwn TopMost and BottomMost
  1162. // ENTRY/EXIT
  1163. //      eModeNew        new mode we're switching to
  1164. //
  1165. void CDockingBar::_ChangeTopMost(UINT eModeNew)
  1166. {
  1167.     BOOL fShouldRegister = (eModeNew & WBM_TOPMOST) && !(eModeNew & WBM_FLOATING);
  1168.     
  1169.     // here's what's legal...
  1170. //              to...................
  1171. // from         btm     top     float
  1172. // ----         ---     ---     -----
  1173. // btm(desk)    -       top+    y(1)    (1) force to top
  1174. // top          top-    -       'undock'
  1175. // float        y(2)    'dock'  -       (2) force to right
  1176. // btm(app)     -       x(3)    y(4)    (3) foster child (4) 'owned' window
  1177. #if 0
  1178.     // (1,4) going from BTMMOST to FLOATING is illegal (and NYI) unless desktop
  1179.     ASSERT(eModeNew != WBM_FLOATING || _eMode != WBM_BOTTOMMOST || ISWBM_DESKTOP());
  1180. #endif
  1181.     // (3) only desktop guys can go to TOPMOST
  1182.     ASSERT(eModeNew != WBM_TOPMOST || ISWBM_DESKTOP());
  1183.     // _uSide should always be laying around (even if floating)
  1184.     ASSERT(_eMode == WBM_NIL || ISABE_DOCK(_uSide));
  1185.     // note the ordering here, make sure window bits are right
  1186.     // before doing resume new or else new will have unexpected state
  1187.     _ChangeWindowStateAndParent(eModeNew);
  1188.     _eMode = eModeNew;
  1189.     _ChangeZorder();
  1190.     // resume new
  1191.     switch (_eMode) {
  1192.     case WBM_NIL:
  1193.         // dummy state for termination
  1194.         return;
  1195.     case WBM_BOTTOMMOST:
  1196.         _ResetZorder();
  1197. #if ! XXX_BROWSEROWNED
  1198.         // fall through
  1199.     case WBM_BBOTTOMMOST:
  1200. #endif
  1201.         break;
  1202.     }
  1203.     
  1204.     _AppBarRegister(fShouldRegister);
  1205. }
  1206. //***   _ChangeZorder -- set z-order appropriately
  1207. // NOTES
  1208. //  currently doesn't account for 'raised' mode (i.e. caller must call
  1209. //  _ChangeZorder before _ResetZorder)
  1210. void CDockingBar::_ChangeZorder()
  1211. {
  1212.     BOOL fWantTopmost = BOOLIFY(WBM_IS_TOPMOST());
  1213.     BOOL fIsTopmost = BOOLIFY(GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST);
  1214.     if (fWantTopmost != fIsTopmost)
  1215.         SetWindowPos(_hwnd, fWantTopmost ? HWND_TOPMOST : HWND_NOTOPMOST,
  1216.                      0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  1217.     return;
  1218. }
  1219. //***    _ResetZorder -- toggle DockingBars between 'normal' and 'raised' mode
  1220. // DESCRIPTION
  1221. //  queries desktop state and does appropriate '_OnRaise'
  1222. //
  1223. void CDockingBar::_ResetZorder()
  1224. {
  1225.     HRESULT hr;
  1226.     VARIANTARG vaIn = {0};      // VariantInit
  1227.     VARIANTARG vaOut = {0};     // VariantInit
  1228.     vaIn.vt = VT_I4;
  1229.     vaIn.lVal = DTRF_QUERY;
  1230.     hr = IUnknown_Exec(_ptbSite, &CGID_ShellDocView, SHDVID_RAISE, OLECMDEXECOPT_DONTPROMPTUSER,
  1231.         &vaIn, &vaOut);
  1232.     if (SUCCEEDED(hr) && vaOut.vt == VT_I4)
  1233.         _OnRaise(vaOut.lVal);
  1234.     // VariantClear
  1235.     return;
  1236. }
  1237. //***   _OnRaise -- handle desktop 'raise' command
  1238. // DESCRIPTION
  1239. //  changes DockingBar z-order depending on desktop raise state:
  1240. //      desktop     DockingBar
  1241. //      raised      force on top (so visible)
  1242. //      restored    return to normal
  1243. // NOTES
  1244. //  BUGBUG should we handle WBM_FLOATING too?
  1245. //  BUGBUG should add ZORD_xxx to deskbar.h and handle non-WBM_BOTTOMMOST
  1246. void CDockingBar::_OnRaise(UINT flags)
  1247. {
  1248.     HWND hwndZorder;
  1249.     if (_eMode != WBM_BOTTOMMOST)
  1250.         return;
  1251.     switch (flags) {
  1252.     case DTRF_RAISE:
  1253.         hwndZorder = HWND_TOPMOST;
  1254.         break;
  1255.     case DTRF_LOWER:
  1256.         hwndZorder = HWND_NOTOPMOST;
  1257.         break;
  1258.     
  1259.     default:
  1260.         ASSERT(0);
  1261.         return;
  1262.     }
  1263.     SetWindowPos(_hwnd, hwndZorder, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
  1264.     return;
  1265. }
  1266. #if XXX_BTMFLOAT && 0 // see 'NOTES'
  1267. //***   _MayReWindow -- change/restore window state for dragging
  1268. // DESCRIPTION
  1269. //      USER won't let us drag outside of the browser unless we reparent
  1270. //      ourselves.
  1271. // NOTES
  1272. //      need to call this *before* we enter USER's move/size loop.
  1273. //      i.e. on LBUTTONDOWN and on EXITSIZEMOVE.  this gets a bit
  1274. //      tricky due to CANCEL etc., since the LBUTTONUP may come thru
  1275. //      either before or after we're done.
  1276. //
  1277. void CDockingBar::_MayReWindow(BOOL fToFloat)
  1278. {
  1279.     if (ISWBM_DESKTOP() || _eMode != WBM_BOTTOMMOST)
  1280.         return;
  1281.     // do style bits 1st or re-parenting breaks
  1282.     SHSetWindowBits(_hwnd, GWL_STYLE, WS_CHILD | WS_POPUP, fToFloat ? WS_POPUP | WS_CHILD);
  1283.     if (!fToFloat) {
  1284.         // float->btm
  1285.         // nuke owner
  1286.         SHSetParentHwnd(_hwnd, NULL);
  1287.         // parent
  1288.         SetParent(_hwnd, _hwndSite);
  1289.     }
  1290.     if (fToFloat) {
  1291.         // btm->float, set owner
  1292.         // parent
  1293.         SetParent(_hwnd, PARENT_FLOATING);
  1294.         // set owner
  1295.         ASSERT(_hwndSite != NULL);
  1296.         SHSetParentHwnd(_hwnd, _hwndSite);
  1297.     }
  1298. }
  1299. #endif
  1300. void CDockingBar::_GetStyleForMode(UINT eMode, LONG* plStyle, LONG* plExStyle, HWND* phwndParent)
  1301. {
  1302.     switch (eMode) {
  1303.     case WBM_NIL:
  1304.         *plStyle = WS_NIL;
  1305.         *plExStyle= WS_EX_NIL;
  1306.         *phwndParent = PARENT_NIL;
  1307.         break;
  1308.     case WBM_BBOTTOMMOST:
  1309.         *plStyle = WS_BBTMMOST;
  1310.         *plExStyle= WS_EX_BBTMMOST;
  1311.         *phwndParent = PARENT_BBTMMOST();
  1312.         break;
  1313.     case WBM_BOTTOMMOST:
  1314.         *plStyle = WS_BTMMOST;
  1315.         *plExStyle= WS_EX_BTMMOST;
  1316.         *phwndParent = PARENT_BTMMOST();
  1317.         break;
  1318.     case WBM_BFLOATING:
  1319.         // BUGBUG todo: FLOATING NYI
  1320.         *plStyle = WS_BFLOATING;
  1321.         *plExStyle = WS_EX_BFLOATING;
  1322.         *phwndParent = _hwndSite;
  1323.         break;
  1324.     case (WBM_FLOATING | WBM_TOPMOST):
  1325.     case WBM_FLOATING:
  1326.         // BUGBUG todo: FLOATING NYI
  1327.         *plStyle = WS_FLOATING;
  1328.         *plExStyle = WS_EX_FLOATING;
  1329.         *phwndParent = PARENT_FLOATING;
  1330.         break;
  1331.     case WBM_TOPMOST:
  1332.         *plStyle = WS_XTOPMOST;
  1333.         *plExStyle= WS_EX_XTOPMOST;
  1334.         *phwndParent = PARENT_XTOPMOST;
  1335.         break;
  1336.     }
  1337. #ifdef DEBUG // {
  1338.     if (_eMode == eMode) {
  1339.         // style, exstyle
  1340.         ASSERT(BITS_SET(GetWindowLong(_hwnd, GWL_STYLE), *plStyle));
  1341.         ASSERT(BITS_SET(GetWindowLong(_hwnd, GWL_EXSTYLE), *plExStyle & ~WS_EX_TOPMOST));
  1342.         // id
  1343.         ASSERT(GetWindowLong(_hwnd, GWL_ID) == 0);
  1344.         // parent 
  1345.         ASSERT(GetParent(_hwnd) == *phwndParent ||
  1346.                (ISWBM_OWNED(_eMode) && GetParent(_hwnd)==_hwndSite));
  1347.     }
  1348. #endif // }
  1349. }
  1350. //***   _ChangeWindowStateAndParent --
  1351. // NOTES
  1352. //      todo: make table-driven (ws1, ws2, etc.)
  1353. //
  1354. void CDockingBar::_ChangeWindowStateAndParent(UINT eModeNew)
  1355. {
  1356.     LONG ws1, wsx1, ws2, wsx2;
  1357.     HWND hwnd;
  1358.     if (eModeNew == _eMode) {
  1359.         // same mode, nothing to do
  1360.         return;
  1361.     }
  1362.     //
  1363.     // nuke old bits
  1364.     //
  1365.     _GetStyleForMode(_eMode, &ws1, &wsx1, &hwnd);
  1366.     //
  1367.     // set new bits
  1368.     //
  1369.     _GetStyleForMode(eModeNew, &ws2, &wsx2, &hwnd);
  1370.     // if it's going to be owned by the browser, 
  1371.     // override hwnd to our site's hwnd
  1372.     if (eModeNew & WBMF_BROWSER)
  1373.         hwnd = _hwndSite;
  1374.     // style, exstyle
  1375.     // (SWB can't do WS_EX_TOPMOST, we do it in caller w/ SWP)
  1376.     SHSetWindowBits(_hwnd, GWL_STYLE, ws1|ws2 , ws2);
  1377.     SHSetWindowBits(_hwnd, GWL_EXSTYLE, (wsx1|wsx2) & ~WS_EX_TOPMOST, wsx2);
  1378.     // id
  1379.     // (unchanged)
  1380.     HWND hwndParent = GetParent(_hwnd); 
  1381.     if (hwndParent != hwnd) {
  1382.         if (hwndParent != HWND_DESKTOP) {
  1383.             // float->btm, nuke owner
  1384.             SHSetParentHwnd(_hwnd, NULL);
  1385.         }
  1386.         // parent
  1387.         SetParent(_hwnd, hwnd);
  1388.         if (hwnd == _hwndSite) {
  1389.             // btm->float, set owner
  1390.             ASSERT(_hwndSite != NULL);
  1391.             SHSetParentHwnd(_hwnd, _hwndSite);
  1392.         }
  1393.     }
  1394.     //
  1395.     // force redraw
  1396.     //
  1397.     SetWindowPos(_hwnd, NULL, 0, 0, 0, 0,
  1398.         SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
  1399.     return;
  1400. }
  1401. //***   _SetNewMonitor --
  1402. // When the Desktop is our Docking Site, set the new monitor I am on
  1403. // and return the old monitor 
  1404. HMONITOR CDockingBar::_SetNewMonitor(HMONITOR hMonNew)
  1405. {
  1406.     HMONITOR hMonOld = NULL;
  1407.     if (ISWBM_DESKTOP() && _ptbSite)
  1408.     {
  1409.         IMultiMonitorDockingSite * pdds;
  1410.         HRESULT hresT = _ptbSite->QueryInterface(IID_IMultiMonitorDockingSite, (LPVOID *)&pdds);
  1411.         if (SUCCEEDED(hresT))
  1412.         {
  1413.             HMONITOR hMon;
  1414.             ASSERT(pdds);
  1415.             if (SUCCEEDED(pdds->GetMonitor(SAFECAST(this, IDockingWindow*), &hMon)))
  1416.             {
  1417.                 if (hMon != hMonNew)
  1418.                 {
  1419.                     pdds->RequestMonitor(SAFECAST(this, IDockingWindow*), &hMonNew);
  1420.                     pdds->SetMonitor(SAFECAST(this, IDockingWindow*), hMonNew, &hMonOld);
  1421.                     // These two should be the same, otherwise something wierd is happening -- dli
  1422.                     ASSERT(hMonOld == hMon);
  1423.                 }
  1424.             }
  1425.             pdds->Release();
  1426.         }
  1427.     }
  1428.     
  1429.     return hMonOld;
  1430. }
  1431. //***   _GetBorderRect --
  1432. // NOTES
  1433. //      result in screen coordinates
  1434. //
  1435. void CDockingBar::_GetBorderRect(HMONITOR hMon, RECT* prc)
  1436. {
  1437.     if (!ISWBM_BOTTOM(_eMode)) {
  1438.         // BUGBUG todo: should use:
  1439.         // floating: _hwndSite (not strictly correct, but good enough)
  1440.         // topmost: UnionRect of:
  1441.         //     GetWindowRect(_hwndSite);        // non-appbar rect
  1442.         //     GetWindowRect(self)              // plus my personal appbar
  1443.         ASSERT(IsWindow(_hwndSite));
  1444.         if (ISWBM_DESKTOP())
  1445.             GetMonitorRect(hMon, prc);
  1446.         else
  1447.             GetWindowRect(_hwndSite, prc);  
  1448. #ifdef DEBUG
  1449. #if 0
  1450.         RECT rcTmp;
  1451.         // these asserts often fail.  e.g. when dragging topmost right->top.
  1452.         // weird: _hwndSite ends up being PROGMAN's hwnd.
  1453.         // weird: also, the GetWindowRect fails.
  1454.         ASSERT(_hwndSite == PARENT_XTOPMOST);
  1455.         // _hwndSite is PROGMAN
  1456.         GetWindowRect(PARENT_XTOPMOST, &rcTmp);
  1457.         ASSERT(EqualRect(prc, &rcTmp));
  1458. #endif
  1459. #endif
  1460.     }
  1461.     else if (_ptbSite) {
  1462.         HMONITOR hMonOld = _SetNewMonitor(hMon);
  1463.         _ptbSite->GetBorderDW(SAFECAST(this, IDockingWindow*), prc);
  1464.         if (hMonOld)
  1465.             _SetNewMonitor(hMonOld);
  1466.         ASSERT(_hwndSite != NULL);
  1467.         //ASSERT(GetParent(_hwnd) == _hwndSite);  // BUGBUG ISWBM_OWNED?
  1468.         // convert if necessary
  1469.         // aka ClientToScreen
  1470.         MapWindowPoints(_hwndSite, HWND_DESKTOP, (POINT*) prc, 2);
  1471.     }
  1472.     return;
  1473. }
  1474. //***   _HideRegister -- (un)register auto-hide w/ edge
  1475. // ENTRY/EXIT
  1476. //      fToHide         TRUE if turning AutoHide on, FALSE if turning off
  1477. //      _fCanHide       [OUT] TRUE if successfully set autohide on; o.w. FALSE 
  1478. //      other           pops up dialog if operation fails
  1479. //
  1480. void CDockingBar::_HideRegister(BOOL fToHide)
  1481. {
  1482.     BOOL fSuccess;
  1483.     APPBARDATA abd;
  1484.     if (! ISWBM_HIDEABLE(_eMode))
  1485.         return;
  1486.     // (try to) register or unregister it
  1487.     // n.b. we're allowed to do this even if we're not an AppBar
  1488.     // that's good, because we want at most one autohide deskbar 
  1489.     // on an edge regardless of mode
  1490.     abd.cbSize = SIZEOF(abd);
  1491.     abd.hWnd = _hwnd;
  1492.     abd.uEdge = _uSide;
  1493.     abd.lParam = fToHide;
  1494.     // BUGBUG should we do a ABM_GETAUTOHIDEBAR at some point?
  1495.     // (tray.c does, and so does the AB sample code...)
  1496.     //ASSERT(_fAppRegistered);
  1497.     fSuccess = (BOOL) SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd);
  1498.     // set our state
  1499.     _fCanHide = BOOLIFY(fSuccess);
  1500.     // BUGBUG how handle failure?
  1501.     // init some stuff
  1502.     if (fToHide)
  1503.     {
  1504.         if (_fCanHide)
  1505.         {
  1506.             RECT rc;
  1507.             ASSERT(_fCanHide);  // so we won't SetVRect
  1508.             ASSERT(!_fHiding);  // (paranoia)
  1509.             // force a '0-width' rectangle so we don't take up any space
  1510.             RectXform(&rc, RX_EDGE|RX_OPPOSE|RX_ADJACENT, &rc, NULL,
  1511.                 0, _uSide, _hMon);
  1512.             switch (_eMode) {
  1513.             case WBM_TOPMOST:
  1514.                 // negotiate/commit it
  1515.                 APPBARDATA abd;
  1516.                 abd.cbSize = sizeof(APPBARDATA);
  1517.                 abd.hWnd = _hwnd;
  1518.                 ASSERT(_fCanHide);
  1519.                 // we used to do:
  1520.                 //  _fCanHide = FALSE;  // hack: so we surrender AppBar's space
  1521.                 //  AppBarQuerySetPos(&rc, _uSide, &rc, &abd, TRUE);
  1522.                 //  _fCanHide = TRUE;   // hack: restore
  1523.                 // but the instant we do the ABSetPos the shell does a recalc
  1524.                 // by doing a ShowDW of all toolbars, which does a _Recalc,
  1525.                 // which does a MSH, which ends up doing ProtoRect w/ our
  1526.                 // 'temporary' _fCanHide=0, which ends up taking space (oops!).
  1527.                 //
  1528.                 // so instead we call the low-level ABQueryPos/ABSetPos guys
  1529.                 // directly.
  1530.                 AppBarQueryPos(&rc, _uSide, _hMon, &rc, &abd, TRUE);
  1531.                 AppBarSetPos0(_uSide, &rc, &abd);
  1532.                 break;
  1533.             }
  1534.             // do *not* start the hide here
  1535.             // it's up to the caller, since a) might want delay or
  1536.             // immediate and b) recursion pblms w/ _MoveSizeHelper
  1537.         }
  1538.         else
  1539.         {
  1540.             // BUGBUG do PostMessage a la tray.c?
  1541.             MLShellMessageBox(_hwnd,
  1542.                 MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
  1543.                 MAKEINTRESOURCE(IDS_WEBBARTITLE),
  1544.                 MB_OK | MB_ICONINFORMATION);
  1545.             ASSERT(!_fCanHide);
  1546.         }
  1547.     }
  1548.     else
  1549.     {
  1550.         // do *not* start the unhide here
  1551.         // it's up to the caller, since a) might want delay or
  1552.         // immediate and b) recursion pblms w/ _MoveSizeHelper
  1553.         _fCanHide = FALSE;
  1554.     }
  1555.     return;
  1556. }
  1557. //***   IsNearPoint -- am i currently near specified point?
  1558. // ENTRY/EXIT
  1559. //  pptBase     (INOUT) IN previous cursor pos, OUT updated to current if !fNear
  1560. //  fNear       (ret) TRUE if near, o.w. FALSE
  1561. // NOTES
  1562. //  heuristic stolen from explorer/tray.c!TraySetUnhideTimer
  1563. //
  1564. BOOL IsNearPoint(/*INOUT*/ POINT *pptBase)
  1565. {
  1566.     POINT ptCur;
  1567.     int dx, dy, dOff, dNear;
  1568.     GetCursorPos(&ptCur);
  1569.     dx = pptBase->x - ptCur.x;
  1570.     dy = pptBase->y - ptCur.y;
  1571.     dOff = dx * dx + dy * dy;
  1572.     dNear = GetSystemMetrics(SM_CXDOUBLECLK) * GetSystemMetrics(SM_CYDOUBLECLK);
  1573.     if (dOff <= dNear)
  1574.         return TRUE;
  1575.     TraceMsg(DM_HIDE2, "cwb.inp: ret=0 dOff=%d dNear=%d", dOff, dNear);
  1576.     *pptBase = ptCur;
  1577.     return FALSE;
  1578. }
  1579. //***   _DoHide --
  1580. // DESCRIPTION
  1581. //      AHO_KILLDO              kill timer for 'do'   operation
  1582. //      AHO_SETDO               set  timer for 'do'   operation
  1583. //      AHO_KILLUN              kill timer for 'undo' operation
  1584. //      AHO_SETUN               set  timer for 'undo' operation
  1585. //      AHO_REG                 register
  1586. //      AHO_UNREG               unregister
  1587. //      AHO_MOVEDO              do the actual hide
  1588. //      AHO_MOVEUN              do the actual unhide
  1589. // NOTES
  1590. //  the _fIdtXxHide stuff stops us from doing a 2nd SetTimer before the
  1591. // 1st one comes in, which makes us never get the 'earlier' ticks.
  1592. //  this fixes nt5:142686: drag-over doesn't unhide.  it was caused by us
  1593. // getting a bunch of WM_NCHITTESTs in rapid succession (OLE asking us on
  1594. // a fast timer?).
  1595. //  BUGBUG i think there's a tiny race window on _fIdtXxHide (between the
  1596. // call to Set/Kill and the shadowing in _fIdtXxHide).  not sure we can
  1597. // even hit it, but if we do, i think the worst that happens is somebody
  1598. // doesn't hide or unhide for a while.
  1599. //
  1600. void CDockingBar::_DoHide(UINT uOpMask)
  1601. {
  1602.     TraceMsg(DM_HIDE, "cwb.dh enter(uOpMask=0x%x(%s))",
  1603.         uOpMask, DbMaskToMneStr(uOpMask, AHO_MNE));
  1604.     if (!ISWBM_HIDEABLE(_eMode)) {
  1605.         TraceMsg(DM_HIDE, "cwb.dh !ISWBM_HIDEABLE(_eMode) => suppress");
  1606.         return;
  1607.     }
  1608.     // nuke old timer
  1609.     if (uOpMask & AHO_KILLDO) {
  1610.         TraceMsg(DM_HIDE, "cwb.dh: KillTimer(idt_autohide)");
  1611.         KillTimer(_hwnd, IDT_AUTOHIDE);
  1612.         _fIdtDoHide = FALSE;
  1613.     }
  1614.     if (uOpMask & AHO_KILLUN) {
  1615.         TraceMsg(DM_HIDE, "cwb.dh: KillTimer(idt_autoUNhide)");
  1616.         KillTimer(_hwnd, IDT_AUTOUNHIDE);
  1617.         _fIdtUnHide = FALSE;
  1618.         _ptIdtUnHide.x = _ptIdtUnHide.y = -1;
  1619.     }
  1620.     if (uOpMask & (AHO_REG|AHO_UNREG)) {
  1621.         _HideRegister(uOpMask & AHO_REG);
  1622.     }
  1623.     if (uOpMask & (AHO_MOVEDO|AHO_MOVEUN)) {
  1624.         // tricky, tricky...
  1625.         // all the smarts are in _MoveSizeHelper, driven by _fHiding (and _fCanHide)
  1626.         // use correct one of (tiny,real)
  1627.         _fHiding = (uOpMask & AHO_MOVEDO) ? HIDE_AUTO : FALSE;
  1628.         TraceMsg(DM_HIDE, "cwb.dh: move _fHiding=%d", _fHiding);
  1629.         ASSERT(_fCanHide);                      // suppress SetVRect
  1630.         _Recalc();  // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  1631.     }
  1632.     // start new timer
  1633.     if (_fCanHide) {
  1634.         if (uOpMask & AHO_SETDO) {
  1635.             TraceMsg(DM_HIDE, "cwb.dh: SetTimer(idt_autohide) fAlready=%d", _fIdtDoHide);
  1636.             if (!_fIdtDoHide) {
  1637.                 _fIdtDoHide = TRUE;
  1638.                 SetTimer(_hwnd, IDT_AUTOHIDE, DLY_AUTOHIDE, NULL);
  1639.             }
  1640.         }
  1641.         if (uOpMask & AHO_SETUN) {
  1642.             TraceMsg(DM_HIDE, "cwb.dh: SetTimer(idt_autoUNhide) fAlready=%d", _fIdtUnHide);
  1643.             // IsNearPoint hysteresis prevents us from unhiding when we happen
  1644.             // to be passed over on the way to something unrelated
  1645.             if (!IsNearPoint(&_ptIdtUnHide) || !_fIdtUnHide) {
  1646.                 _fIdtUnHide = TRUE;
  1647.                 SetTimer(_hwnd, IDT_AUTOUNHIDE, DLY_AUTOUNHIDE, NULL);
  1648.             }
  1649.         }
  1650.     }
  1651.     else {
  1652. #ifdef DEBUG
  1653.         if ((uOpMask & (AHO_SETDO|AHO_SETUN))) {
  1654.             TraceMsg(DM_HIDE, "cwb.dh: !_fCanHide => suppress AHO_SET*");
  1655.         }
  1656. #endif
  1657.     }
  1658.     return;
  1659. }
  1660. //***   SlideWindow -- sexy slide effect
  1661. // NOTES
  1662. //      stolen from tray.c
  1663. void SlideWindow(HWND hwnd, RECT *prc, HMONITOR hMonClip, BOOL fShow)
  1664. {
  1665.     RECT rcMonitor, rcClip;
  1666.     BOOL fRegionSet = FALSE; 
  1667.     SetRectEmpty(&rcMonitor);
  1668.     if (GetNumberOfMonitors() > 1)
  1669.     {
  1670.         GetMonitorRect(hMonClip, &rcMonitor);
  1671.         // aka ScreenToClient
  1672.         MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rcMonitor, 2);     }
  1673.     // Future: We could loop on the following code for the slide effect 
  1674.     IntersectRect(&rcClip, &rcMonitor, prc);
  1675.     if (!IsRectEmpty(&rcClip))
  1676.     {
  1677.         HRGN hrgnClip;
  1678.         // Change the clip region to be relative to the upper left corner of prc
  1679.         // NOTE: this is not converting rcClip to prc client coordinate
  1680.         OffsetRect(&rcClip, -prc->left, -prc->top);
  1681.         
  1682.         hrgnClip = CreateRectRgnIndirect(&rcClip);
  1683.         // LINTASSERT(hrgnClip || !hgnClip);    // 0 semi-ok for SetWindowRgn
  1684.         // nt5:149630: always repaint, o.w. auto-unhide BitBlt's junk
  1685.         // from hide position
  1686.         fRegionSet = SetWindowRgn(hwnd, hrgnClip, /*fRepaint*/TRUE);
  1687.     }
  1688.     MoveWindow(hwnd, prc->left, prc->top, RECTWIDTH(*prc), RECTHEIGHT(*prc), TRUE);
  1689.     // Turn off the region stuff if we don't hide any more
  1690.     if (fRegionSet && fShow)
  1691.         SetWindowRgn(hwnd, NULL, TRUE);
  1692.     return;
  1693. }
  1694. /***    AppBarQueryPos -- negotiate position
  1695.  * ENTRY/EXIT
  1696.  *      return  width (height) from docked edge to opposing edge
  1697.  */
  1698. int CDockingBar::AppBarQueryPos(RECT* prcOut, UINT uEdge, HMONITOR hMon, const RECT* prcReq,
  1699.     PAPPBARDATA pabd, BOOL fCommit)
  1700. {
  1701.     int iWH;
  1702.     ASSERT(ISWBM_DESKTOP());
  1703.     // snap to edge (in case another AppBar disappeared w/o us knowing),
  1704.     // readjust opposing side to reflect that snap,
  1705.     // and max out adjacent sides to fill up full strip.
  1706.     iWH = RectGetWH(prcReq, uEdge);
  1707.     
  1708.     RectXform(&(pabd->rc), RX_EDGE|RX_OPPOSE|RX_ADJACENT|(_fHiding ? RX_HIDE : 0), prcReq, NULL, iWH, uEdge, hMon);
  1709.     ASSERT(EqualRect(&(pabd->rc), prcReq));     // caller guarantees?
  1710.     // negotiate
  1711.     // if we're dragging we might not be registered yet (floating->docked)
  1712.     // in that case we'll just use the requested size (w/o negotiating).
  1713.     // ditto for if we're in the middle of a top/non-top mode switch.
  1714.     if (_fAppRegistered) {
  1715.         pabd->uEdge = uEdge;
  1716.         TraceMsg(DM_APPBAR, "cwb.abqp: call ABM_QUERYPOS");
  1717.         SHAppBarMessage(ABM_QUERYPOS, pabd);
  1718.     }
  1719.     // readjust opposing side to reflect the negotiation (which only
  1720.     // adjusts the moved edge-most side, not the opposing edge).
  1721.     // BUGBUG: (dli) need to find the right hmonitor to pass  in
  1722.     RectXform(prcOut, RX_OPPOSE, &(pabd->rc), NULL, iWH, uEdge, hMon);
  1723.     return RectGetWH(prcOut, uEdge);
  1724. }
  1725. //***   AppBarSetPos --
  1726. // NOTES
  1727. //      does *not* do _SetVRect and MoveWindow, that's up to caller
  1728. //
  1729. void CDockingBar::AppBarSetPos(UINT uEdge, const RECT* prcReq, PAPPBARDATA pabd)
  1730. {
  1731.     ASSERT(_eMode == WBM_TOPMOST);
  1732.     if (!_fCanHide && _fAppRegistered)
  1733.         AppBarSetPos0(uEdge, prcReq, pabd);
  1734.     return;
  1735. }
  1736. void CDockingBar::AppBarSetPos0(UINT uEdge, const RECT* prcReq, PAPPBARDATA pabd)
  1737. {
  1738.     CopyRect(&(pabd->rc), prcReq);
  1739.     pabd->uEdge = uEdge;
  1740.     TraceMsg(DM_APPBAR, "cwb.absp: call ABM_SETPOS");
  1741.     ASSERT(_fAppRegistered);
  1742.     SHAppBarMessage(ABM_SETPOS, pabd);
  1743.     // BUGBUG workaround explorer bug: during dragging we get:
  1744.     //  querypos*; wm_winposchanged; querypos; setpos
  1745.     // the lack of a wm_winposchanged at the end screws up the
  1746.     // autohide bring-to-top code.
  1747.     ASSERT(pabd->cbSize == sizeof(APPBARDATA));
  1748.     ASSERT(pabd->hWnd == _hwnd);
  1749.     TraceMsg(DM_APPBAR, "cwb.absp: call ABM_WINPOSCHGED");
  1750.     SHAppBarMessage(ABM_WINDOWPOSCHANGED, pabd);
  1751.     // n.b. _SetVRect and MoveWindow done by caller
  1752.     return;
  1753. }
  1754. //***   AppBarQuerySetPos --
  1755. //
  1756. void CDockingBar::AppBarQuerySetPos(RECT* prcOut, UINT uEdge, HMONITOR hMon, const RECT* prcReq,
  1757.     PAPPBARDATA pabd, BOOL fCommit)
  1758. {
  1759.     RECT rcTmp;
  1760.     if (prcOut == NULL)
  1761.         prcOut = &rcTmp;
  1762.     AppBarQueryPos(prcOut, uEdge, hMon, prcReq, pabd, fCommit);
  1763.     if (fCommit) {
  1764.         AppBarSetPos(uEdge, prcOut, pabd);
  1765.         ASSERT(EqualRect(prcOut, &(pabd->rc))); // callers assume prcOut correct
  1766.     }
  1767.     return;
  1768. }
  1769. void CDockingBar::_AppBarOnSize()
  1770. {
  1771.     RECT rc;
  1772.     APPBARDATA abd;
  1773.     ASSERT(_eMode == WBM_TOPMOST);
  1774.     ASSERT(ISABE_DOCK(_uSide));
  1775.     if (!_fAppRegistered)
  1776.         return;
  1777.     // don't commit until done
  1778.     if (_fDragging)
  1779.         return;
  1780.     abd.cbSize = sizeof(APPBARDATA);
  1781.     abd.hWnd = _hwnd;
  1782.     GetWindowRect(_hwnd, &rc);
  1783.     AppBarQuerySetPos(NULL, _uSide, _hMon, &rc, &abd, TRUE);
  1784.     return;
  1785. }
  1786. void CDockingBar::_RemoveToolbar(DWORD dwFlags)
  1787. {
  1788.     if (_ptbSite) {
  1789.         // WM_DESTROY will do _ChangeTopMost(WBM_NIL) for us
  1790.         IDockingWindowFrame* ptbframe;
  1791.         HRESULT hresT=_ptbSite->QueryInterface(IID_IDockingWindowFrame, (LPVOID*)&ptbframe);
  1792.         if (SUCCEEDED(hresT)) {
  1793.             AddRef();   // guard against self destruction
  1794.             ptbframe->RemoveToolbar(SAFECAST(this, IDockingWindow*), dwFlags);
  1795.             ptbframe->Release();
  1796.             Release();
  1797.         }
  1798.     } else {
  1799.         CloseDW(0);
  1800.     }
  1801. }
  1802. void CDockingBar::_AppBarOnCommand(UINT idCmd)
  1803. {
  1804.     UINT eModeNew;
  1805.     switch (idCmd) {
  1806.     case IDM_AB_TOPMOST:
  1807.         eModeNew = _eMode ^ WBM_TOPMOST;
  1808.         _MoveSizeHelper(eModeNew, _uSide, _hMon, NULL, NULL, TRUE, TRUE);
  1809.         break;
  1810.     case IDM_AB_AUTOHIDE:
  1811.         if (_fWantHide)
  1812.         {
  1813.             // on->off
  1814.             _DoHide(AHO_KILLDO|AHO_UNREG);      // _ChangeHide
  1815.             _fWantHide = FALSE;
  1816.         }
  1817.         else
  1818.         {
  1819.             // off->on
  1820.             _fWantHide = TRUE;
  1821.             // don't do AHO_SETDO now, wait for WM_ACTIVATE(deactivate)
  1822.             _DoHide(AHO_REG);     // _ChangeHide
  1823.         }
  1824.         // force it to happen *now*
  1825.         // BUGBUG potential race condition w/ the AHO_SETDO above,
  1826.         // but worst case that should cause a 2nd redraw (?).
  1827.         _Recalc();  // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  1828.         if (SHIsChildOrSelf(GetActiveWindow(), _hwnd) != S_OK)
  1829.         {
  1830.             // nt5:148444: if we're already deactive, we need to kick off
  1831.             // the hide now.  this is needed e.g. for login when we load
  1832.             // persisted auto-hide deskbars.  they come up inactive so we
  1833.             // never get the initial deact to hide them.
  1834.             _OnActivate(MAKEWPARAM(WA_INACTIVE, FALSE), (LPARAM)(HWND)0);
  1835.         }
  1836.         break;
  1837. #ifdef DEBUG
  1838.     case IDM_AB_ACTIVATE:
  1839.         // BUGBUG temporary until we make browser tell us about activation
  1840.         // note that since we're faking this w/ a menu our (normal) assumption
  1841.         // in WM_ENTERMENU is bogus so make sure you keep the mouse over
  1842.         // the BrowserBar during activation or it will hide away out from under
  1843.         // you and the Activate won't work...
  1844.         _OnActivate(MAKEWPARAM(_fActive ? WA_INACTIVE : WA_ACTIVE, FALSE),
  1845.             (LPARAM) (HWND) 0);
  1846.         _fActive = !_fActive;
  1847.         break;
  1848. #endif
  1849.     case IDM_AB_CLOSE:
  1850.         _OnCloseBar(TRUE);
  1851.         break;
  1852.     default:
  1853.         MessageBeep(0);
  1854.         break;
  1855.     }
  1856. }
  1857. BOOL CDockingBar::_OnCloseBar(BOOL fConfirm)
  1858. {
  1859.     _RemoveToolbar(0);
  1860.     return TRUE;
  1861. }
  1862. void CDockingBar::_AppBarOnWM(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1863. {
  1864.     switch (uMsg) {
  1865.     case WM_WINDOWPOSCHANGED:
  1866.     case WM_ACTIVATE:
  1867.         {
  1868.             APPBARDATA abd;
  1869.             abd.cbSize = sizeof(APPBARDATA);
  1870.             abd.hWnd = _hwnd;
  1871.             abd.lParam = (long) NULL;
  1872.             if (uMsg == WM_WINDOWPOSCHANGED) {
  1873.                 TraceMsg(DM_APPBAR, "cwb.WM_WPC: call ABM_WINPOSCHGED");
  1874.                 SHAppBarMessage(ABM_WINDOWPOSCHANGED, &abd);
  1875.             }
  1876.             else {
  1877.                 //if (LOWORD(wParam) != WA_INACTIVE)
  1878.                 // just do it always, doesn't hurt...
  1879.                 TraceMsg(DM_APPBAR, "cwb.WM_ACT: call ABM_ACTIVATE");
  1880.                 SHAppBarMessage(ABM_ACTIVATE, &abd);
  1881.             }
  1882.         }
  1883.         break;
  1884.     default:
  1885.         ASSERT(0);
  1886.         break;
  1887.     }
  1888.     return;
  1889. }
  1890. // try to preserve our thinkness
  1891. void CDockingBar::_AppBarOnPosChanged(PAPPBARDATA pabd)
  1892. {
  1893.     RECT rcWindow;
  1894.     ASSERT(_eMode == WBM_TOPMOST);
  1895.     GetWindowRect(pabd->hWnd, &rcWindow);
  1896.     RectXform(&rcWindow, RX_EDGE|RX_OPPOSE, &rcWindow, NULL, RectGetWH(&rcWindow, _uSide), _uSide, _hMon);
  1897.     _Recalc();  // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  1898.     return;
  1899. }
  1900. /***    _InitPos4 -- initialize edge positions
  1901.  * ENTRY/EXIT
  1902.  *  fCtor       TRUE if called from constructor; o.w. FALSE
  1903.  */
  1904. void CDockingBar::_InitPos4(BOOL fCtor)
  1905. {
  1906.     RECT rcSite;
  1907.     TraceMsg(DM_PERSIST, "cdb.ip4(fCtor=%d) enter", fCtor);
  1908.     if (fCtor)
  1909.     {
  1910.         // set some worst-case defaults for the Load(bag) case
  1911.         _adEdge[ABE_TOP]    = 80;
  1912.         _adEdge[ABE_BOTTOM] = 80;
  1913.         _adEdge[ABE_LEFT]   = 80;
  1914.         _adEdge[ABE_RIGHT]  = 80;
  1915.         SetRect(&_rcFloat, 10, 10, 310, 310);       // BUGBUG todo: NYI
  1916.         _hMon = GetPrimaryMonitor();
  1917.     }
  1918.     else
  1919.     {
  1920.         // set up semi-reasonable defaults for the InitNew case
  1921.         ASSERT(_eInitLoaded == IPS_INITNEW);    // not req'd, but expected
  1922.         ASSERT(IsWindow(_hwndSite));
  1923.         GetWindowRect(_hwndSite, &rcSite);
  1924.         _adEdge[ABE_TOP]    = AB_THEIGHT(rcSite);
  1925.         _adEdge[ABE_BOTTOM] = AB_BHEIGHT(rcSite);
  1926.         _adEdge[ABE_LEFT]   = AB_LWIDTH(rcSite);
  1927.         _adEdge[ABE_RIGHT]  = AB_RWIDTH(rcSite);
  1928.         
  1929.         // BUGBUG: (dli) should we ask _hwndSite for it's hmonitor?
  1930.         _hMon = MonitorFromRect(&rcSite, MONITOR_DEFAULTTONULL);
  1931.         if (!_hMon)
  1932.         {
  1933.             POINT ptCenter;
  1934.             ptCenter.x = (rcSite.left + rcSite.right) / 2;
  1935.             ptCenter.y = (rcSite.top + rcSite.bottom) / 2;
  1936.             _hMon = MonitorFromPoint(ptCenter, MONITOR_DEFAULTTONEAREST);
  1937.         }
  1938.     }
  1939.     return;
  1940. }
  1941. /***    RectXform -- transform RECT
  1942.  * ENTRY/EXIT
  1943.  *      prcOut
  1944.  *      uRxMask
  1945.  *      prcIn           initial rect
  1946.  *      prcBound        bounding rect specifying min/max dimensions
  1947.  *      iWH
  1948.  *      uSide
  1949.  * DESCRIPTION
  1950.  *      RX_EDGE         set edgemost side  to extreme (0 or max)
  1951.  *      RX_OPPOSE       set opposing side  to edge + width
  1952.  *      RX_ADJACENT     set adjacent sides to extremes (0 and max)
  1953.  *      RX_GETWH        get distance to opposing side
  1954.  *
  1955.  *      Two common calls are:
  1956.  *      ...
  1957.  * NOTES
  1958.  *      Note that rcOut, rcIn, and rcSize can all be the same.
  1959.  *      BUGBUG do we ever use rcIn or can it be rcBound (or NULL)?
  1960.  */
  1961. int CDockingBar::RectXform(RECT* prcOut, UINT uRxMask,
  1962.     const RECT* prcIn, RECT* prcBound, int iWH, UINT uSide, HMONITOR hMon)
  1963. {
  1964.     RECT rcDef;
  1965.     int  iRet = 0;
  1966.     BOOL bMirroredWnd=FALSE;
  1967.     if (prcOut != prcIn && prcOut != NULL) {
  1968.         ASSERT(prcIn != NULL);  // used to do SetRect(prcOut,0,0,0,0)
  1969.         CopyRect(prcOut, prcIn);
  1970.     }
  1971. #ifdef DEBUG
  1972.     if (! (uRxMask & (RX_OPPOSE|RX_GETWH))) {
  1973.         ASSERT(iWH == -1);
  1974.         iWH = -1;       // try to force something to go wrong...
  1975.     }
  1976. #endif
  1977.     if (uRxMask & (RX_EDGE|RX_ADJACENT)) {
  1978.         if (prcBound == NULL) {
  1979.             prcBound = &rcDef;
  1980.             ASSERT(hMon);
  1981.             GetMonitorRect(hMon, prcBound);     // aka GetSystemMetrics(SM_CXSCREEN)
  1982.         }
  1983.         #define iXMin (prcBound->left)
  1984.         #define iYMin (prcBound->top)
  1985.         #define iXMax (prcBound->right);
  1986.         #define iYMax (prcBound->bottom);
  1987.     }
  1988.     if (uRxMask & (RX_EDGE|RX_OPPOSE|RX_HIDE|RX_GETWH)) {
  1989.         //
  1990.         // If docking is happening on a horizontal size, then...
  1991.         // 
  1992.         if ((ABE_LEFT == uSide) || (ABE_RIGHT == uSide)) {
  1993.             bMirroredWnd = (IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd)));
  1994.         }
  1995.         switch (uSide) {
  1996.         case ABE_TOP:
  1997.             if (uRxMask & RX_EDGE)
  1998.                 prcOut->top = iYMin;
  1999.             if (uRxMask & RX_OPPOSE)
  2000.                 prcOut->bottom = prcOut->top + iWH;
  2001.             if (uRxMask & RX_HIDE)
  2002.                 MoveRect(prcOut, prcOut->left, prcOut->top - iWH + CXYHIDE(uSide));
  2003.             if (uRxMask & RX_GETWH)
  2004.                 iRet = RECTHEIGHT(*prcIn);
  2005.             break;
  2006.         case ABE_BOTTOM:
  2007.             if (uRxMask & RX_EDGE)
  2008.                 prcOut->bottom = iYMax;
  2009.             if (uRxMask & RX_OPPOSE)
  2010.                 prcOut->top = prcOut->bottom - iWH;
  2011.             if (uRxMask & RX_HIDE)
  2012.                 MoveRect(prcOut, prcOut->left, prcOut->bottom - CXYHIDE(uSide));
  2013.             if (uRxMask & RX_GETWH)
  2014.                 iRet = RECTHEIGHT(*prcIn);
  2015.             break;
  2016.         case ABE_LEFT:
  2017.             if (uRxMask & RX_EDGE)
  2018.                 prcOut->left = iXMin;
  2019.             if (uRxMask & RX_OPPOSE) {
  2020.                 //
  2021.                 // If the parent of this docked window is mirrored, then it is placed and
  2022.                 // aligned to the right. [samera]
  2023.                 //
  2024.                 if (bMirroredWnd)
  2025.                     prcOut->left = prcOut->right - iWH;
  2026.                 else
  2027.                     prcOut->right = prcOut->left + iWH;
  2028.             }
  2029.             if (uRxMask & RX_HIDE)
  2030.                 MoveRect(prcOut, prcOut->left - iWH + CXYHIDE(uSide), prcOut->top);
  2031.             if (uRxMask & RX_GETWH)
  2032.                 iRet = RECTWIDTH(*prcIn);
  2033.             break;
  2034.         case ABE_RIGHT:
  2035.             if (uRxMask & RX_EDGE)
  2036.                 prcOut->right = iXMax;
  2037.             if (uRxMask & RX_OPPOSE) {
  2038.                 //
  2039.                 // If the parent of this docked window is mirrored, then it is placed and
  2040.                 // aligned to the left
  2041.                 //
  2042.                 if (bMirroredWnd)
  2043.                     prcOut->right = prcOut->left + iWH;
  2044.                 else
  2045.                     prcOut->left = prcOut->right - iWH;
  2046.             }
  2047.             if (uRxMask & RX_HIDE)
  2048.                 MoveRect(prcOut, prcOut->right - CXYHIDE(uSide), prcOut->top);
  2049.             if (uRxMask & RX_GETWH)
  2050.                 iRet = RECTWIDTH(*prcIn);
  2051.             break;
  2052.         }
  2053.     }
  2054.     if (uRxMask & RX_ADJACENT) {
  2055.         if (uSide == ABE_LEFT || uSide == ABE_RIGHT) {
  2056.             prcOut->top    = iYMin;
  2057.             prcOut->bottom = iYMax;
  2058.         }
  2059.         else {
  2060.             prcOut->left   = iXMin;
  2061.             prcOut->right  = iXMax;
  2062.         }
  2063.     }
  2064.     return iRet;
  2065. }
  2066. //***   _ProtoRect -- create best-guess proto rect for specified location
  2067. //
  2068. void CDockingBar::_ProtoRect(RECT* prcOut, UINT eModeNew, UINT uSideNew, HMONITOR hMonNew, POINT* ptXY)
  2069. {
  2070.     if (ISWBM_FLOAT(eModeNew))
  2071.     {
  2072.         // start at last position/size, and move to new left-top if requested
  2073.         CopyRect(prcOut, &_rcFloat);
  2074.         if (ptXY != NULL)
  2075.             MoveRect(prcOut, ptXY->x, ptXY->y);
  2076.         // if we're (e.g.) floating on the far right and the display shrinks,
  2077.         // we need to reposition ourselves
  2078.         // BUGBUG perf: wish we could do this at resolution-change time but
  2079.         // WM_DISPLAYCHANGE comes in too early (before our [pseudo] parent
  2080.         // has changed).
  2081.         if (eModeNew == WBM_FLOATING)
  2082.         {
  2083.             // make sure we're still visible
  2084.             // BUGBUG todo: multi-mon
  2085.             RECT rcTmp;
  2086.             _GetBorderRect(hMonNew, &rcTmp);
  2087.             if (prcOut->left > rcTmp.right || prcOut->top > rcTmp.bottom)
  2088.             {
  2089.                 // BUGBUG note we don't explicitly account for other toolbars
  2090.                 // this may be a bug (though other apps seem to behave the
  2091.                 // same way)
  2092.                 MoveRect(prcOut,
  2093.                     prcOut->left <= rcTmp.right ? prcOut->left :
  2094.                         rcTmp.right - CXFLOAT(),
  2095.                     prcOut->top  <= rcTmp.bottom ? prcOut->top  :
  2096.                         rcTmp.bottom - CYFLOAT()
  2097.                 );
  2098.             }
  2099.         }
  2100.     }
  2101.     else
  2102.     {
  2103.         ASSERT(ISABE_DOCK(uSideNew));
  2104.         if (_fCanHide && ISWBM_HIDEABLE(eModeNew))
  2105.         {
  2106.             // force a 'tiny' rectangle
  2107.             // (BUGBUG prcBound==NULL bogus for XXX_HIDEALL && XXX_BROWSEROWNED)
  2108.             RectXform(prcOut, RX_EDGE|RX_OPPOSE|RX_ADJACENT|(_fHiding ? RX_HIDE : 0),
  2109.                 prcOut, NULL, _adEdge[uSideNew], uSideNew, hMonNew);
  2110.         }
  2111.         else
  2112.         {
  2113.             // get current rect, adjust opposing side per request
  2114.             _GetBorderRect(hMonNew, prcOut);    //BUGBUG _GetClientRect(prcOut);
  2115.             RectXform(prcOut, RX_OPPOSE, prcOut, NULL, _adEdge[uSideNew], uSideNew, hMonNew);
  2116.         }
  2117.     }
  2118.     return;
  2119. }
  2120. //***   _NegotiateRect --
  2121. // NOTES
  2122. //      will only return an approximate result in the non-commit case.
  2123. //
  2124. void CDockingBar::_NegotiateRect(UINT eModeNew, UINT uSideNew, HMONITOR hMonNew,
  2125.     RECT* rcReq, BOOL fCommit)
  2126. {
  2127.     switch (eModeNew) {
  2128.     case WBM_TOPMOST:
  2129.         APPBARDATA abd;
  2130.         abd.cbSize = sizeof(APPBARDATA);
  2131.         abd.hWnd = _hwnd;
  2132.         AppBarQuerySetPos(rcReq, uSideNew, hMonNew, rcReq, &abd, fCommit);
  2133.         if (_fCanHide)
  2134.         {
  2135.             // we did a query to adjust the adjacent sides (e.g. so we don't
  2136.             // cover up the 'start' menu when we unhide).  however that may
  2137.             // have also moved us in from the edge, which we don't want.
  2138.             // so snap back to edge.
  2139.             int iWH;
  2140.             iWH = RectGetWH(rcReq, uSideNew);
  2141.             RectXform(rcReq, RX_EDGE|RX_OPPOSE|(_fHiding ? RX_HIDE : 0), rcReq, NULL, iWH, uSideNew, hMonNew);
  2142.         }
  2143.         goto Ldefault;
  2144.     default:
  2145.     Ldefault:
  2146.         // everyone else just gives us what we want
  2147.         // but, we need to free up border
  2148.         _NegotiateBorderRect(NULL, NULL, fCommit);     // free up space
  2149.         break;
  2150.     case WBM_BOTTOMMOST:
  2151.     case WBM_BBOTTOMMOST:
  2152.         _NegotiateBorderRect(rcReq, rcReq, fCommit);
  2153.         break;
  2154.     }
  2155.     return;
  2156. }
  2157. void CDockingBar::_AppBarCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2158. {
  2159.     APPBARDATA abd;
  2160.     ASSERT(_eMode == WBM_TOPMOST);
  2161.     abd.cbSize = sizeof(abd);
  2162.     abd.hWnd = hwnd;
  2163.     switch (wParam) {
  2164.     case ABN_FULLSCREENAPP:
  2165.         // when 1st  app goes   full-screen, move ourselves to BOTTOM;
  2166.         // when last app leaves full-screen, move ourselves back
  2167.         // todo: FullScreen(flg)
  2168.         SetWindowPos(hwnd,
  2169.             lParam ? HWND_BOTTOM : HWND_TOPMOST,
  2170.             0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  2171.         break;
  2172.     case ABN_POSCHANGED:
  2173.         TraceMsg(DM_APPBAR, "cwb.abcb: ABN_POSCHANGED");
  2174.         // note that we do this even if _fHiding.  while we want
  2175.         // to stay snapped to the edge as a 'tiny' rect, a change
  2176.         // in someone else *should* effect our adjacent edges.
  2177.         //
  2178.         // BUGBUG unfortunately this currently causes 'jiggle' of a hidden
  2179.         // guy when another appbar moves (due to a SlideWindow of a 0-width
  2180.         // hidden guy and a rounded-up 8-pixel wide guy).  when we switch
  2181.         // to explorer's new offscreen hide that should go away.
  2182.         _AppBarOnPosChanged(&abd);
  2183.         break;
  2184.     }
  2185.     return;
  2186. }
  2187. HRESULT CDockingBar::QueryInterface(REFIID riid, LPVOID * ppvObj)
  2188. {
  2189.     static const QITAB qit[] = {
  2190.         QITABENT(CDockingBar, IDockingWindow),        // IID_IDockingWindow
  2191.         QITABENT(CDockingBar, IObjectWithSite),       // IID_IObjectWithSite
  2192.         QITABENT(CDockingBar, IPersistStreamInit),    // IID_IPersistStreamInit
  2193.         QITABENTMULTI(CDockingBar, IPersistStream, IPersistStreamInit), // IID_IPersistStream
  2194.         QITABENTMULTI(CDockingBar, IPersist, IPersistStreamInit), // IID_IPersist
  2195.         QITABENT(CDockingBar, IPersistPropertyBag),   // IID_IPersistPropertyBag
  2196.         { 0 },
  2197.     };
  2198.     HRESULT hres = QISearch(this, qit, riid, ppvObj);
  2199.     if (FAILED(hres))
  2200.         hres = SUPERCLASS::QueryInterface(riid, ppvObj);
  2201.     return hres;
  2202. }
  2203. HRESULT CDockingBar::QueryService(REFGUID guidService,
  2204.                                 REFIID riid, void **ppvObj)
  2205. {
  2206.     HRESULT hres = E_FAIL;
  2207.     *ppvObj = NULL; // assume error
  2208.     //  Block IID_ITargetFrame, so we don't look like a frame of the
  2209.     //  window we are attached to
  2210.     if (IsEqualGUID(guidService, IID_ITargetFrame)
  2211.         ||IsEqualGUID(guidService, IID_ITargetFrame2)) {
  2212.         return hres;
  2213.     }
  2214.     hres = SUPERCLASS::QueryService(guidService, riid, ppvObj);
  2215.     if (FAILED(hres))
  2216.     {
  2217.         const GUID* pguidService = &guidService;
  2218.     
  2219.         if (IsEqualGUID(guidService, SID_SProxyBrowser)) {
  2220.             pguidService = &SID_STopLevelBrowser;
  2221.         }
  2222.     
  2223.         if (_ptbSite) {
  2224.             hres = IUnknown_QueryService(_ptbSite, *pguidService, riid, ppvObj);
  2225.         }
  2226.     }
  2227.     return hres;
  2228. }
  2229. void CDockingBar::_GrowShrinkBar(DWORD dwDirection)
  2230. {
  2231.     RECT    rcNew, rcOld;
  2232.     int     iMin;
  2233.     iMin = GetSystemMetrics(SM_CXVSCROLL) * 4;
  2234.     GetWindowRect(_hwnd, &rcNew);   
  2235.     rcOld = rcNew;
  2236.     
  2237.     switch(_uSide)
  2238.     {
  2239.         case ABE_TOP:
  2240.             if (VK_DOWN == dwDirection)
  2241.                 rcNew.bottom += GetSystemMetrics(SM_CYFRAME);
  2242.             if (VK_UP == dwDirection)
  2243.                 rcNew.bottom -= GetSystemMetrics(SM_CYFRAME);
  2244.             if ((rcNew.bottom - rcNew.top) < iMin)
  2245.                 rcNew.bottom = rcNew.top + iMin;
  2246.             break;
  2247.         case ABE_BOTTOM:
  2248.             if (VK_UP == dwDirection)
  2249.                 rcNew.top -= GetSystemMetrics(SM_CYFRAME);
  2250.             if (VK_DOWN == dwDirection)
  2251.                 rcNew.top += GetSystemMetrics(SM_CYFRAME);
  2252.             if ((rcNew.bottom - rcNew.top) < iMin)
  2253.                 rcNew.top = rcNew.bottom - iMin;
  2254.             break;
  2255.         case ABE_LEFT:
  2256.             if (VK_RIGHT == dwDirection)
  2257.                 rcNew.right += GetSystemMetrics(SM_CXFRAME);
  2258.             if (VK_LEFT == dwDirection)
  2259.                 rcNew.right -= GetSystemMetrics(SM_CXFRAME);
  2260.             if ((rcNew.right - rcNew.left) < iMin)
  2261.                 rcNew.right = rcNew.left + iMin;
  2262.             break;
  2263.             
  2264.         case ABE_RIGHT:
  2265.             if (VK_LEFT == dwDirection)
  2266.                 rcNew.left -= GetSystemMetrics(SM_CXFRAME);
  2267.             if (VK_RIGHT == dwDirection)
  2268.                 rcNew.left += GetSystemMetrics(SM_CXFRAME);
  2269.             if ((rcNew.right - rcNew.left) < iMin)
  2270.                 rcNew.left = rcNew.right - iMin;
  2271.             break;
  2272.     }
  2273.     if (!EqualRect(&rcOld, &rcNew))
  2274.     {
  2275.         int iWH;
  2276.         RECT rcScreen;
  2277.         // don't let the new size get > MonitorRect/2
  2278.         GetMonitorRect(_hMon, &rcScreen);   // aka GetSystemMetrics(SM_CXSCREEN)
  2279.         iWH = RECTGETWH(_uSide, &rcScreen);
  2280.         iWH /= 2;
  2281.         if (RECTGETWH(_uSide, &rcNew) > iWH) 
  2282.         {
  2283.             RectXform(&rcNew, RX_OPPOSE, &rcNew, NULL, iWH, _uSide, NULL);
  2284.         }
  2285.         _SetVRect(&rcNew);
  2286.         _Recalc();
  2287.     }
  2288. }
  2289. //*** CDockingBar::IOleCommandTarget::* {
  2290. HRESULT CDockingBar::Exec(const GUID *pguidCmdGroup,
  2291.     DWORD nCmdID, DWORD nCmdexecopt,
  2292.     VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  2293. {
  2294.     if (pguidCmdGroup == NULL) {
  2295.         /*NOTHING*/
  2296.     }
  2297.     else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup)) {
  2298.         switch (nCmdID) {
  2299.         case SHDVID_RAISE:
  2300.             ASSERT(pvarargIn && pvarargIn->vt == VT_I4);
  2301.             if (pvarargIn->vt == VT_I4 && pvarargIn->lVal != DTRF_QUERY) {
  2302.                 _OnRaise(pvarargIn->lVal);
  2303.                 return S_OK;
  2304.             }
  2305.             break;  // e.g. DTRF_QUERY
  2306.         default:
  2307.             // note that this means we may get OLECMDERR_E_UNKNOWNGROUP
  2308.             // rather than OLECMDERR_E_NOTSUPPORTED for unhandled guys...
  2309.             break;
  2310.         }
  2311.     } 
  2312.     else if (IsEqualGUID(CGID_DeskBarClient, *pguidCmdGroup)) 
  2313.     {
  2314.         if (DBCID_RESIZE == nCmdID)
  2315.         {
  2316.             _GrowShrinkBar(nCmdexecopt);
  2317.             return S_OK;
  2318.         }
  2319.     }
  2320.     
  2321.     return SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt,
  2322.         pvarargIn, pvarargOut);
  2323. }
  2324. // }
  2325. //*** CDockingBar::IDockingWindow::* {
  2326. //
  2327. HRESULT CDockingBar::SetSite(IUnknown* punkSite)
  2328. {
  2329.     ATOMICRELEASE(_ptbSite);
  2330.     if (punkSite)
  2331.     {
  2332.         HRESULT hresT;
  2333.         hresT = punkSite->QueryInterface(IID_IDockingWindowSite, (LPVOID*)&_ptbSite);
  2334.         IUnknown_GetWindow(punkSite, &_hwndSite);
  2335.         //
  2336.         // Check if we are under the desktop browser or not and set
  2337.         // the initial state correctly. (Always on top for Desktop)
  2338.         //
  2339.         IUnknown* punkT;
  2340.         hresT = punkSite->QueryInterface(SID_SShellDesktop, (LPVOID*)&punkT);
  2341.         if (SUCCEEDED(hresT))
  2342.         {
  2343.             _fDesktop = TRUE;
  2344.             punkT->Release();
  2345.         }
  2346.         if (!_fInitSited)
  2347.         {
  2348.             if (!_eInitLoaded)
  2349.             {
  2350.                 // if we haven't initialized, do it now.
  2351.                 InitNew();
  2352.                 _eMode = WBM_BOTTOMMOST;
  2353.             }
  2354.                 
  2355.             ASSERT(_eInitLoaded);
  2356.             if (_eInitLoaded == IPS_INITNEW)
  2357.             {
  2358.                 _InitPos4(FALSE);
  2359.                 _eMode = _fDesktop ? WBM_TOPMOST : WBM_BBOTTOMMOST;
  2360.             }
  2361.         }
  2362.         ASSERT(_eMode != WBM_NIL);
  2363.         // BUGBUG actually we could also be owned floating...
  2364.         ASSERT(ISWBM_DESKTOP() == _fDesktop);
  2365.         ASSERT(_fDesktop || _eMode == WBM_BBOTTOMMOST);
  2366.         ASSERT(ISWBM_DESKTOP() == _fDesktop);
  2367.         ASSERT(ISWBM_DESKTOP() || _eMode == WBM_BBOTTOMMOST);
  2368.     }
  2369.     _fInitSited = TRUE;     // done w/ 1st-time init
  2370.     return S_OK;
  2371. }
  2372. HRESULT CDockingBar::ShowDW(BOOL fShow)
  2373. {
  2374.     fShow = BOOLIFY(fShow);     // so comparisons and assigns to bitfields work
  2375.     // we used to early out if BOOLIFY(_fShow) == fShow.
  2376.     // however we now count on ShowDW(TRUE) to force a refresh
  2377.     // (e.g. when screen resolution changes CBB::v_ShowHideChildWindows
  2378.     // calls us)
  2379.     if (BOOLIFY(_fShow) == fShow)
  2380.         return S_OK;
  2381.     _fShow = fShow;
  2382.     if (!_fInitShowed)
  2383.     {
  2384.         ASSERT(_fInitSited && _eInitLoaded);
  2385.         _Initialize();
  2386.         ASSERT(_fInitShowed);
  2387.     }
  2388.     if (_fShow)
  2389.     {
  2390.         // BUGBUG _ChangeTopMost does this...
  2391.         // Tell itself to resize.
  2392.         // use _MoveSizeHelper (not just _NegotiateBorderRect) since we might
  2393.         // actually be moving to a new position...
  2394.         _Recalc();  // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  2395.         // _NegotiateBorderRect(NULL, NULL, FALSE)
  2396.         if (EVAL(_pDBC))
  2397.             _pDBC->UIActivateDBC(DBC_SHOW);
  2398.         // nt5:148444: SW_SHOWNA (vs. SW_SHOW) so we don't unhide on create
  2399.         // this fix will cause a new bug -- newly created bars don't have
  2400.         // focus (e.g. drag a band to floating, the new floating bar won't
  2401.         // have focus) -- but that should be the lesser of evils.
  2402.         //ShowWindow(_hwnd, ISWBM_FLOAT(_eMode) ? SW_SHOWNORMAL : SW_SHOWNA);
  2403.         ShowWindow(_hwnd, SW_SHOWNA);
  2404.         _OnSize();
  2405.     }
  2406.     else
  2407.     {
  2408.         ShowWindow(_hwnd, SW_HIDE);
  2409.         if (EVAL(_pDBC))
  2410.             _pDBC->UIActivateDBC(DBC_SHOWOBSCURE);
  2411.         UIActivateIO(FALSE, NULL);
  2412.         
  2413.         // Tell itself to resize.
  2414.         // don't call MoveSizeHelper here since it will do (e.g.)
  2415.         // negotiation, which will cause flicker and do destructive stuff.
  2416.         //_Recalc();  //_MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  2417.         _NegotiateBorderRect(NULL, NULL, TRUE);     // hide=>0 border space
  2418.     }
  2419.     return S_OK;
  2420. }
  2421. HRESULT CDockingBar::ResizeBorderDW(LPCRECT prcBorder,
  2422.     IUnknown* punkToolbarSite, BOOL fReserved)
  2423. {
  2424.     _Recalc();  // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
  2425.     return S_OK;    // BUGBUG _NegotiateBorderRect()?
  2426. }
  2427. HRESULT CDockingBar::_NegotiateBorderRect(RECT* prcOut, RECT* prcReq, BOOL fCommit)
  2428. {
  2429.     UINT eMode, uSide;
  2430.     HMONITOR hMon;
  2431.     int iWH;
  2432.     // BUGBUG should be params like MSH etc.
  2433.     eMode = ((_fDragging == DRAG_MOVE) ? _eModePending : _eMode);
  2434.     uSide = ((_fDragging == DRAG_MOVE) ? _uSidePending : _uSide);
  2435.     hMon = ((_fDragging == DRAG_MOVE) ? _hMonPending : _hMon);
  2436.     if (prcOut != prcReq && prcOut != NULL && prcReq != NULL)
  2437.         CopyRect(prcOut, prcReq);
  2438.     if (_ptbSite) {
  2439.         RECT rcRequest = { 0, 0, 0, 0 };
  2440.         if (_fShow && ISWBM_BOTTOM(eMode)) {
  2441.             
  2442.             iWH = RectGetWH(prcReq, uSide);
  2443.             ASSERT(iWH == _adEdge[uSide]);
  2444.             if ((!_fCanHide) && uSide != ABE_NIL)
  2445.                 ((int*)&rcRequest)[uSide] = iWH;
  2446.                 
  2447.             if (_fTheater) {
  2448.                 // MOVE TO CBROWSERBAR
  2449.                 
  2450.                 // we override the left that we request from the browser, but
  2451.                 // we need to notify theater what the user has requested for the expaneded width
  2452.                 VARIANTARG v = { 0 };
  2453.                 v.vt = VT_I4;
  2454.                 v.lVal = rcRequest.left;
  2455.                 IUnknown_Exec(_ptbSite, &CGID_Theater, THID_SETBROWSERBARWIDTH, 0, &v, NULL);
  2456.                 _iTheaterWidth = v.lVal;
  2457.                 
  2458.                 // if we're in theater mode, we can only be on the left and we only grab left border
  2459.                 ASSERT(uSide == ABE_LEFT);
  2460.                 // if we're in autohide mode, we request no space
  2461.                 if (!_fNoAutoHide)
  2462.                     rcRequest.left = 0;
  2463.                 // END MOVE TO CBROWSERBAR                
  2464.             }
  2465.         }
  2466.         // BUGBUG leave alone (at 0 from HideRegister?) if _fHiding==HIDE_AUTO
  2467.         HMONITOR hMonOld = _SetNewMonitor(hMon);  
  2468.         _ptbSite->RequestBorderSpaceDW(SAFECAST(this, IDockingWindow*), &rcRequest);
  2469.         if (fCommit) {
  2470.             RECT rcMirRequest;
  2471.             LPRECT lprcRequest = &rcRequest; 
  2472.             if (IS_WINDOW_RTL_MIRRORED(_hwnd) && 
  2473.                 !IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd))) {
  2474.                 // Swap left and right.
  2475.                 rcMirRequest.left   = rcRequest.right;
  2476.                 rcMirRequest.right  = rcRequest.left;
  2477.                 rcMirRequest.top    = rcRequest.top;
  2478.                 rcMirRequest.bottom = rcRequest.bottom;
  2479.                 lprcRequest = &rcMirRequest;
  2480.             }
  2481.             _ptbSite->SetBorderSpaceDW(SAFECAST(this, IDockingWindow*), lprcRequest);
  2482.         }
  2483.         if (_fShow && ISWBM_BOTTOM(eMode) && !_fTheater) {
  2484.             // were'd we end up (as a real rect not just a size)?
  2485.             // start w/ our full border area, then apply negotiated width.
  2486.             // however that may have also moved us in from the edge, which
  2487.             // we don't want if we're autohide, so snap back to edge if so.
  2488.             _ptbSite->GetBorderDW(SAFECAST(this, IDockingWindow*), prcOut);
  2489.             // aka ClientToScreen
  2490.             MapWindowPoints(_hwndSite, HWND_DESKTOP, (POINT*) prcOut, 2);
  2491.             if ((!_fCanHide) && uSide != ABE_NIL)
  2492.                 iWH = ((int*)&rcRequest)[uSide];
  2493.             
  2494.             RectXform(prcOut, (_fCanHide ? RX_EDGE : 0)|RX_OPPOSE|(_fHiding ? RX_HIDE : 0), prcOut, NULL, iWH, uSide, hMon);
  2495.         }
  2496.         
  2497.         if (hMonOld)
  2498.             _SetNewMonitor(hMonOld);
  2499.     }
  2500.     return S_OK;
  2501. }
  2502. // }
  2503. //*** CDockingBar::IPersistStream*::* {
  2504. //
  2505. HRESULT CDockingBar::IsDirty(void)
  2506. {
  2507.     return S_FALSE; // Never be dirty
  2508. }
  2509. //
  2510. // Persisted CDockingBar
  2511. //
  2512. struct SWebBar
  2513. {
  2514.     DWORD   cbSize;
  2515.     DWORD   cbVersion;
  2516.     UINT    uSide : 3;
  2517.     UINT    fWantHide :1;
  2518.     INT     adEdge[4];  // BUGBUG wordsize dependent
  2519.     RECT    rcFloat;    // BUGBUG ...
  2520.     POINT   ptSiteCenter; // Center of the docking site -- in case of multiple docking sites
  2521.     UINT    eMode;
  2522.     UINT    fAlwaysOnTop;
  2523.     RECT    rcChild;
  2524. };
  2525. #define SWB_VERSION 8
  2526. HRESULT CDockingBar::Load(IStream *pstm)
  2527. {
  2528.     SWebBar swb = {0};
  2529.     ULONG cbRead;
  2530.     TraceMsg(DM_PERSIST, "cwb.l enter(this=%x pstm=%x) tell()=%x", this, pstm, DbStreamTell(pstm));
  2531.     ASSERT(!_eInitLoaded);
  2532.     HRESULT hres = pstm->Read(&swb, SIZEOF(swb), &cbRead);
  2533. #ifdef DEBUG
  2534.     // just in case we toast ourselves (offscreen or something)...
  2535.     static BOOL fNoPersist = FALSE;
  2536.     if (fNoPersist)
  2537.         hres = E_FAIL;
  2538. #endif
  2539.     
  2540.     if (hres==S_OK && cbRead==SIZEOF(swb)) {
  2541.         // BUGBUG: this is not forward compatible!
  2542.         if (swb.cbSize==SIZEOF(SWebBar) && swb.cbVersion==SWB_VERSION) {
  2543.             _eMode = swb.eMode;
  2544.             _uSide = swb.uSide;
  2545.             _hMon  = MonitorFromPoint(swb.ptSiteCenter, MONITOR_DEFAULTTONEAREST);
  2546.             // don't call _SetModeSide, _MoveSizeHelper, etc. until *after* _Initialize
  2547.             _fWantHide = swb.fWantHide;
  2548.             memcpy(_adEdge, swb.adEdge, SIZEOF(_adEdge));
  2549.             _rcFloat = swb.rcFloat;
  2550.             _NotifyModeChange(0);
  2551.             // child (e.g. bandsite)
  2552.             ASSERT(_pDBC != NULL);
  2553.             if (_pDBC != NULL) {
  2554.                 // BUGBUG require IPersistStreamInit?
  2555.                 IPersistStream *ppstm;
  2556.                 hres = _pDBC->QueryInterface(IID_IPersistStream, (LPVOID*)&ppstm);
  2557.                 if (SUCCEEDED(hres)) {
  2558.                     // set the child size first because initialization layout might depend on it
  2559.                     SetWindowPos(_hwndChild, 0,
  2560.                                  swb.rcChild.left, swb.rcChild.top, RECTWIDTH(swb.rcChild), RECTHEIGHT(swb.rcChild),
  2561.                                  SWP_NOACTIVATE|SWP_NOZORDER);
  2562.                     
  2563.                     ppstm->Load(pstm);
  2564.                     ppstm->Release();
  2565.                 }
  2566.             }
  2567.             _eInitLoaded = IPS_LOAD;    // BUGBUG what if OLFS of bands fails?
  2568.             TraceMsg(DM_PERSIST, "CDockingBar::Load succeeded");
  2569.         } else {
  2570.             TraceMsg(DM_ERROR, "CWB::Load failed swb.cbSize==SIZEOF(SWebBar) && swb.cbVersion==SWB_VERSION");
  2571.             hres = E_FAIL;
  2572.         }
  2573.     } else {
  2574.         TraceMsg(DM_ERROR, "CWB::Load failed (hres==S_OK && cbRead==SIZEOF(_adEdge)");
  2575.         hres = E_FAIL;
  2576.     }
  2577.     TraceMsg(DM_PERSIST, "cwb.l leave tell()=%x", DbStreamTell(pstm));
  2578.     return hres;
  2579. }
  2580. HRESULT CDockingBar::Save(IStream *pstm, BOOL fClearDirty)
  2581. {
  2582.     HRESULT hres;
  2583.     SWebBar swb = {0};
  2584.     RECT rcMonitor;
  2585.     swb.cbSize = SIZEOF(SWebBar);
  2586.     swb.cbVersion = SWB_VERSION;
  2587.     swb.uSide = _uSide;
  2588.     swb.eMode = _eMode;
  2589.     swb.fWantHide = _fWantHide;
  2590.     memcpy(swb.adEdge, _adEdge, SIZEOF(_adEdge));
  2591.     swb.rcFloat = _rcFloat;
  2592.     GetWindowRect(_hwndChild, &swb.rcChild);
  2593.     MapWindowRect(HWND_DESKTOP, _hwnd, &swb.rcChild);
  2594.     ASSERT(_hMon);
  2595.     GetMonitorRect(_hMon, &rcMonitor);
  2596.     swb.ptSiteCenter.x = (rcMonitor.left + rcMonitor.right) / 2;
  2597.     swb.ptSiteCenter.y = (rcMonitor.top + rcMonitor.bottom) / 2;
  2598.     
  2599.     hres = pstm->Write(&swb, SIZEOF(swb), NULL);
  2600.     if (SUCCEEDED(hres))
  2601.     {
  2602.         IPersistStream* ppstm;
  2603.         hres = _pDBC->QueryInterface(IID_IPersistStream, (LPVOID*)&ppstm);
  2604.         if (SUCCEEDED(hres))
  2605.         {
  2606.             hres = ppstm->Save(pstm, TRUE);
  2607.             ppstm->Release();
  2608.         }
  2609.     }
  2610.     
  2611.     return hres;
  2612. }
  2613. HRESULT CDockingBar::GetSizeMax(ULARGE_INTEGER *pcbSize)
  2614. {
  2615.     ULARGE_INTEGER cbMax = { SIZEOF(SWebBar), 0 };
  2616.     *pcbSize = cbMax;
  2617.     return S_OK;
  2618. }
  2619. HRESULT CDockingBar::InitNew(void)
  2620. {
  2621.     ASSERT(!_eInitLoaded);
  2622.     _eInitLoaded = IPS_INITNEW;
  2623.     TraceMsg(DM_PERSIST, "CDockingBar::InitNew called");
  2624.     // can't call _InitPos4 until set site in SetSite
  2625.     // don't call _SetModeSide, _MoveSizeHelper, etc. until *after* _Initialize
  2626.     // derived class (e.g. CBrowserBarApp) does the _Populate...
  2627.     // on first creation, before bands are added, but the bandsite IS created, we need to notify the bandsite of the new position
  2628.     _NotifyModeChange(0);
  2629.     return S_OK;
  2630. }
  2631. HRESULT CDockingBar::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog)
  2632. {
  2633.     ASSERT(!_eInitLoaded);
  2634.     _eInitLoaded = IPS_LOADBAG;
  2635.     // TODO: We'll read following properties.
  2636.     //
  2637.     //  URL = "..."
  2638.     //  Mode = 0 - TopMost, 1 - Bottom, 2 - Undocked
  2639.     //  Side = 0 - Right, 1 - Top, 2 - Left, 3 - Bottom
  2640.     //  Left/Right/Top/Bottom = Initial docked size
  2641.     //
  2642.     UINT uSide;
  2643.     UINT eMode = _eMode;
  2644.     if (WBM_NIL == eMode)
  2645.         eMode = WBM_BOTTOMMOST;
  2646.     
  2647.     eMode = PropBag_ReadInt4(pPropBag, L"Mode", eMode);
  2648.     uSide = PropBag_ReadInt4(pPropBag, L"Side", _uSide);
  2649.     _adEdge[ABE_LEFT] = PropBag_ReadInt4(pPropBag, L"Left", _adEdge[ABE_LEFT]);
  2650.     _adEdge[ABE_RIGHT] = PropBag_ReadInt4(pPropBag, L"Right", _adEdge[ABE_RIGHT]);
  2651.     _adEdge[ABE_TOP] = PropBag_ReadInt4(pPropBag, L"Top", _adEdge[ABE_TOP]);
  2652.     _adEdge[ABE_BOTTOM] = PropBag_ReadInt4(pPropBag, L"Bottom", _adEdge[ABE_BOTTOM]);
  2653.     int x = PropBag_ReadInt4(pPropBag, L"X", _rcFloat.left);
  2654.     int y = PropBag_ReadInt4(pPropBag, L"Y", _rcFloat.top);
  2655.     OffsetRect(&_rcFloat, x - _rcFloat.left, y - _rcFloat.top);
  2656.     int cx = PropBag_ReadInt4(pPropBag, L"CX", RECTWIDTH(_rcFloat));
  2657.     int cy = PropBag_ReadInt4(pPropBag, L"CY", RECTHEIGHT(_rcFloat));
  2658.     _rcFloat.right = _rcFloat.left + cx;
  2659.     _rcFloat.bottom = _rcFloat.top + cy;
  2660.     // set up vars for eventual CDockingBar::_Initialize call
  2661.     ASSERT(!CDB_INITED());
  2662.     _eMode = eMode;
  2663.     _uSide = uSide;
  2664.     
  2665.     POINT pt = {x, y};
  2666.     // (dli) compute the new hMonitor 
  2667.     _hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  2668.     // don't call _SetModeSide, _MoveSizeHelper, etc. until *after* _Initialize
  2669.     // derived class (e.g. CBrowserBarApp) does the _Populate...
  2670.     // on first creation, before bands are added, but the bandsite IS created, we need to notify the bandsite of the new position
  2671.     _NotifyModeChange(0);
  2672.     return S_OK;
  2673. }
  2674. HRESULT CDockingBar::Save(IPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
  2675. {
  2676.     // We don't need to support this for now.
  2677.     return E_NOTIMPL;
  2678. }
  2679. // }
  2680. //*** CDockingBar::IDocHostUIHandler::* {
  2681. //
  2682. HRESULT CDockingBar::ShowContextMenu(DWORD dwID,
  2683.     POINT* ppt,
  2684.     IUnknown* pcmdtReserved,
  2685.     IDispatch* pdispReserved)
  2686. {
  2687.     if (dwID==0) {
  2688.         TraceMsg(DM_MENU, "cdb.scm: intercept");
  2689.         return _TrackPopupMenu(ppt);
  2690.     }
  2691.     return S_FALSE;
  2692. }
  2693. // }
  2694. void CDockingBar::_SetModeSide(UINT eMode, UINT uSide, HMONITOR hMonNew, BOOL fNoMerge) 
  2695. {
  2696.     _ChangeTopMost(eMode);
  2697.     _uSide = uSide;
  2698.     _hMon = hMonNew;
  2699.     _SetNewMonitor(hMonNew);
  2700. }
  2701. //***   CDockingBar::IInputObjectSite::* {
  2702. HRESULT CDockingBar::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
  2703. {
  2704.     return UnkOnFocusChangeIS(_ptbSite, SAFECAST(this, IInputObject*), fSetFocus);
  2705. }
  2706. // }
  2707. ////////////////////////////////////////////////////////////////
  2708. //
  2709. //  A deskbar property bag
  2710. //////
  2711. HRESULT CDockingBarPropertyBag_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  2712. {
  2713.     CDockingBarPropertyBag* p = new CDockingBarPropertyBag();
  2714.     if (p != NULL)
  2715.     {
  2716.         *ppunk = SAFECAST(p, IPropertyBag*);
  2717.         return S_OK;
  2718.     }
  2719.     *ppunk = NULL;
  2720.     return E_OUTOFMEMORY;
  2721. }
  2722. ULONG CDockingBarPropertyBag::AddRef()
  2723. {
  2724.     _cRef++;
  2725.     return _cRef;
  2726. }
  2727. ULONG CDockingBarPropertyBag::Release()
  2728. {
  2729.     ASSERT(_cRef > 0);
  2730.     _cRef--;
  2731.     if (_cRef > 0)
  2732.         return _cRef;
  2733.     delete this;
  2734.     return 0;
  2735. }
  2736. HRESULT CDockingBarPropertyBag::QueryInterface(REFIID riid, LPVOID * ppvObj)
  2737. {
  2738.     static const QITAB qit[] = {
  2739.         QITABENT(CDockingBarPropertyBag, IPropertyBag),     // IID_IPropertyBag
  2740.         QITABENT(CDockingBarPropertyBag, IDockingBarPropertyBagInit),     // IID_IDockingBarPropertyBagInit
  2741.         { 0 },
  2742.     };
  2743.     return QISearch(this, qit, riid, ppvObj);
  2744. }
  2745. const WCHAR * const c_szPropNames[] = {
  2746.     L"Side",
  2747.     L"Mode",
  2748.     L"Left",
  2749.     L"Top",
  2750.     L"Right",
  2751.     L"Bottom",
  2752.     L"Deleteable",
  2753.     L"X",
  2754.     L"Y",
  2755.     L"CX",
  2756.     L"CY"
  2757. };
  2758. HRESULT CDockingBarPropertyBag::Read( 
  2759.                     /* [in] */ LPCOLESTR pszPropName,
  2760.                     /* [out][in] */ VARIANT *pVar,
  2761.                     /* [in] */ IErrorLog *pErrorLog)
  2762. {
  2763.     int epropdata;
  2764.     for (epropdata = 0; epropdata < (int)PROPDATA_COUNT; epropdata++) {
  2765.         if (!StrCmpW(pszPropName, c_szPropNames[epropdata])) {
  2766.             break;
  2767.         }
  2768.     }
  2769.     if (epropdata < PROPDATA_COUNT && 
  2770.         _props[epropdata]._fSet) {
  2771.         pVar->lVal = _props[epropdata]._dwData;
  2772.         pVar->vt = VT_I4;
  2773.         return S_OK;
  2774.     }
  2775.     
  2776.     return E_FAIL;
  2777. }
  2778. #ifdef DEBUG
  2779. //***   DbCheckWindow --
  2780. // NOTES
  2781. //  BUGBUG nuke the 'hwndClient' param and just use GetParent (but what
  2782. //  about 'owned' windows, does GetParent give the correct answer?)
  2783. BOOL DbCheckWindow(HWND hwnd, RECT *prcExp, HWND hwndClient)
  2784. {
  2785.     RECT rcAct;
  2786.     GetWindowRect(hwnd, &rcAct);
  2787.     hwndClient = GetParent(hwnd);   // BUGBUG nuke this param
  2788.     if (hwndClient != NULL) {
  2789.         // aka ClientToScreen
  2790.         MapWindowPoints(HWND_DESKTOP, hwndClient, (POINT*) &rcAct, 2);
  2791.     }
  2792.     if (!EqualRect(&rcAct, prcExp)) {
  2793.         TraceMsg(DM_TRACE,
  2794.             "cwb.dbcw: !EqualRect rcAct=(%d,%d,%d,%d) (%dx%d) rcExp=(%d,%d,%d,%d) (%dx%d) hwndClient=0x%x",
  2795.             rcAct.left, rcAct.top, rcAct.right, rcAct.bottom,
  2796.             RECTWIDTH(rcAct), RECTHEIGHT(rcAct),
  2797.             prcExp->left, prcExp->top, prcExp->right, prcExp->bottom,
  2798.             RECTWIDTH(*prcExp), RECTHEIGHT(*prcExp),
  2799.             hwndClient);
  2800.         return FALSE;
  2801.     }
  2802.     return TRUE;
  2803. }
  2804. //***   DbStreamTell -- get position in stream (low part only)
  2805. //
  2806. unsigned long DbStreamTell(IStream *pstm)
  2807. {
  2808.     if (pstm == 0)
  2809.         return (unsigned long) -1;
  2810.     ULARGE_INTEGER liEnd;
  2811.     pstm->Seek(c_li0, STREAM_SEEK_CUR, &liEnd);
  2812.     if (liEnd.HighPart != 0)
  2813.         TraceMsg(DM_TRACE, "DbStreamTell: hi!=0");
  2814.     return liEnd.LowPart;
  2815. }
  2816. //***   DbMaskToMneStr -- pretty-print a bit mask in mnemonic form
  2817. // ENTRY/EXIT
  2818. //  uMask       bit mask
  2819. //  szMne       mnemonics, sz[0] for bit 0 .. sz[N] for highest bit
  2820. //  return      ptr to *static* buffer
  2821. // NOTES
  2822. //  n.b.: non-reentrant!!!
  2823. TCHAR *DbMaskToMneStr(UINT uMask, TCHAR *szMnemonics)
  2824. {
  2825.     static TCHAR buf[33];       // BUGBUG non-reentrant!!!
  2826.     TCHAR *p;
  2827.     p = &buf[ARRAYSIZE(buf) - 1];       // point at EOS
  2828.     ASSERT(*p == '');
  2829.     for (;;) {
  2830.         if (*szMnemonics == 0) {
  2831.             ASSERT(uMask == 0);
  2832.             break;
  2833.         }
  2834.         --p;
  2835.         *p = (uMask & 1) ? *szMnemonics : TEXT('-');
  2836.         ++szMnemonics;
  2837.         uMask >>= 1;
  2838.     }
  2839.     return p;
  2840. }
  2841. #endif