Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3371 lines
104 KiB

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