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.

2730 lines
84 KiB

  1. #include "priv.h"
  2. #include "sccls.h"
  3. #include "menuband.h"
  4. #include "itbar.h"
  5. #include "dpastuff.h" // COrderList_*
  6. #include "resource.h"
  7. #include "mnbase.h"
  8. #include "oleacc.h"
  9. #include "apithk.h"
  10. #include "menuisf.h"
  11. #include "iaccess.h"
  12. #include "uemapp.h"
  13. #ifdef UNIX
  14. #include "unixstuff.h"
  15. #endif
  16. // Conflicts with one defined in winuserp.h
  17. #undef WINEVENT_VALID //It's tripping on this...
  18. #include "winable.h"
  19. #define DM_MISC 0 // miscellany
  20. #define MAXUEMTIMEOUT 2000
  21. /*----------------------------------------------------------
  22. Purpose: Return the button command given the position.
  23. */
  24. int GetButtonCmd(HWND hwnd, int iPos)
  25. {
  26. ASSERT(IsWindow(hwnd));
  27. int nRet = -1; // Punt on failure
  28. TBBUTTON tbb;
  29. if (ToolBar_GetButton(hwnd, iPos, &tbb))
  30. {
  31. nRet = tbb.idCommand;
  32. }
  33. return nRet;
  34. }
  35. void* ItemDataFromPos(HWND hwndTB, int iPos)
  36. {
  37. TBBUTTONINFO tbbi;
  38. tbbi.cbSize = SIZEOF(tbbi);
  39. tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
  40. if (ToolBar_GetButtonInfo(hwndTB, iPos, &tbbi) >= 0)
  41. {
  42. return (void*)tbbi.lParam;
  43. }
  44. return NULL;
  45. }
  46. long GetIndexFromChild(BOOL fTop, int iIndex)
  47. {
  48. return (fTop? TOOLBAR_MASK: 0) | iIndex + 1;
  49. }
  50. //--------------------------------------------------------------------------------
  51. //
  52. // CMenuToolbarBase
  53. //
  54. //--------------------------------------------------------------------------------
  55. CMenuToolbarBase::CMenuToolbarBase(CMenuBand* pmb, DWORD dwFlags) : _pcmb(pmb)
  56. {
  57. #ifdef DEBUG
  58. _cRef = 1;
  59. #endif
  60. _dwFlags = dwFlags;
  61. _nItemTimer = -1;
  62. _idCmdChevron = -1;
  63. _fFirstTime = TRUE;
  64. }
  65. // *** IObjectWithSite methods ***
  66. HRESULT CMenuToolbarBase::SetSite(IUnknown *punkSite)
  67. {
  68. ASSERT(punkSite && IS_VALID_READ_PTR(punkSite, CMenuBand*));
  69. // We are guaranteed the lifetime of this object is contained within
  70. // the menuband, so we don't addref pcmb.
  71. if (SUCCEEDED(punkSite->QueryInterface(CLSID_MenuBand, (LPVOID*)&_pcmb))) {
  72. punkSite->Release();
  73. } else {
  74. ASSERT(0);
  75. }
  76. _fVerticalMB = !BOOLIFY(_pcmb->_dwFlags & SMINIT_HORIZONTAL);
  77. _fTopLevel = BOOLIFY(_pcmb->_dwFlags & SMINIT_TOPLEVEL);
  78. return S_OK;
  79. }
  80. HRESULT CMenuToolbarBase::GetSite(REFIID riid, void ** ppvSite)
  81. {
  82. if (!_pcmb)
  83. return E_FAIL;
  84. return _pcmb->QueryInterface(riid, ppvSite);
  85. }
  86. // *** IUnknown methods ***
  87. STDMETHODIMP_(ULONG) CMenuToolbarBase::AddRef()
  88. {
  89. DEBUG_CODE(_cRef++);
  90. if (_pcmb)
  91. {
  92. return _pcmb->AddRef();
  93. }
  94. return 0;
  95. }
  96. STDMETHODIMP_(ULONG) CMenuToolbarBase::Release()
  97. {
  98. ASSERT(_cRef > 0);
  99. DEBUG_CODE(_cRef--);
  100. if (_pcmb)
  101. {
  102. return _pcmb->Release();
  103. }
  104. return 0;
  105. }
  106. HRESULT CMenuToolbarBase::QueryInterface(REFIID riid, void** ppvObj)
  107. {
  108. HRESULT hres;
  109. if (IsEqualGUID(riid, CLSID_MenuToolbarBase) && ppvObj)
  110. {
  111. AddRef();
  112. *ppvObj = (LPVOID)this;
  113. hres = S_OK;
  114. }
  115. else
  116. hres = _pcmb->QueryInterface(riid, ppvObj);
  117. return hres;
  118. }
  119. void CMenuToolbarBase::SetToTop(BOOL bToTop)
  120. {
  121. // A menu toolbar can be at the top or the bottom of the menu.
  122. // This is an exclusive attribute.
  123. if (bToTop)
  124. {
  125. _dwFlags |= SMSET_TOP;
  126. _dwFlags &= ~SMSET_BOTTOM;
  127. }
  128. else
  129. {
  130. _dwFlags |= SMSET_BOTTOM;
  131. _dwFlags &= ~SMSET_TOP;
  132. }
  133. }
  134. void CMenuToolbarBase::KillPopupTimer()
  135. {
  136. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  137. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): Killing Popout Timer...", this);
  138. KillTimer(_hwndMB, MBTIMER_POPOUT);
  139. _nItemTimer = -1;
  140. }
  141. void CMenuToolbarBase::SetWindowPos(LPSIZE psize, LPRECT prc, DWORD dwFlags)
  142. {
  143. if (_hwndMB)
  144. {
  145. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  146. DWORD rectWidth = RECTWIDTH(*prc);
  147. TraceMsg(TF_MENUBAND, "CMTB::SetWindowPos %d - (%d,%d,%d,%d)", psize?psize->cx:0,
  148. prc->left, prc->top, prc->right, prc->bottom);
  149. ::SetWindowPos(_hwndMB, NULL, prc->left, prc->top,
  150. rectWidth, RECTHEIGHT(*prc), SWP_NOZORDER | SWP_NOACTIVATE | dwFlags);
  151. // hackhack: we only do this when multicolumn. this call is to facilitate the size negotiation between
  152. // static menu and folder menu. Set the width of the toolbar to the width of the button in case
  153. // of non-multicolumn.
  154. if (!(_fMulticolumnMB) && psize)
  155. ToolBar_SetButtonWidth(_hwndMB, psize->cx, psize->cx);
  156. // Force this to redraw. I put this here because the HMenu portion was painting after the shell
  157. // folder portion was done enumerating the folder, which is pretty slow. I wanted the HMENU portion
  158. // to paint right away...
  159. RedrawWindow(_hwndMB, NULL, NULL, RDW_UPDATENOW);
  160. }
  161. }
  162. // NOTE: if psize is (0,0) we use tb button size as param in figuring out ideal tb size
  163. // else we use max of psize length and tb button length as our metric
  164. void CMenuToolbarBase::GetSize(SIZE* psize)
  165. {
  166. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  167. if (EVAL(_hwndMB))
  168. {
  169. LRESULT lButtonSize;
  170. lButtonSize = SendMessage(_hwndMB, TB_GETBUTTONSIZE, 0, 0);
  171. if (psize->cx || psize->cy)
  172. {
  173. int cx = max(psize->cx, LOWORD(lButtonSize));
  174. int cy = max(psize->cy, HIWORD(lButtonSize));
  175. lButtonSize = MAKELONG(cx, cy);
  176. }
  177. if (_fVerticalMB)
  178. {
  179. psize->cx = LOWORD(lButtonSize);
  180. SendMessage(_hwndMB, TB_GETIDEALSIZE, TRUE, (LPARAM)psize);
  181. }
  182. else
  183. {
  184. psize->cy = HIWORD(lButtonSize);
  185. SendMessage(_hwndMB, TB_GETIDEALSIZE, FALSE, (LPARAM)psize);
  186. }
  187. TraceMsg(TF_MENUBAND, "CMTB::GetSize (%d, %d)", psize->cx, psize->cy);
  188. }
  189. }
  190. /*----------------------------------------------------------
  191. Purpose: Timer handler. Used to pop open/close cascaded submenus.
  192. */
  193. LRESULT CMenuToolbarBase::_OnTimer(WPARAM wParam)
  194. {
  195. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  196. switch (wParam)
  197. {
  198. case MBTIMER_INFOTIP:
  199. {
  200. // Do we have a hot item to display the tooltip for?
  201. int iHotItem = ToolBar_GetHotItem(_hwndMB);
  202. KillTimer(_hwndMB, wParam);
  203. if (iHotItem >= 0)
  204. {
  205. // Yep.
  206. TCHAR szTip[MAX_PATH];
  207. int idCmd = GetButtonCmd(_hwndMB, iHotItem);
  208. // Ask the superclass for the tip
  209. if (S_OK == v_GetInfoTip(idCmd, szTip, ARRAYSIZE(szTip)))
  210. {
  211. // Now display it. Yawn.
  212. _pcmb->_pmbState->CenterOnButton(_hwndMB, FALSE, idCmd, NULL, szTip);
  213. }
  214. }
  215. }
  216. break;
  217. case MBTIMER_CHEVRONTIP:
  218. KillTimer(_hwndMB, wParam);
  219. _pcmb->_pmbState->HideTooltip(TRUE);
  220. break;
  221. case MBTIMER_FLASH:
  222. {
  223. _cFlashCount++;
  224. if (_cFlashCount == COUNT_ENDFLASH)
  225. {
  226. _cFlashCount = 0;
  227. KillTimer(_hwndMB, wParam);
  228. ToolBar_MarkButton(_hwndMB, _idCmdChevron, FALSE);
  229. _SetTimer(MBTIMER_UEMTIMEOUT);
  230. // Now that we've flashed, let's show the Chevron tip.
  231. // This is for a confused user: If they've hovered over an item for too long,
  232. // or this is the first time they've seen intellimenus, then we flash and display
  233. // the tooltip. We only want to display this if we are shown: We would end up with
  234. // and dangling tooltip if you happen to move to another menu while it was flashing.
  235. // Ummm, is the Chevron still visible?
  236. if (_fShowMB && _idCmdChevron != -1)
  237. {
  238. TCHAR szTip[MAX_PATH];
  239. TCHAR szTitle[MAX_PATH];
  240. if (S_OK == v_CallCBItem(_idCmdChevron, SMC_CHEVRONGETTIP, (WPARAM)szTitle, (LPARAM)szTip))
  241. {
  242. _pcmb->_pmbState->CenterOnButton(_hwndMB, TRUE, _idCmdChevron, szTitle, szTip);
  243. _SetTimer(MBTIMER_CHEVRONTIP);
  244. }
  245. }
  246. }
  247. else
  248. ToolBar_MarkButton(_hwndMB, _idCmdChevron, (_cFlashCount % 2) == 0);
  249. }
  250. break;
  251. case MBTIMER_UEMTIMEOUT:
  252. {
  253. POINT pt;
  254. RECT rect;
  255. // Don't fire timeouts when we're in edit mode.
  256. if (_fEditMode)
  257. {
  258. KillTimer(_hwndMB, wParam);
  259. break;
  260. }
  261. GetWindowRect(_hwndMB, &rect);
  262. GetCursorPos(&pt);
  263. if (PtInRect(&rect, pt))
  264. {
  265. TraceMsg(TF_MENUBAND, "*** UEM TimeOut. At Tick Count (%d) ***", GetTickCount());
  266. _FireEvent(UEM_TIMEOUT);
  267. }
  268. else
  269. {
  270. TraceMsg(TF_MENUBAND, " *** UEM TimeOut. At Tick Count (%d)."
  271. " Mouse outside menu. Killing *** ", GetTickCount());
  272. KillTimer(_hwndMB, wParam);
  273. }
  274. }
  275. break;
  276. case MBTIMER_EXPAND:
  277. KillTimer(_hwndMB, wParam);
  278. if (_fShowMB)
  279. {
  280. v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
  281. Expand(TRUE);
  282. _fClickHandled = TRUE;
  283. _SetTimer(MBTIMER_CLICKUNHANDLE);
  284. }
  285. break;
  286. case MBTIMER_DRAGPOPDOWN:
  287. // There has not been a drag enter in this band for a while,
  288. // so we'll try to cancel the menus.
  289. KillTimer(_hwndMB, wParam);
  290. PostMessage(_pcmb->_pmbState->GetSubclassedHWND(), g_nMBDragCancel, 0, 0);
  291. break;
  292. case MBTIMER_DRAGOVER:
  293. {
  294. TraceMsg(TF_MENUBAND, "CMenuToolbarBase::OnTimer(DRAG)");
  295. KillTimer(_hwndMB, wParam);
  296. DAD_ShowDragImage(FALSE);
  297. // Does this item cascade?
  298. int idBtn = GetButtonCmd(_hwndMB, v_GetDragOverButton());
  299. if (v_GetFlags(idBtn) & SMIF_SUBMENU)
  300. {
  301. TraceMsg(TF_MENUBAND, "CMenuToolbarBase::OnTimer(DRAG): Is a submenu");
  302. // Yes; pop it open
  303. if (!_fVerticalMB)
  304. _pcmb->_fInvokedByDrag = TRUE;
  305. _DoPopup(idBtn, FALSE);
  306. }
  307. else if (idBtn == _idCmdChevron)
  308. {
  309. Expand(TRUE);
  310. }
  311. else
  312. {
  313. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  314. }
  315. }
  316. break;
  317. case MBTIMER_POPOUT:
  318. {
  319. int nItemTimer = _nItemTimer;
  320. KillPopupTimer();
  321. // Popup a new submenu?
  322. if (-1 != nItemTimer)
  323. {
  324. if (nItemTimer != _pcmb->_nItemCur)
  325. {
  326. // Yes; post message since the currently expanded submenu
  327. // may be a CTrackPopup object, which posts its cancel mode.
  328. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): Timer went off. Expanding...", this);
  329. PostPopup(nItemTimer, FALSE, FALSE);
  330. }
  331. }
  332. else
  333. {
  334. // No; just collapse the currently open submenu
  335. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _OnTimer sending MPOS_CANCELLEVEL to submenu popup", this);
  336. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  337. }
  338. break;
  339. }
  340. case MBTIMER_CLOSE:
  341. KillTimer(_hwndMB, wParam);
  342. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _OnTimer sending MPOS_FULLCANCEL", this);
  343. if (_fVerticalMB)
  344. _pcmb->_SiteOnSelect(MPOS_FULLCANCEL);
  345. else
  346. {
  347. _pcmb->_SubMenuOnSelect(MPOS_FULLCANCEL);
  348. }
  349. break;
  350. }
  351. return 1;
  352. }
  353. void CMenuToolbarBase::_DrawMenuArrowGlyph( HDC hdc, RECT * prc, COLORREF rgbText )
  354. {
  355. SIZE size = {_pcmb->_pmbm->_cxArrow, _pcmb->_pmbm->_cyArrow};
  356. //
  357. // If the DC is mirrred, then the Arrow should be mirrored
  358. // since it is done thru TextOut, NOT the 2D graphics APIs [samera]
  359. //
  360. _DrawMenuGlyph(hdc,
  361. _pcmb->_pmbm->_hFontArrow,
  362. prc,
  363. (IS_DC_RTL_MIRRORED(hdc)) ? CH_MENUARROWRTLA :
  364. CH_MENUARROWA,
  365. rgbText,
  366. &size);
  367. }
  368. void CMenuToolbarBase::_DrawMenuGlyph( HDC hdc, HFONT hFont, RECT * prc,
  369. CHAR ch, COLORREF rgbText,
  370. LPSIZE psize)
  371. {
  372. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  373. if (_pcmb->_pmbm->_hFontArrow)
  374. {
  375. SIZE size;
  376. int cx, cy, y, x;
  377. HFONT hFontOld;
  378. int iOldBk = SetBkMode(hdc, TRANSPARENT);
  379. hFontOld = (HFONT)SelectObject(hdc, hFont);
  380. if (psize == NULL)
  381. {
  382. GetTextExtentPoint32A( hdc, &ch, 1, &size);
  383. psize = &size;
  384. }
  385. cy = prc->bottom - prc->top;
  386. y = prc->top + ((cy - psize->cy) / 2);
  387. cx = prc->right - prc->left;
  388. x = prc->left + ((cx - psize->cx) /2);
  389. COLORREF rgbOld = SetTextColor(hdc, rgbText);
  390. #ifndef UNIX
  391. TextOutA(hdc, x, y, &ch, 1);
  392. #else
  393. // Paint motif look arrow.
  394. PaintUnixMenuArrow( hdc, prc, (DWORD)rgbText );
  395. #endif
  396. SetTextColor(hdc, rgbOld);
  397. SetBkMode(hdc, iOldBk);
  398. SelectObject(hdc, hFontOld);
  399. }
  400. }
  401. void CMenuToolbarBase::SetMenuBandMetrics(CMenuBandMetrics* pmbm)
  402. {
  403. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  404. // This can be called before the toolbar is created.
  405. // So we'll check this condition. When the toolbar is created, then
  406. // the toolbar will get the metrics at that point.
  407. if (!_hwndMB)
  408. return;
  409. //Loop through toolbar.
  410. for (int iButton = ToolBar_ButtonCount(_hwndMB)-1; iButton >= 0; iButton--)
  411. {
  412. IOleCommandTarget* poct;
  413. int idCmd = GetButtonCmd(_hwndMB, iButton);
  414. // If it's not a seperator, see if there is a sub menu.
  415. if (idCmd != -1 &&
  416. SUCCEEDED(v_GetSubMenu(idCmd, NULL, IID_IOleCommandTarget, (void**)&poct)))
  417. {
  418. VARIANT Var;
  419. Var.vt = VT_UNKNOWN;
  420. Var.punkVal = SAFECAST(pmbm, IUnknown*);
  421. // Exec to set new Metrics.
  422. poct->Exec(&CGID_MenuBand, MBANDCID_SETFONTS, 0, &Var, NULL);
  423. poct->Release();
  424. }
  425. }
  426. _SetFontMetrics();
  427. // return
  428. }
  429. void CMenuToolbarBase::_SetFontMetrics()
  430. {
  431. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  432. if (_hwndMB && _pcmb->_pmbm)
  433. {
  434. SendMessage(_hwndMB, WM_SETFONT, (WPARAM)_pcmb->_pmbm->_hFontMenu, FALSE);
  435. }
  436. }
  437. void CMenuToolbarBase::CreateToolbar(HWND hwndParent)
  438. {
  439. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  440. ASSERT( _hwndMB != NULL );
  441. DWORD dwToolBarStyle = TBSTYLE_TRANSPARENT;
  442. // if we're set up as a popup, don't do any transparent stuff
  443. if (_fVerticalMB)
  444. {
  445. dwToolBarStyle = TBSTYLE_CUSTOMERASE; // Vertical Toolbars don't get Transparent
  446. DWORD dwExtendedStyle = 0;
  447. // This is for TBMenu which actually has a Horizontal menubar within the
  448. // Vertical menuband.
  449. if (!_fHorizInVerticalMB)
  450. dwExtendedStyle |= TBSTYLE_EX_VERTICAL;
  451. if (_fMulticolumnMB)
  452. dwExtendedStyle |= TBSTYLE_EX_MULTICOLUMN;
  453. ToolBar_SetExtendedStyle(_hwndMB,
  454. dwExtendedStyle, TBSTYLE_EX_VERTICAL | TBSTYLE_EX_MULTICOLUMN);
  455. ToolBar_SetListGap(_hwndMB, LIST_GAP);
  456. }
  457. SHSetWindowBits(_hwndMB, GWL_STYLE,
  458. TBSTYLE_TRANSPARENT | TBSTYLE_CUSTOMERASE, dwToolBarStyle );
  459. ToolBar_SetInsertMarkColor(_hwndMB, GetSysColor(COLOR_MENUTEXT));
  460. v_UpdateIconSize(_pcmb->_uIconSize, FALSE);
  461. _SetFontMetrics();
  462. }
  463. HRESULT CMenuToolbarBase::_SetMenuBand(IShellMenu* psm)
  464. {
  465. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  466. HRESULT hres = E_FAIL;
  467. IBandSite* pmbs = NULL;
  468. if (!_pcmb->_pmpSubMenu)
  469. {
  470. hres = CoCreateInstance(CLSID_MenuDeskBar, NULL, CLSCTX_INPROC_SERVER, IID_IMenuPopup, (void**)&_pcmb->_pmpSubMenu);
  471. if (SUCCEEDED(hres))
  472. {
  473. IUnknown_SetSite(_pcmb->_pmpSubMenu, SAFECAST(_pcmb, IOleCommandTarget*));
  474. hres = CoCreateInstance(CLSID_MenuBandSite, NULL, CLSCTX_INPROC_SERVER, IID_IBandSite, (void**)&pmbs);
  475. if (SUCCEEDED(hres))
  476. {
  477. hres = _pcmb->_pmpSubMenu->SetClient(pmbs);
  478. // Don't release pmbs here. We are using below
  479. }
  480. // Menu band will Release _pmpSubMenu.
  481. }
  482. }
  483. else
  484. {
  485. IUnknown* punk;
  486. _pcmb->_pmpSubMenu->GetClient(&punk);
  487. if (punk)
  488. {
  489. hres = punk->QueryInterface(IID_IBandSite, (void**)&pmbs);
  490. punk->Release();
  491. }
  492. }
  493. if (pmbs)
  494. {
  495. if (SUCCEEDED(hres))
  496. hres = pmbs->AddBand(psm);
  497. pmbs->Release();
  498. }
  499. return hres;
  500. }
  501. HRESULT CMenuToolbarBase::GetSubMenu(int idCmd, GUID* pguidService, REFIID riid, void** ppvObj)
  502. {
  503. // pguidService is for asking a for specifically the Shell Folder portion or the Static portion
  504. HRESULT hres = E_FAIL;
  505. if (v_GetFlags(idCmd) & SMIF_TRACKPOPUP ||
  506. _pcmb->_dwFlags & SMINIT_DEFAULTTOTRACKPOPUP)
  507. {
  508. hres = v_CreateTrackPopup(idCmd, riid, (void**)ppvObj);
  509. if (SUCCEEDED(hres))
  510. {
  511. _pcmb->SetTrackMenuPopup((IUnknown*)*ppvObj);
  512. }
  513. }
  514. else
  515. {
  516. IShellMenu* psm;
  517. hres = v_GetSubMenu(idCmd, pguidService, IID_IShellMenu, (void**)&psm);
  518. if (SUCCEEDED(hres))
  519. {
  520. TraceMsg(TF_MENUBAND, "GetUIObject psm %#lx", psm);
  521. _pcmb->SetTracked(this);
  522. hres = _SetMenuBand(psm);
  523. psm->Release();
  524. // Did we succeed in getting a menupopup?
  525. if (SUCCEEDED(hres))
  526. {
  527. // Yep; Sweet!
  528. _pcmb->_pmpSubMenu->QueryInterface(riid, ppvObj);
  529. HWND hwnd;
  530. IUnknown_GetWindow(_pcmb->_pmpSubMenu, &hwnd);
  531. PostMessage(_pcmb->_pmbState->GetSubclassedHWND(), g_nMBAutomation, (WPARAM)hwnd, (LPARAM)-1);
  532. }
  533. }
  534. }
  535. return hres;
  536. }
  537. HRESULT CMenuToolbarBase::PositionSubmenu(int idCmd)
  538. {
  539. IMenuPopup* pmp = NULL;
  540. HRESULT hres = E_FAIL;
  541. DWORD dwFlags = 0;
  542. if (_pcmb->_fInSubMenu)
  543. {
  544. // Since the selection has probrably changed, we use the cached item id
  545. // to calculate the postion rect
  546. idCmd = _pcmb->_nItemSubMenu;
  547. dwFlags = MPPF_REPOSITION | MPPF_NOANIMATE;
  548. pmp = _pcmb->_pmpSubMenu;
  549. pmp->AddRef();
  550. ASSERT(pmp); // If _fInSubmenu is set, then this must be valid
  551. hres = S_OK;
  552. }
  553. else
  554. {
  555. // Only do these when we're not repositioning.
  556. if (_pcmb->_fInitialSelect)
  557. dwFlags |= MPPF_INITIALSELECT;
  558. if (g_bRunOnNT5 && !_pcmb->_fCascadeAnimate)
  559. dwFlags |= MPPF_NOANIMATE;
  560. _pcmb->_nItemSubMenu = idCmd;
  561. hres = GetSubMenu(idCmd, NULL, IID_IMenuPopup, (void**)&pmp);
  562. }
  563. ASSERT(idCmd != -1); // Make sure at this point we have an item.
  564. if (SUCCEEDED(hres))
  565. {
  566. ASSERT(pmp);
  567. // Make sure the menuitem is pressed
  568. _PressBtn(idCmd, TRUE);
  569. RECT rc;
  570. RECT rcTB;
  571. RECT rcTemp;
  572. POINT pt;
  573. SendMessage(_hwndMB, TB_GETRECT, idCmd, (LPARAM)&rc);
  574. GetClientRect(_hwndMB, &rcTB);
  575. // Is the button rect within the boundries of the
  576. // visible toolbar?
  577. if (!IntersectRect(&rcTemp, &rcTB, &rc))
  578. {
  579. // No; Then we need to bias that rect into
  580. // the visible region of the toolbar.
  581. // We only want to bias one side
  582. if (rc.left > rcTB.right)
  583. {
  584. rc.left = rcTB.right - (rc.right - rc.left);
  585. rc.right = rcTB.right;
  586. }
  587. }
  588. MapWindowPoints(_hwndMB, HWND_DESKTOP, (POINT*)&rc, 2);
  589. if (_fVerticalMB)
  590. {
  591. pt.x = rc.right;
  592. pt.y = rc.top;
  593. }
  594. else
  595. {
  596. //
  597. // If the shell dropdown (toolbar button) menus are mirrored,
  598. // then take the right edge as the anchor point
  599. //
  600. if (IS_WINDOW_RTL_MIRRORED(_hwndMB))
  601. pt.x = rc.right;
  602. else
  603. pt.x = rc.left;
  604. pt.y = rc.bottom;
  605. }
  606. // Since toolbar buttons expand almost to the end of the basebar,
  607. // shrink the exclude rect so if overlaps.
  608. // NOTE: the items are GetSystemMetrics(SM_CXEDGE) larger than before. So adjust to that.
  609. if (_pcmb->_fExpanded)
  610. InflateRect(&rc, -GetSystemMetrics(SM_CXEDGE), 0);
  611. // We want to stop showing the chevron tip when we cascade into another menu
  612. _pcmb->_pmbState->HideTooltip(TRUE);
  613. // Only animate the first show at this level.
  614. _pcmb->_fCascadeAnimate = FALSE;
  615. hres = pmp->Popup((POINTL*)&pt, (RECTL*)&rc, dwFlags);
  616. pmp->Release();
  617. }
  618. return hres;
  619. }
  620. /*----------------------------------------------------------
  621. Purpose: Cascade to the _nItemCur item's menu popup.
  622. If the popup call was modal, S_FALSE is returned; otherwise
  623. it is S_OK, or error.
  624. */
  625. HRESULT CMenuToolbarBase::PopupOpen(int idBtn)
  626. {
  627. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  628. HRESULT hres = E_FAIL;
  629. // Tell the current submenu popup to cancel. This must be done
  630. // before the PostMessage b/c CTrackPopupBar itself posts a message
  631. // which it must receive before we receive our post.
  632. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): PostPopup sending MPOS_CANCELLEVEL to submenu popup", this);
  633. if (_pcmb->_fInSubMenu)
  634. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  635. hres = PositionSubmenu(idBtn);
  636. // Modal?
  637. if (S_FALSE == hres)
  638. {
  639. // Yes; take the capture back
  640. GetMessageFilter()->RetakeCapture();
  641. // return S_OK so we stay in the menu mode
  642. hres = S_OK;
  643. }
  644. else if (FAILED(hres))
  645. _PressBtn(idBtn, FALSE);
  646. // Since CTrackPopupBar is modal, it should be a useless blob
  647. // of bits in memory by now...
  648. _pcmb->SetTrackMenuPopup(NULL);
  649. return hres;
  650. }
  651. /*----------------------------------------------------------
  652. Purpose: Called to hide a modeless menu.
  653. */
  654. void CMenuToolbarBase::PopupClose(void)
  655. {
  656. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  657. if (-1 != _pcmb->_nItemCur)
  658. {
  659. _PressBtn(_pcmb->_nItemCur, FALSE);
  660. NotifyWinEvent(EVENT_OBJECT_FOCUS, _hwndMB, OBJID_CLIENT,
  661. GetIndexFromChild(_dwFlags & SMSET_TOP, ToolBar_CommandToIndex(_hwndMB, _pcmb->_nItemCur)));
  662. _pcmb->_fInSubMenu = FALSE;
  663. _pcmb->_fInvokedByDrag = FALSE;
  664. _pcmb->_nItemCur = -1;
  665. }
  666. }
  667. LRESULT CMenuToolbarBase::_OnWrapHotItem(NMTBWRAPHOTITEM* pnmwh)
  668. {
  669. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  670. if (_fProcessingWrapHotItem ||
  671. (_pcmb->_pmtbTop == _pcmb->_pmtbBottom && !_fHasDemotedItems))
  672. return 0;
  673. _fProcessingWrapHotItem = TRUE;
  674. // If we want ourselves to not be wrapped into (Like for empty items)
  675. // Then forward the wrap message to the other toolbar
  676. if (_pcmb->_pmtbTracked->_dwFlags & SMSET_TOP && !(_pcmb->_pmtbBottom->_fDontShowEmpty))
  677. {
  678. _pcmb->SetTracked(_pcmb->_pmtbBottom);
  679. }
  680. else if (!(_pcmb->_pmtbTop->_fDontShowEmpty))
  681. {
  682. _pcmb->SetTracked(_pcmb->_pmtbTop);
  683. }
  684. int iIndex;
  685. if (pnmwh->iDir < 0)
  686. {
  687. HWND hwnd = _pcmb->_pmtbTracked->_hwndMB;
  688. iIndex = ToolBar_ButtonCount(hwnd) - 1;
  689. int idCmd = GetButtonCmd(hwnd, iIndex);
  690. // We do not want to wrap onto a chevron.
  691. if (idCmd == _idCmdChevron)
  692. iIndex -= 1;
  693. }
  694. else
  695. {
  696. iIndex = 0;
  697. }
  698. _pcmb->_pmtbTracked->SetHotItem(pnmwh->iDir, iIndex, -1, pnmwh->nReason);
  699. _fProcessingWrapHotItem = FALSE;
  700. return 1;
  701. }
  702. LRESULT CMenuToolbarBase::_OnWrapAccelerator(NMTBWRAPACCELERATOR* pnmwa)
  703. {
  704. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  705. int iHotItem = -1;
  706. int iNumTopAccel = 0;
  707. int iNumBottomAccel = 0;
  708. if (_pcmb->_fProcessingDup)
  709. return 0;
  710. // Check to see if there is only one toolbar.
  711. if (_pcmb->_pmtbTop == _pcmb->_pmtbBottom)
  712. return 0;
  713. ToolBar_HasAccelerator(_pcmb->_pmtbTop->_hwndMB, pnmwa->ch, &iNumTopAccel);
  714. ToolBar_HasAccelerator(_pcmb->_pmtbBottom->_hwndMB, pnmwa->ch, &iNumBottomAccel);
  715. _pcmb->_fProcessingDup = TRUE;
  716. CMenuToolbarBase* pmbtb = NULL;
  717. if (_pcmb->_pmtbTracked->_dwFlags & SMSET_TOP)
  718. {
  719. ToolBar_MapAccelerator(_pcmb->_pmtbBottom->_hwndMB, pnmwa->ch, &iHotItem);
  720. pmbtb = _pcmb->_pmtbBottom;
  721. }
  722. else
  723. {
  724. ToolBar_MapAccelerator(_pcmb->_pmtbTop->_hwndMB, pnmwa->ch, &iHotItem);
  725. pmbtb = _pcmb->_pmtbTop;
  726. }
  727. _pcmb->_fProcessingDup = FALSE;
  728. if (iHotItem != -1)
  729. {
  730. _pcmb->SetTracked(pmbtb);
  731. int idCmd = ToolBar_CommandToIndex(pmbtb->_hwndMB, iHotItem);
  732. DWORD dwFlags = HICF_ACCELERATOR;
  733. // If either (but not both) toolbars have the accelerator, and it is exactly one,
  734. // then cause the drop down.
  735. if ( (iNumTopAccel >= 1) ^ (iNumBottomAccel >= 1) &&
  736. (iNumTopAccel == 1 || iNumBottomAccel == 1) )
  737. dwFlags |= HICF_TOGGLEDROPDOWN;
  738. SendMessage(pmbtb->_hwndMB, TB_SETHOTITEM2, idCmd, dwFlags);
  739. pnmwa->iButton = -1;
  740. return 1;
  741. }
  742. return 0;
  743. }
  744. LRESULT CMenuToolbarBase::_OnDupAccelerator(NMTBDUPACCELERATOR* pnmda)
  745. {
  746. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  747. if (_pcmb->_fProcessingDup || (_pcmb->_pmtbBottom == _pcmb->_pmtbTop))
  748. return 0;
  749. _pcmb->_fProcessingDup = TRUE;
  750. int iNumTopAccel = 0;
  751. int iNumBottomAccel = 0;
  752. if (_pcmb->_pmtbTop)
  753. ToolBar_HasAccelerator(_pcmb->_pmtbTop->_hwndMB, pnmda->ch, &iNumTopAccel);
  754. if (_pcmb->_pmtbBottom)
  755. ToolBar_HasAccelerator(_pcmb->_pmtbBottom->_hwndMB, pnmda->ch, &iNumBottomAccel);
  756. _pcmb->_fProcessingDup = FALSE;
  757. if (0 == iNumTopAccel && 0 == iNumBottomAccel)
  758. {
  759. // We want to return 1 if Both of them have one.
  760. //Otherwise, return 0, and let the toolbar handle it itself.
  761. return 0;
  762. }
  763. pnmda->fDup = TRUE;
  764. return 1;
  765. }
  766. /*----------------------------------------------------------
  767. Purpose: Handle WM_NOTIFY
  768. */
  769. LRESULT CMenuToolbarBase::_OnNotify(LPNMHDR pnm)
  770. {
  771. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  772. LRESULT lres = 0;
  773. CMBMsgFilter* pmf = GetMessageFilter();
  774. // These are notifies we handle even when disengaged from the message hook.
  775. switch (pnm->code)
  776. {
  777. case NM_CUSTOMDRAW:
  778. // We now custom draw even the TopLevelMenuBand (for the correct font)
  779. lres = _OnCustomDraw((NMCUSTOMDRAW*)pnm);
  780. break;
  781. }
  782. // Is the Global Message filter Disengaged? This will happen when the Subclassed window
  783. // looses activation to a dialog box of some kind.
  784. if (lres == 0 && !pmf->IsEngaged())
  785. {
  786. // Yes; We've lost activation so we don't want to track like a normal menu...
  787. // For hot item change, return 1 so that the toolbar does not change the hot item.
  788. if (pnm->code == TBN_HOTITEMCHANGE && _pcmb->_fMenuMode)
  789. return 1;
  790. // For all other items, don't do anything....
  791. return 0;
  792. }
  793. switch (pnm->code)
  794. {
  795. case NM_RELEASEDCAPTURE:
  796. pmf->RetakeCapture();
  797. break;
  798. case NM_KEYDOWN:
  799. BLOCK
  800. {
  801. LPNMKEY pnmk = (LPNMKEY)pnm;
  802. lres = _OnKey(TRUE, pnmk->nVKey, pnmk->uFlags);
  803. }
  804. break;
  805. case NM_CHAR:
  806. {
  807. LPNMCHAR pnmc = (LPNMCHAR)pnm;
  808. if (pnmc->ch == TEXT(' '))
  809. return TRUE;
  810. if (pnmc->dwItemNext == -1 &&
  811. !_pcmb->_fVertical)
  812. {
  813. // If it's horizontal, then it must be top level.
  814. ASSERT(_pcmb->_fTopLevel);
  815. _pcmb->_CancelMode(MPOS_FULLCANCEL);
  816. }
  817. }
  818. break;
  819. case TBN_HOTITEMCHANGE:
  820. lres = _OnHotItemChange((LPNMTBHOTITEM)pnm);
  821. break;
  822. case NM_LDOWN:
  823. // We need to kill the expand timer, because the user might
  824. // move out of the chevron and accidentally select another item.
  825. if ( (int)((LPNMCLICK)pnm)->dwItemSpec == _idCmdChevron && _idCmdChevron != -1)
  826. {
  827. KillTimer(_hwndMB, MBTIMER_EXPAND);
  828. _fIgnoreHotItemChange = TRUE;
  829. }
  830. break;
  831. case NM_CLICK:
  832. {
  833. int idCmd = (int)((LPNMCLICK)pnm)->dwItemSpec;
  834. _fIgnoreHotItemChange = FALSE;
  835. if ( idCmd == -1 )
  836. {
  837. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  838. _pcmb->SetTracked(NULL);
  839. lres = 1;
  840. }
  841. else if ( idCmd == _idCmdChevron )
  842. {
  843. // Retake the capture on the button-up, b/c the toolbar took
  844. // it away for a moment.
  845. pmf->RetakeCapture();
  846. v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
  847. Expand(TRUE);
  848. _fClickHandled = TRUE;
  849. _SetTimer(MBTIMER_CLICKUNHANDLE);
  850. lres = 1;
  851. }
  852. else if (!_fEmpty)
  853. {
  854. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): upclick %d", this, idCmd);
  855. // Retake the capture on the button-up, b/c the toolbar took
  856. // it away for a moment.
  857. pmf->RetakeCapture();
  858. if (v_GetFlags(idCmd) & SMIF_SUBMENU) // Submenus support double click
  859. {
  860. if (_iLastClickedTime == 0) // First time it was clicked
  861. {
  862. _iLastClickedTime = GetTickCount();
  863. _idCmdLastClicked = idCmd;
  864. }
  865. // Did they click on the same item twice?
  866. else if (idCmd != _idCmdLastClicked)
  867. {
  868. _iLastClickedTime = _idCmdLastClicked = 0;
  869. }
  870. else
  871. {
  872. // Was this item double clicked on?
  873. if ((GetTickCount() - _iLastClickedTime) < GetDoubleClickTime())
  874. {
  875. // We need to post this back to ourselves, because
  876. // the Tray will become in active when double clicking
  877. // on something like programs. This happens because the
  878. // Toolbar will set capture back to itself and the tray
  879. // doesn't get any more messages.
  880. PostMessage(_hwndMB, g_nMBExecute, idCmd, 0);
  881. _fClickHandled = TRUE;
  882. }
  883. _iLastClickedTime = _idCmdLastClicked = 0;
  884. }
  885. }
  886. // Sent on the button-up. Handle the same way.
  887. if (!_fClickHandled && -1 != idCmd)
  888. _DropDownOrExec(idCmd, FALSE);
  889. _fClickHandled = FALSE;
  890. lres = 1;
  891. }
  892. }
  893. break;
  894. case TBN_DROPDOWN:
  895. lres = _OnDropDown((LPNMTOOLBAR)pnm);
  896. break;
  897. #ifdef UNICODE
  898. case TBN_GETINFOTIPA:
  899. {
  900. LPNMTBGETINFOTIPA pnmTT = (LPNMTBGETINFOTIPA)pnm;
  901. UINT uiCmd = pnmTT->iItem;
  902. TCHAR szTip[MAX_PATH];
  903. if ( S_OK == v_GetInfoTip(pnmTT->iItem, szTip, ARRAYSIZE(szTip)) )
  904. {
  905. SHUnicodeToAnsi(szTip, pnmTT->pszText, pnmTT->cchTextMax);
  906. }
  907. else
  908. {
  909. // Set the lpszText to NULL to prevent the toolbar from setting
  910. // the button text by default
  911. pnmTT->pszText = NULL;
  912. }
  913. lres = 1;
  914. break;
  915. }
  916. #endif
  917. case TBN_GETINFOTIP:
  918. {
  919. LPNMTBGETINFOTIP pnmTT = (LPNMTBGETINFOTIP)pnm;
  920. UINT uiCmd = pnmTT->iItem;
  921. if ( S_OK != v_GetInfoTip(pnmTT->iItem, pnmTT->pszText, pnmTT->cchTextMax) )
  922. {
  923. // Set the lpszText to NULL to prevent the toolbar from setting
  924. // the button text by default
  925. pnmTT->pszText = NULL;
  926. }
  927. lres = 1;
  928. break;
  929. }
  930. case NM_RCLICK:
  931. // When we go into a context menu, stop monitoring.
  932. KillTimer(_hwndMB, MBTIMER_EXPAND);
  933. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  934. break;
  935. case TBN_WRAPHOTITEM:
  936. lres = _OnWrapHotItem((NMTBWRAPHOTITEM*)pnm);
  937. break;
  938. case TBN_WRAPACCELERATOR:
  939. lres = _OnWrapAccelerator((NMTBWRAPACCELERATOR*)pnm);
  940. break;
  941. case TBN_DUPACCELERATOR:
  942. lres = _OnDupAccelerator((NMTBDUPACCELERATOR*)pnm);
  943. break;
  944. case TBN_DRAGOVER:
  945. // This message is sent when drag and drop within the toolbar indicates that it
  946. // is about to mark a button. Since this gets messed up because of LockWindowUpdate
  947. // we tell it not to do anything.
  948. lres = 1;
  949. break;
  950. }
  951. return(lres);
  952. }
  953. BOOL CMenuToolbarBase::_SetTimer(int nTimer)
  954. {
  955. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  956. long lTimeOut;
  957. #ifndef UNIX
  958. // If we're on NT5 or Win98, use the cool new SPI
  959. if (SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &g_lMenuPopupTimeout, 0)) {
  960. // Woo-hoo, all done.
  961. }
  962. else if (g_lMenuPopupTimeout == -1)
  963. #endif
  964. {
  965. // NT4 or Win95. Grovel the registry (yuck).
  966. DWORD dwType;
  967. TCHAR szDelay[6]; // int is 5 characters + null.
  968. DWORD cbSize = ARRAYSIZE(szDelay);
  969. g_lMenuPopupTimeout = MBTIMER_TIMEOUT;
  970. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"),
  971. TEXT("MenuShowDelay"), &dwType, (void*)szDelay, &cbSize))
  972. {
  973. g_lMenuPopupTimeout = (UINT)StrToInt(szDelay);
  974. }
  975. }
  976. lTimeOut = g_lMenuPopupTimeout;
  977. switch (nTimer)
  978. {
  979. case MBTIMER_EXPAND:
  980. case MBTIMER_DRAGPOPDOWN:
  981. lTimeOut *= 2;
  982. if (lTimeOut < MAXUEMTIMEOUT)
  983. lTimeOut = MAXUEMTIMEOUT;
  984. break;
  985. case MBTIMER_UEMTIMEOUT:
  986. if (!_fHasDemotedItems || _pcmb->_pmbState->GetExpand() || _fEditMode)
  987. return TRUE;
  988. lTimeOut *= 5;
  989. // We want a minimum of MAXUEMTIMEOUT for people who set the expand rate to zero
  990. if (lTimeOut < MAXUEMTIMEOUT)
  991. lTimeOut = MAXUEMTIMEOUT;
  992. TraceMsg(TF_MENUBAND, "*** UEM SetTimeOut to (%d) milliseconds"
  993. "at Tick Count (%d).*** ", GetTickCount());
  994. break;
  995. case MBTIMER_CHEVRONTIP:
  996. lTimeOut = 60 * 1000; // Please make the intellimenu's balloon tip go
  997. // away after one minute of no action.
  998. break;
  999. case MBTIMER_INFOTIP:
  1000. lTimeOut = 500; // Half a second hovering over an item?
  1001. break;
  1002. }
  1003. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): Setting %d Timer to %d milliseconds at tickcount %d",
  1004. this, nTimer, lTimeOut, GetTickCount());
  1005. return (BOOL)SetTimer(_hwndMB, nTimer, lTimeOut, NULL);
  1006. }
  1007. BOOL CMenuToolbarBase::_HandleObscuredItem(int idCmd)
  1008. {
  1009. RECT rc;
  1010. GetClientRect(_hwndMB, &rc);
  1011. int iButton = (int)SendMessage(_hwndMB, TB_COMMANDTOINDEX, idCmd, 0);
  1012. if (SHIsButtonObscured(_hwndMB, &rc, iButton))
  1013. {
  1014. // clear hot item
  1015. ToolBar_SetHotItem(_hwndMB, -1);
  1016. _pcmb->_SubMenuOnSelect(MPOS_FULLCANCEL);
  1017. _pcmb->_CancelMode(MPOS_FULLCANCEL); // This is for the track menus.
  1018. HWND hwnd = _pcmb->_pmbState->GetSubclassedHWND();
  1019. PostMessage(hwnd? hwnd: _hwndMB, g_nMBOpenChevronMenu, (WPARAM)idCmd, 0);
  1020. return TRUE;
  1021. }
  1022. return FALSE;
  1023. }
  1024. LRESULT CMenuToolbarBase::_OnHotItemChange(NMTBHOTITEM * pnmhot)
  1025. {
  1026. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1027. LRESULT lres = 0;
  1028. #ifdef UNIX
  1029. // IEUNIX : If this is a mouse move check if the left button is pressed
  1030. // deviating from Windows behavior to be motif compliant.
  1031. if (_fVerticalMB && (pnmhot->dwFlags & HICF_MOUSE) && !(pnmhot->dwFlags & HICF_LMOUSE))
  1032. return 1;
  1033. #endif
  1034. if (_pcmb->_fMenuMode && _pcmb->_fShow && !_fIgnoreHotItemChange)
  1035. {
  1036. // Always kill the expand timer when something changes
  1037. KillTimer(_hwndMB, MBTIMER_EXPAND);
  1038. KillTimer(_hwndMB, MBTIMER_INFOTIP);
  1039. // Is this toolbar being entered?
  1040. if (!(pnmhot->dwFlags & HICF_LEAVING))
  1041. {
  1042. // Yes; set it to be the currently tracking toolbar
  1043. TraceMsg(TF_MENUBAND, "CMTB::OnHotItemChange. Setting Tracked....", this);
  1044. _pcmb->SetTracked(this);
  1045. _pcmb->_pmbState->HideTooltip(FALSE);
  1046. _SetTimer(MBTIMER_INFOTIP);
  1047. }
  1048. // If the Toolbar has keybaord focus, we need to send OBJID_CLIENT so that we track correctly.
  1049. if (!(pnmhot->dwFlags & HICF_LEAVING))
  1050. {
  1051. NotifyWinEvent(EVENT_OBJECT_FOCUS, _hwndMB, OBJID_CLIENT,
  1052. GetIndexFromChild(_dwFlags & SMSET_TOP, ToolBar_CommandToIndex(_hwndMB, pnmhot->idNew)));
  1053. }
  1054. DEBUG_CODE( TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHANGE (state:%#02lx, %d-->%d)",
  1055. this, pnmhot->dwFlags,
  1056. (pnmhot->dwFlags & HICF_ENTERING) ? -1 : pnmhot->idOld,
  1057. (pnmhot->dwFlags & HICF_LEAVING) ? -1 : pnmhot->idNew); )
  1058. // While in edit mode, we do not automatically cascade
  1059. // submenus, unless while dropping. But the dropping case
  1060. // is handled in HitTest, not here. So don't deal with that
  1061. // here.
  1062. // Is this because an accelerator key was hit?
  1063. if (pnmhot->dwFlags & HICF_ACCELERATOR)
  1064. {
  1065. KillPopupTimer();
  1066. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  1067. // Yes; now that TBSTYLE_DROPDOWN is used, let _DropDownOrExec handle it
  1068. // in response to TBN_DROPDOWN.
  1069. }
  1070. // Is this because direction keys were hit?
  1071. else if (pnmhot->dwFlags & HICF_ARROWKEYS)
  1072. {
  1073. // Yes
  1074. KillPopupTimer();
  1075. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  1076. if (!_fVerticalMB &&
  1077. _HandleObscuredItem(pnmhot->idNew))
  1078. {
  1079. lres = 1;
  1080. }
  1081. else
  1082. {
  1083. // It doesn't make sense that we would get these keyboard
  1084. // notifications if there is a submenu open...it should get
  1085. // the messages
  1086. ASSERT(!_pcmb->_fInSubMenu);
  1087. v_SendMenuNotification(pnmhot->idNew, FALSE);
  1088. // Since the only way that the chevron can get the highlight is
  1089. // through a keyboard down, then we expand.
  1090. if (_fHasDemotedItems && pnmhot->idNew == (int)_idCmdChevron)
  1091. {
  1092. v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
  1093. Expand(TRUE);
  1094. lres = 1; // We already handled the hot item change
  1095. }
  1096. }
  1097. _pcmb->_pmbState->HideTooltip(FALSE);
  1098. _SetTimer(MBTIMER_INFOTIP);
  1099. }
  1100. // Is this because the mouse moved or an explicit sendmessage?
  1101. else if (!(pnmhot->dwFlags & HICF_LEAVING) &&
  1102. (pnmhot->idNew != _pcmb->_nItemCur || // Ignore if we're moving over same item
  1103. (_nItemTimer != -1 && _pcmb->_nItemCur == pnmhot->idNew))) // we need to go through here to reset if the user went back to the cascaded guy
  1104. {
  1105. // Yes
  1106. if (!_fVerticalMB) // Horizontal menus will always have an underlying hmenu
  1107. {
  1108. if (_HandleObscuredItem(pnmhot->idNew))
  1109. {
  1110. lres = 1;
  1111. }
  1112. else if (_pcmb->_fInSubMenu)
  1113. {
  1114. // Only popup a menu since we're already in one (as mouse
  1115. // moves across bar).
  1116. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHG: Posting CMBPopup message", this);
  1117. PostPopup(pnmhot->idNew, FALSE, _pcmb->_fKeyboardSelected); // Will handle menu notification on receipt of message
  1118. }
  1119. else
  1120. v_SendMenuNotification(pnmhot->idNew, FALSE);
  1121. }
  1122. else if (!_fEditMode)
  1123. {
  1124. v_SendMenuNotification(pnmhot->idNew, FALSE);
  1125. // check to see if we have just entered a new item and it is a sub-menu...
  1126. // Did we already set a timer?
  1127. if (-1 != _nItemTimer)
  1128. {
  1129. // Yes; kill it b/c the mouse moved to another item
  1130. KillPopupTimer();
  1131. }
  1132. // if we're not over the currently expanded guy
  1133. // Have we moved over an item that expands OR
  1134. // are we moving away from a cascaded item?
  1135. DWORD dwFlags = v_GetFlags(pnmhot->idNew);
  1136. // Reset the stupid user timer
  1137. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  1138. // UEMStuff
  1139. if (!(dwFlags & SMIF_SUBMENU))
  1140. {
  1141. _SetTimer(MBTIMER_UEMTIMEOUT);
  1142. _FireEvent(UEM_HOT_ITEM);
  1143. }
  1144. if ( (pnmhot->dwFlags & HICF_MOUSE) && _pcmb->_nItemCur != pnmhot->idNew)
  1145. {
  1146. if (dwFlags & SMIF_SUBMENU || _pcmb->_fInSubMenu)
  1147. {
  1148. // Is this the only item in the menu?
  1149. if ( _cPromotedItems == 1 &&
  1150. !(_fHasDemotedItems && _pcmb->_fExpanded) &&
  1151. dwFlags & SMIF_SUBMENU)
  1152. {
  1153. // Yes; Then we want to pop it open immediatly,
  1154. // instead of waiting for the timeout
  1155. PostPopup(pnmhot->idNew, FALSE, FALSE);
  1156. }
  1157. else if (_SetTimer(MBTIMER_POPOUT))
  1158. {
  1159. // No; fire a timer to open/close the submenu
  1160. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHG: Starting timer for id=%d", this, pnmhot->idNew);
  1161. if (v_GetFlags(pnmhot->idNew) & SMIF_SUBMENU)
  1162. _nItemTimer = pnmhot->idNew;
  1163. else
  1164. _nItemTimer = -1;
  1165. }
  1166. }
  1167. if (_fHasDemotedItems && pnmhot->idNew == (int)_idCmdChevron)
  1168. {
  1169. _SetTimer(MBTIMER_EXPAND);
  1170. }
  1171. _pcmb->_pmbState->HideTooltip(FALSE);
  1172. _SetTimer(MBTIMER_INFOTIP);
  1173. }
  1174. }
  1175. }
  1176. else if (pnmhot->dwFlags & HICF_LEAVING)
  1177. {
  1178. v_SendMenuNotification(pnmhot->idOld, TRUE);
  1179. if (-1 != _nItemTimer && !_fEditMode)
  1180. {
  1181. // kill the cascading menu popup timer...
  1182. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): TBN_HOTITEMCHG: Killing timer", this);
  1183. KillPopupTimer();
  1184. }
  1185. _pcmb->_pmbState->HideTooltip(FALSE);
  1186. }
  1187. if ( !(pnmhot->dwFlags & HICF_LEAVING) )
  1188. _pcmb->_SiteOnSelect(MPOS_CHILDTRACKING);
  1189. }
  1190. return lres;
  1191. }
  1192. void CMenuToolbarBase::s_FadeCallback(DWORD dwStep, LPVOID pvParam)
  1193. {
  1194. CMenuToolbarBase* pmtb = (CMenuToolbarBase*)pvParam;
  1195. if (pmtb && dwStep == FADE_BEGIN) // Paranoia
  1196. {
  1197. // Command has been posted. Exit menu.
  1198. pmtb->_pcmb->_SiteOnSelect(MPOS_EXECUTE);
  1199. }
  1200. }
  1201. LRESULT CMenuToolbarBase::_DropDownOrExec(UINT idCmd, BOOL bKeyboard)
  1202. {
  1203. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1204. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _DropDownOrExec %d", this, idCmd);
  1205. // Don't do anything when we're in edit mode
  1206. if (_fEditMode)
  1207. return 0;
  1208. if ( v_GetFlags(idCmd) & SMIF_SUBMENU )
  1209. {
  1210. v_SendMenuNotification(idCmd, FALSE);
  1211. PostPopup(idCmd, FALSE, bKeyboard);
  1212. }
  1213. else if (idCmd != -1)
  1214. {
  1215. RECT rc;
  1216. AddRef(); // I might get released in the call.
  1217. // Fading Selection
  1218. IEPlaySound(TEXT("MenuCommand"), TRUE);
  1219. SendMessage(_hwndMB, TB_GETRECT, idCmd, (LPARAM)&rc);
  1220. MapWindowPoints(_hwndMB, HWND_DESKTOP, (POINT*)&rc, 2);
  1221. if (!(GetKeyState(VK_SHIFT) < 0))
  1222. {
  1223. // Were we able to fade?
  1224. if (!_pcmb->_pmbState->FadeRect(&rc, s_FadeCallback, this))
  1225. {
  1226. // No; Then we blow away the menus here instead of the Fade callback
  1227. // Command has been posted. Exit menu.
  1228. _pcmb->_SiteOnSelect(MPOS_EXECUTE);
  1229. }
  1230. }
  1231. if (g_dwProfileCAP & 0x00002000)
  1232. StartCAP();
  1233. v_ExecItem(idCmd);
  1234. if (g_dwProfileCAP & 0x00002000)
  1235. StopCAP();
  1236. Release();
  1237. }
  1238. else
  1239. MessageBeep(MB_OK);
  1240. return 0;
  1241. }
  1242. /*----------------------------------------------------------
  1243. Purpose: Handles TBN_DROPDOWN, which is sent on the button-down.
  1244. */
  1245. LRESULT CMenuToolbarBase::_OnDropDown(LPNMTOOLBAR pnmtb)
  1246. {
  1247. DWORD dwInput = _fTopLevel ? 0 : -1; // -1: don't track, 0: do
  1248. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1249. LRESULT lres = 0;
  1250. // Expected behavior with the mouse:
  1251. //
  1252. // 1) For cascading menuitems-
  1253. // a) expand on button-down
  1254. // b) collapse on button-up (horizontal menu only)
  1255. // c) if the button-down occurs on the item that is
  1256. // already selected, then assume the click indicates
  1257. // a drag/drop scenario
  1258. // 2) For other menuitems-
  1259. // a) execute on button-up
  1260. #ifdef DEBUG
  1261. if (_fTopLevel) {
  1262. // browser menu comes thru here; start menu goes elsewhere (via tray.c)
  1263. //ASSERT(!_fVertical);
  1264. TraceMsg(DM_MISC, "cmtbb._odd: _fTopLevel(1) mouse=%d", GetKeyState(VK_LBUTTON) < 0);
  1265. }
  1266. #endif
  1267. // Is this because the mouse button was used?
  1268. if (GetKeyState(VK_LBUTTON) < 0)
  1269. {
  1270. // Yes
  1271. // Assume it won't be handled. This will allow the toolbar
  1272. // to see the button-down as a potential drag and drop.
  1273. lres = TBDDRET_TREATPRESSED;
  1274. // Clicking on same item that is currently expanded?
  1275. if (pnmtb->iItem == _pcmb->_nItemCur)
  1276. {
  1277. // Is this horizontal?
  1278. if (!_fVerticalMB)
  1279. {
  1280. // Yes; toggle the dropdown
  1281. _pcmb->_SubMenuOnSelect(MPOS_FULLCANCEL);
  1282. // Say it is handled, so the button will toggle
  1283. lres = TBDDRET_DEFAULT;
  1284. }
  1285. _fClickHandled = TRUE;
  1286. // Otherwise don't do anything more, user might be starting a
  1287. // drag-drop procedure on the cascading menuitem
  1288. }
  1289. else
  1290. {
  1291. if (v_GetFlags(pnmtb->iItem) & SMIF_SUBMENU)
  1292. {
  1293. // Handle on the button-down
  1294. _fClickHandled = TRUE;
  1295. lres = _DropDownOrExec(pnmtb->iItem, FALSE);
  1296. }
  1297. }
  1298. if (dwInput != -1)
  1299. dwInput = UIBL_INPMOUSE;
  1300. }
  1301. else
  1302. {
  1303. // No; must be the keyboard
  1304. _fClickHandled = TRUE;
  1305. lres = _DropDownOrExec(pnmtb->iItem, TRUE);
  1306. if (dwInput != -1)
  1307. dwInput = UIBL_INPMENU;
  1308. }
  1309. // browser menu (*not* start menu) alt+key, mouse
  1310. if (dwInput != -1)
  1311. UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UIINPUT, dwInput);
  1312. return lres;
  1313. }
  1314. /*----------------------------------------------------------
  1315. Purpose: Handle WM_KEYDOWN/WM_KEYUP
  1316. Returns: TRUE if handled
  1317. */
  1318. BOOL CMenuToolbarBase::_OnKey(BOOL bDown, UINT vk, UINT uFlags)
  1319. {
  1320. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1321. int idCmd;
  1322. HWND hwnd = _hwndMB;
  1323. _pcmb->_pmbState->SetKeyboardCue(TRUE);
  1324. //
  1325. // If the menu window is RTL mirrored, then the arrow keys should
  1326. // be mirrored to reflect proper cursor movement. [samera]
  1327. //
  1328. if (IS_WINDOW_RTL_MIRRORED(hwnd))
  1329. {
  1330. switch (vk)
  1331. {
  1332. case VK_LEFT:
  1333. vk = VK_RIGHT;
  1334. break;
  1335. case VK_RIGHT:
  1336. vk = VK_LEFT;
  1337. break;
  1338. }
  1339. }
  1340. switch (vk)
  1341. {
  1342. case VK_LEFT:
  1343. if (_fVerticalMB)
  1344. {
  1345. _pcmb->_SiteOnSelect(MPOS_SELECTLEFT);
  1346. return TRUE;
  1347. }
  1348. break;
  1349. case VK_RIGHT:
  1350. if (_fVerticalMB)
  1351. goto Cascade;
  1352. break;
  1353. case VK_DOWN:
  1354. case VK_UP:
  1355. if (!_fVerticalMB)
  1356. {
  1357. Cascade:
  1358. idCmd = GetButtonCmd(hwnd, ToolBar_GetHotItem(hwnd));
  1359. if (v_GetFlags(idCmd) & SMIF_SUBMENU)
  1360. {
  1361. // Enter the submenu
  1362. TraceMsg(TF_MENUBAND, "(pmb=%#08lx): _OnKey: Posting CMBPopup message", this);
  1363. PostPopup(idCmd, FALSE, TRUE);
  1364. }
  1365. else if (VK_RIGHT == vk)
  1366. {
  1367. // Nothing to cascade to, move to next sibling menu
  1368. _pcmb->_SiteOnSelect(MPOS_SELECTRIGHT);
  1369. }
  1370. return TRUE;
  1371. }
  1372. else
  1373. {
  1374. #if 0
  1375. _pcmb->_OnSelectArrow(vk == VK_UP? -1 : 1);
  1376. return TRUE;
  1377. #endif
  1378. }
  1379. break;
  1380. case VK_SPACE:
  1381. if (!_pcmb->_fExpanded && _fHasDemotedItems)
  1382. {
  1383. v_CallCBItem(_idCmdChevron, SMC_CHEVRONEXPAND, 0, 0);
  1384. Expand(TRUE);
  1385. }
  1386. else
  1387. {
  1388. // Toolbars map the spacebar to VK_RETURN. Menus don't except
  1389. // in the horizontal menubar.
  1390. if (_fVerticalMB)
  1391. MessageBeep(MB_OK);
  1392. }
  1393. return TRUE;
  1394. #if 0
  1395. case VK_RETURN:
  1396. // Handle this now, rather than letting the toolbar handle it.
  1397. // This way we don't have to rely on WM_COMMAND, which doesn't
  1398. // convey whether it was invoked by the keyboard or the mouse.
  1399. idCmd = GetButtonCmd(hwnd, ToolBar_GetHotItem(hwnd));
  1400. _DropDownOrExec(idCmd, TRUE);
  1401. return TRUE;
  1402. #endif
  1403. }
  1404. return FALSE;
  1405. }
  1406. /*----------------------------------------------------------
  1407. Purpose: There are two flavors of this function: _DoPopup and
  1408. PostPopup. Both cancel the existing submenu (relative
  1409. to this band) and pops open a new submenu. _DoPopup
  1410. does it atomically. PostPopup posts a message to
  1411. handle it.
  1412. */
  1413. void CMenuToolbarBase::_DoPopup(int idCmd, BOOL bInitialSelect)
  1414. {
  1415. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1416. if (-1 != idCmd)
  1417. {
  1418. PopupHelper(idCmd, bInitialSelect);
  1419. }
  1420. }
  1421. /*----------------------------------------------------------
  1422. Purpose: See the _DoPopup comment
  1423. */
  1424. void CMenuToolbarBase::PostPopup(int idCmd, BOOL bSetItem, BOOL bInitialSelect)
  1425. {
  1426. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1427. if (-1 != idCmd)
  1428. {
  1429. _pcmb->_SubMenuOnSelect(MPOS_CANCELLEVEL);
  1430. _pcmb->SetTracked(this);
  1431. HWND hwnd = _pcmb->_pmbState->GetSubclassedHWND();
  1432. PostMessage(hwnd? hwnd: _hwndMB, g_nMBPopupOpen, idCmd, MAKELPARAM(bSetItem, bInitialSelect));
  1433. }
  1434. }
  1435. /*----------------------------------------------------------
  1436. Purpose: Helper function to finally invoke submenu. Use _DoPopup
  1437. or PostPopup
  1438. */
  1439. void CMenuToolbarBase::PopupHelper(int idCmd, BOOL bInitialSelect)
  1440. {
  1441. // We do not want to pop open a sub menu if we are not displayed. This is especially
  1442. // a problem during drag and drop.
  1443. if (_fShowMB)
  1444. {
  1445. _pcmb->_nItemNew = idCmd;
  1446. ASSERT(-1 != _pcmb->_nItemNew);
  1447. _pcmb->SetTracked(this);
  1448. _pcmb->_fPopupNewMenu = TRUE;
  1449. _pcmb->_fInitialSelect = BOOLIFY(bInitialSelect);
  1450. _pcmb->UIActivateIO(TRUE, NULL);
  1451. _FireEvent(UEM_HOT_FOLDER);
  1452. _SetTimer(MBTIMER_UEMTIMEOUT);
  1453. }
  1454. }
  1455. void CMenuToolbarBase::_PaintButton(HDC hdc, int idCmd, LPRECT prc, DWORD dwSMIF)
  1456. {
  1457. if (!_pcmb->_fExpanded)
  1458. return;
  1459. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1460. RECT rcClient;
  1461. GetClientRect(_hwndMB, &rcClient);
  1462. #ifndef DRAWEDGE
  1463. // Draw Left Edge
  1464. HPEN hPenOld = (HPEN)SelectObject(hdc, _pcmb->_pmbm->_hPenHighlight);
  1465. MoveToEx(hdc, prc->left, prc->top, NULL);
  1466. LineTo(hdc, prc->left, prc->bottom);
  1467. #endif
  1468. if (!(dwSMIF & SMIF_DEMOTED))
  1469. {
  1470. #ifdef DRAWEDGE
  1471. DWORD dwEdge = BF_RIGHT;
  1472. // Don't paint the edge next to the bitmap.
  1473. if (_uIconSizeMB == ISFBVIEWMODE_SMALLICONS)
  1474. dwEdge |= BF_LEFT;
  1475. RECT rc = *prc;
  1476. #else
  1477. // Draw Right Edge:
  1478. SelectObject(hdc, _pcmb->_pmbm->_hPenShadow);
  1479. MoveToEx(hdc, prc->right-1, prc->top, NULL);
  1480. LineTo(hdc, prc->right-1, prc->bottom);
  1481. #endif
  1482. HWND hwnd = _hwndMB;
  1483. int iPos = ToolBar_CommandToIndex(hwnd, idCmd);
  1484. if (iPos == -1)
  1485. {
  1486. iPos = ToolBar_ButtonCount(hwnd) - 1;
  1487. }
  1488. if (iPos >= 0)
  1489. {
  1490. int iNumButtons = ToolBar_ButtonCount(hwnd);
  1491. int idCmd2 = GetButtonCmd(hwnd, iPos + 1);
  1492. CMenuToolbarBase* pmtb = this;
  1493. BOOL fOverflowed = FALSE;
  1494. // Situations for Drawing the Bottom line
  1495. // 1) This button is at the bottom.
  1496. // 2) This button is at the bottom and the toolbar
  1497. // below is not visible (_fDontShowEmpty).
  1498. // 3) This button is at the bottom and the button
  1499. // at the top of the bottom toolbar is demoted.
  1500. // 4) The button below this one in the toolbar is
  1501. // demoted.
  1502. // 5) The botton below this one is demoted and we're
  1503. // not expanded
  1504. if (iPos + 1 >= iNumButtons)
  1505. {
  1506. if (_pcmb->_pmtbBottom != this &&
  1507. !_pcmb->_pmtbBottom->_fDontShowEmpty)
  1508. {
  1509. pmtb = _pcmb->_pmtbBottom;
  1510. hwnd = pmtb->_hwndMB;
  1511. idCmd2 = GetButtonCmd(hwnd, 0);
  1512. }
  1513. else
  1514. fOverflowed = TRUE;
  1515. }
  1516. else if (prc->bottom == rcClient.bottom &&
  1517. _pcmb->_pmtbBottom == this) // This button is at the top.
  1518. fOverflowed = TRUE;
  1519. DWORD dwFlags = pmtb->v_GetFlags(idCmd2);
  1520. if ((_pcmb->_fExpanded && dwFlags & SMIF_DEMOTED) ||
  1521. fOverflowed)
  1522. {
  1523. #ifdef DRAWEDGE
  1524. dwEdge |= BF_BOTTOM;
  1525. #else
  1526. int iLeft = prc->left;
  1527. if (iPos != iNumButtons - 1)
  1528. iLeft ++; // Move the next line in.
  1529. MoveToEx(hdc, iLeft, prc->bottom-1, NULL);
  1530. LineTo(hdc, prc->right-1, prc->bottom-1);
  1531. #endif
  1532. }
  1533. // Situations for Drawing the Top line
  1534. // 1) This button is at the top.
  1535. // 2) This button is at the top and the toolbar
  1536. // above is not visible (_fDontShowEmpty).
  1537. // 3) This button is at the top and the button
  1538. // at the bottom of the top toolbar is demoted.
  1539. // 4) The button above this one in the toolbar is
  1540. // demoted.
  1541. // 5) If the button above this is demoted, and we're
  1542. // not expanded
  1543. fOverflowed = FALSE;
  1544. if (iPos - 1 < 0)
  1545. {
  1546. if (_pcmb->_pmtbTop != this &&
  1547. !_pcmb->_pmtbTop->_fDontShowEmpty)
  1548. {
  1549. pmtb = _pcmb->_pmtbTop;
  1550. hwnd = pmtb->_hwndMB;
  1551. idCmd2 = GetButtonCmd(hwnd, ToolBar_ButtonCount(hwnd) - 1);
  1552. }
  1553. else
  1554. fOverflowed = TRUE; // There is nothing at the top of this menu, draw the line.
  1555. }
  1556. else
  1557. {
  1558. hwnd = _hwndMB;
  1559. idCmd2 = GetButtonCmd(hwnd, iPos - 1);
  1560. pmtb = this;
  1561. if (prc->top == rcClient.top &&
  1562. _pcmb->_pmtbTop == this) // This button is at the top.
  1563. fOverflowed = TRUE;
  1564. }
  1565. dwFlags = pmtb->v_GetFlags(idCmd2);
  1566. if ((_pcmb->_fExpanded && dwFlags & SMIF_DEMOTED) ||
  1567. fOverflowed)
  1568. {
  1569. #ifdef DRAWEDGE
  1570. dwEdge |= BF_TOP;
  1571. #else
  1572. SelectObject(hdc, _pcmb->_pmbm->_hPenHighlight);
  1573. MoveToEx(hdc, prc->left, prc->top, NULL);
  1574. LineTo(hdc, prc->right-1, prc->top);
  1575. #endif
  1576. }
  1577. }
  1578. #ifdef DRAWEDGE
  1579. DrawEdge(hdc, &rc, BDR_RAISEDINNER, dwEdge);
  1580. #endif
  1581. }
  1582. #ifndef DRAWEDGE
  1583. SelectObject(hdc, hPenOld);
  1584. #endif
  1585. }
  1586. LRESULT CMenuToolbarBase::_OnCustomDraw(NMCUSTOMDRAW * pnmcd)
  1587. {
  1588. // Make it look like a menu
  1589. NMTBCUSTOMDRAW * ptbcd = (NMTBCUSTOMDRAW *)pnmcd;
  1590. DWORD dwRet = 0;
  1591. // Edit mode never hot tracks, and the selected item being
  1592. // moved has a black frame around it. Items that cascade are
  1593. // still highlighted normally, even in edit mode.
  1594. DWORD dwSMIF = v_GetFlags((UINT)pnmcd->dwItemSpec);
  1595. switch(pnmcd->dwDrawStage)
  1596. {
  1597. case CDDS_PREPAINT:
  1598. dwRet = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
  1599. break;
  1600. case CDDS_ITEMPREPAINT:
  1601. if (_fVerticalMB)
  1602. {
  1603. if (pnmcd->dwItemSpec == -1)
  1604. {
  1605. // a -1 is sent with a seperator
  1606. RECT rc = pnmcd->rc;
  1607. rc.top += 3; // Hard coded in toolbar.
  1608. rc.left += GetSystemMetrics(SM_CXEDGE);
  1609. rc.right -= GetSystemMetrics(SM_CXEDGE);
  1610. DrawEdge(pnmcd->hdc, &rc, EDGE_ETCHED, BF_TOP);
  1611. _PaintButton(pnmcd->hdc, -1, &pnmcd->rc, dwSMIF);
  1612. dwRet = CDRF_SKIPDEFAULT;
  1613. }
  1614. else
  1615. {
  1616. ptbcd->clrText = _pcmb->_pmbm->_clrMenuText;
  1617. // This is for Darwin Ads.
  1618. if (dwSMIF & SMIF_ALTSTATE)
  1619. {
  1620. ptbcd->clrText = GetSysColor(COLOR_BTNSHADOW);
  1621. }
  1622. ptbcd->rcText.right = ptbcd->rcText.right - _pcmb->_pmbm->_cxMargin;
  1623. ptbcd->clrBtnFace = _pcmb->_pmbm->_clrBackground;
  1624. if (_fHasSubMenu)
  1625. ptbcd->rcText.right -= _pcmb->_pmbm->_cxArrow;
  1626. if ( _fHasDemotedItems && _idCmdChevron == (int)pnmcd->dwItemSpec)
  1627. {
  1628. _DrawChevron(pnmcd->hdc, &pnmcd->rc,
  1629. (BOOL)(pnmcd->uItemState & CDIS_HOT) ||
  1630. (BOOL)(pnmcd->uItemState & CDIS_MARKED),
  1631. (BOOL)(pnmcd->uItemState & CDIS_SELECTED) );
  1632. dwRet |= CDRF_SKIPDEFAULT;
  1633. }
  1634. else
  1635. {
  1636. #ifdef MARK_DRAGGED_ITEM
  1637. // We have no good way to undo this on a multi pane drop.
  1638. if (_idCmdDragging != -1 &&
  1639. _idCmdDragging == (int)pnmcd->dwItemSpec)
  1640. pnmcd->uItemState |= CDIS_HOT;
  1641. #endif
  1642. // Yes; draw with highlight
  1643. if (pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT))
  1644. {
  1645. #ifdef UNIX
  1646. if( MwCurrentLook() == LOOK_MOTIF )
  1647. SelectMotifMenu(pnmcd->hdc, &pnmcd->rc, TRUE );
  1648. else
  1649. #endif
  1650. {
  1651. ptbcd->clrHighlightHotTrack = GetSysColor(COLOR_HIGHLIGHT);
  1652. ptbcd->clrBtnFace = GetSysColor(COLOR_HIGHLIGHT);
  1653. ptbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1654. dwRet |= TBCDRF_HILITEHOTTRACK;
  1655. }
  1656. }
  1657. // Is this menu empty?
  1658. if (_fEmpty)
  1659. {
  1660. // Yes, draw the empty string as disabled.
  1661. pnmcd->uItemState |= CDIS_DISABLED;
  1662. ptbcd->clrText = ptbcd->clrBtnFace;
  1663. // Don't draw the etched effect if it is selected
  1664. if (pnmcd->uItemState & CDIS_HOT)
  1665. dwRet |= TBCDRF_NOETCHEDEFFECT;
  1666. }
  1667. // When this item is demoted, we only want to paint his background
  1668. // then we are in edit mode _OR_ it is not selected, checked or hot.
  1669. if (dwSMIF & SMIF_DEMOTED)
  1670. {
  1671. BOOL fDrawDemoted = TRUE;
  1672. if (_fEditMode)
  1673. fDrawDemoted = TRUE;
  1674. if (pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT))
  1675. fDrawDemoted = FALSE;
  1676. if (fDrawDemoted)
  1677. {
  1678. ptbcd->clrBtnFace = _pcmb->_pmbm->_clrDemoted;
  1679. SHFillRectClr(pnmcd->hdc, &pnmcd->rc, ptbcd->clrBtnFace);
  1680. }
  1681. }
  1682. // We draw our own highlighting
  1683. dwRet |= (TBCDRF_NOEDGES | TBCDRF_NOOFFSET);
  1684. }
  1685. }
  1686. }
  1687. else
  1688. {
  1689. // If g_fRunOnMemphis or g_fRunOnNT5 are not defined then the menus will
  1690. // never be grey.
  1691. if (!_pcmb->_fAppActive)
  1692. // menus from user use Button Shadow for non active menus
  1693. ptbcd->clrText = GetSysColor(COLOR_3DSHADOW);
  1694. else
  1695. ptbcd->clrText = _pcmb->_pmbm->_clrMenuText;
  1696. // If we're in high contrast mode, make the menu bar look like
  1697. // veritcal items on select.
  1698. if (_pcmb->_pmbm->_fHighContrastMode)
  1699. {
  1700. // Yes; draw with highlight
  1701. if (pnmcd->uItemState & (CDIS_CHECKED | CDIS_SELECTED | CDIS_HOT))
  1702. {
  1703. #ifdef UNIX
  1704. if( MwCurrentLook() == LOOK_MOTIF )
  1705. SelectMotifMenu(pnmcd->hdc, &pnmcd->rc, TRUE );
  1706. else
  1707. #endif
  1708. {
  1709. ptbcd->clrHighlightHotTrack = GetSysColor(COLOR_HIGHLIGHT);
  1710. ptbcd->clrBtnFace = GetSysColor(COLOR_HIGHLIGHT);
  1711. ptbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1712. dwRet |= TBCDRF_HILITEHOTTRACK;
  1713. }
  1714. }
  1715. }
  1716. }
  1717. dwRet |= CDRF_NOTIFYPOSTPAINT | TBCDRF_NOMARK;
  1718. break;
  1719. case CDDS_ITEMPOSTPAINT:
  1720. if (_fVerticalMB)
  1721. {
  1722. RECT rc = pnmcd->rc;
  1723. COLORREF rgbText;
  1724. if (pnmcd->uItemState & (CDIS_SELECTED | CDIS_HOT))
  1725. rgbText = GetSysColor( COLOR_HIGHLIGHTTEXT );
  1726. else
  1727. rgbText = _pcmb->_pmbm->_clrMenuText;
  1728. // Is this item Checked?
  1729. if (dwSMIF & SMIF_CHECKED)
  1730. {
  1731. rc.right = rc.left + (rc.bottom - rc.top);
  1732. _DrawMenuGlyph(pnmcd->hdc, _pcmb->_pmbm->_hFontArrow
  1733. , &rc, CH_MENUCHECKA, rgbText, NULL);
  1734. rc = pnmcd->rc;
  1735. }
  1736. // Is this a cascading item?
  1737. if (dwSMIF & SMIF_SUBMENU)
  1738. {
  1739. // Yes; draw the arrow
  1740. RECT rcT = rc;
  1741. rcT.left = rcT.right - _pcmb->_pmbm->_cxArrow;
  1742. _DrawMenuArrowGlyph(pnmcd->hdc, &rcT, rgbText);
  1743. }
  1744. _PaintButton(pnmcd->hdc, (UINT)pnmcd->dwItemSpec, &rc, dwSMIF);
  1745. }
  1746. break;
  1747. case CDDS_PREERASE:
  1748. {
  1749. RECT rcClient;
  1750. GetClientRect(_hwndMB, &rcClient);
  1751. ptbcd->clrBtnFace = _pcmb->_pmbm->_clrBackground;
  1752. SHFillRectClr(pnmcd->hdc, &rcClient, _pcmb->_pmbm->_clrBackground);
  1753. dwRet = CDRF_SKIPDEFAULT;
  1754. }
  1755. break;
  1756. }
  1757. return dwRet;
  1758. }
  1759. void CMenuToolbarBase::_PressBtn(int idBtn, BOOL bDown)
  1760. {
  1761. if (!_fVerticalMB)
  1762. {
  1763. DWORD dwState = ToolBar_GetState(_hwndMB, idBtn);
  1764. if (bDown)
  1765. dwState |= TBSTATE_PRESSED;
  1766. else
  1767. dwState &= ~TBSTATE_PRESSED;
  1768. ToolBar_SetState(_hwndMB, idBtn, dwState);
  1769. // Avoid ugly late repaints
  1770. UpdateWindow(_hwndMB);
  1771. }
  1772. }
  1773. /*----------------------------------------------------------
  1774. Purpose: IWinEventHandler::OnWinEvent method
  1775. Processes messages passed on from the menuband.
  1776. */
  1777. STDMETHODIMP CMenuToolbarBase::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres)
  1778. {
  1779. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1780. HRESULT hres = S_FALSE;
  1781. EnterModeless();
  1782. switch (uMsg)
  1783. {
  1784. case WM_SETTINGCHANGE:
  1785. if ((SHIsExplorerIniChange(wParam, lParam) == EICH_UNKNOWN) ||
  1786. (wParam == SPI_SETNONCLIENTMETRICS))
  1787. {
  1788. v_UpdateIconSize(-1, TRUE);
  1789. v_Refresh();
  1790. goto L_WM_SYSCOLORCHANGE;
  1791. }
  1792. break;
  1793. case WM_SYSCOLORCHANGE:
  1794. L_WM_SYSCOLORCHANGE:
  1795. ToolBar_SetInsertMarkColor(_hwndMB, GetSysColor(COLOR_MENUTEXT));
  1796. SendMessage(_hwndMB, uMsg, wParam, lParam);
  1797. InvalidateRect(_hwndMB, NULL, TRUE);
  1798. hres = S_OK;
  1799. break;
  1800. case WM_PALETTECHANGED:
  1801. InvalidateRect( _hwndMB, NULL, FALSE );
  1802. SendMessage( _hwndMB, uMsg, wParam, lParam );
  1803. hres = S_OK;
  1804. break;
  1805. case WM_NOTIFY:
  1806. *plres = _OnNotify((LPNMHDR)lParam);
  1807. hres = S_OK;
  1808. break;
  1809. }
  1810. ExitModeless();
  1811. return hres;
  1812. }
  1813. void CMenuToolbarBase::v_CalcWidth(int* pcxMin, int* pcxMax)
  1814. {
  1815. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1816. ASSERT(IS_VALID_WRITE_PTR(pcxMin, int));
  1817. ASSERT(IS_VALID_WRITE_PTR(pcxMax, int));
  1818. *pcxMin = 0;
  1819. *pcxMax = 0;
  1820. if (_fVerticalMB && _pcmb->_pmbm && _pcmb->_pmbm->_hFontMenu)
  1821. {
  1822. HIMAGELIST himl;
  1823. int cel;
  1824. int cxItemMax = 0;
  1825. HWND hwnd = _hwndMB;
  1826. ASSERT(hwnd);
  1827. HDC hdc = GetDC(hwnd);
  1828. if (hdc)
  1829. {
  1830. HFONT hFontOld = (HFONT) SelectObject(hdc, _pcmb->_pmbm->_hFontMenu);
  1831. if (hFontOld)
  1832. {
  1833. TCHAR sz[MAX_PATH];
  1834. cel = ToolBar_ButtonCount(hwnd);
  1835. // Find the maximum length text
  1836. for(int i = 0; i < cel; i++)
  1837. {
  1838. int idCmd = GetButtonCmd(hwnd, i);
  1839. if (_idCmdChevron != idCmd &&
  1840. !(!_pcmb->_fExpanded && v_GetFlags(idCmd) & SMIF_DEMOTED))
  1841. {
  1842. UINT cch = (UINT)SendMessage(hwnd, TB_GETBUTTONTEXT, idCmd, NULL);
  1843. if (cch != 0 && cch < ARRAYSIZE(sz))
  1844. {
  1845. if (SendMessage(hwnd, TB_GETBUTTONTEXT, idCmd, (LPARAM)sz) > 0)
  1846. {
  1847. RECT rect = {0};
  1848. DWORD dwDTFlags = DT_CALCRECT | DT_SINGLELINE | DT_LEFT | DT_VCENTER;
  1849. if (ShowAmpersand())
  1850. dwDTFlags |= DT_NOPREFIX;
  1851. DrawText(hdc, sz, -1, &rect, dwDTFlags);
  1852. cxItemMax = max(rect.right, cxItemMax);
  1853. }
  1854. }
  1855. }
  1856. }
  1857. SelectObject(hdc, hFontOld);
  1858. }
  1859. ReleaseDC(hwnd, hdc);
  1860. }
  1861. himl = (HIMAGELIST)SendMessage(hwnd, TB_GETIMAGELIST, 0, 0);
  1862. if (himl)
  1863. {
  1864. int cy;
  1865. // Start with the width of the button
  1866. ImageList_GetIconSize(himl, pcxMin, &cy);
  1867. // We want at least a bit of space around the icon
  1868. if (_uIconSizeMB != ISFBVIEWMODE_SMALLICONS)
  1869. {
  1870. // Old FSMenu code took the height of the larger of
  1871. // the icon and text then added 2.
  1872. ToolBar_SetPadding(hwnd, 0, 0);
  1873. *pcxMin += 10;
  1874. }
  1875. else
  1876. {
  1877. // Old FSMenu code took the height of the larger of
  1878. // the icon and text then added cySpacing, which defaults to 6.
  1879. ToolBar_SetPadding(hwnd, 0, 4);
  1880. *pcxMin += 3 * GetSystemMetrics(SM_CXEDGE);
  1881. }
  1882. }
  1883. RECT rect = {0};
  1884. int cxDesired = _pcmb->_pmbm->_cxMargin + cxItemMax + _pcmb->_pmbm->_cxArrow;
  1885. int cxMax = 0;
  1886. if (SystemParametersInfoA(SPI_GETWORKAREA, 0, &rect, 0))
  1887. {
  1888. // We're figuring a third of the screen is a good max width
  1889. cxMax = (rect.right-rect.left) / 3;
  1890. }
  1891. *pcxMin += min(cxDesired, cxMax) + LIST_GAP;
  1892. *pcxMax = *pcxMin;
  1893. }
  1894. TraceMsg(TF_MENUBAND, "CMenuToolbarBase::v_CalcWidth(%d, %d)", *pcxMin, *pcxMax);
  1895. }
  1896. void CMenuToolbarBase::_SetToolbarState()
  1897. {
  1898. SHSetWindowBits(_hwndMB, GWL_STYLE, TBSTYLE_LIST, TBSTYLE_LIST);
  1899. }
  1900. void CMenuToolbarBase::v_ForwardMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  1901. {
  1902. RECT rc;
  1903. POINT pt;
  1904. pt.x = GET_X_LPARAM(lParam);
  1905. pt.y = GET_Y_LPARAM(lParam);
  1906. GetWindowRect(_hwndMB, &rc);
  1907. if (PtInRect(&rc, pt))
  1908. {
  1909. ScreenToClient(_hwndMB, &pt);
  1910. SendMessage(_hwndMB, uMsg, wParam, MAKELONG(pt.x, pt.y));
  1911. }
  1912. }
  1913. void CMenuToolbarBase::NegotiateSize()
  1914. {
  1915. RECT rc;
  1916. GetClientRect(GetParent(_hwndMB), &rc);
  1917. _pcmb->OnPosRectChangeDB(&rc);
  1918. // If we came in here it's because the Menubar did not change sizes or position.
  1919. }
  1920. void CMenuToolbarBase::SetParent(HWND hwndParent)
  1921. {
  1922. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1923. if (hwndParent)
  1924. {
  1925. if (!_hwndMB)
  1926. CreateToolbar(hwndParent);
  1927. }
  1928. else
  1929. {
  1930. // As an optimization, we implement "disowning" ourselves
  1931. // as just moving ourselves offscreen. The previous parent
  1932. // still owns us. The parent is invariably the menusite.
  1933. RECT rc = {-1,-1,-1,-1};
  1934. SetWindowPos(NULL, &rc, 0);
  1935. }
  1936. // We want to set the parent all the time because we don't want to destroy the
  1937. // window with it's parent..... Sizing to -1,-1,-1,-1 causes it not to be displayed.
  1938. if (_hwndMB)
  1939. {
  1940. ::SetParent(_hwndMB, hwndParent);
  1941. SendMessage(_hwndMB, TB_SETPARENT, (WPARAM)hwndParent, NULL);
  1942. }
  1943. }
  1944. void CMenuToolbarBase::v_OnEmptyToolbar()
  1945. {
  1946. ASSERT(_pcmb); // if you hit this assert, you haven't initialized yet.. call SetSite first
  1947. for (int iNumButtons = ToolBar_ButtonCount(_hwndMB) -1;
  1948. iNumButtons >= 0;
  1949. iNumButtons--)
  1950. {
  1951. // HACKHACK (lamadio): For some reason, _fEmptyingToolbar gets set to FALSE.
  1952. // We then Do a TB_DELETEBUTTON, which sends a notify. This does go through on
  1953. // the top level menubands (Start Menu, Browser menu bar), and deletes the
  1954. // associated data. We then try and delete it again.
  1955. // So now, I set null into the sub menu, so that the other code gracefully fails.
  1956. TBBUTTONINFO tbbi;
  1957. tbbi.cbSize = SIZEOF(tbbi);
  1958. tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX;
  1959. ToolBar_GetButtonInfo(_hwndMB, iNumButtons, &tbbi);
  1960. LPVOID pData = (LPVOID)tbbi.lParam;
  1961. tbbi.lParam = NULL;
  1962. ToolBar_SetButtonInfo(_hwndMB, iNumButtons, &tbbi);
  1963. SendMessage(_hwndMB, TB_DELETEBUTTON, iNumButtons, 0);
  1964. v_OnDeleteButton(pData);
  1965. }
  1966. }
  1967. void CMenuToolbarBase::EmptyToolbar()
  1968. {
  1969. if (_hwndMB)
  1970. {
  1971. _fEmptyingToolbar = TRUE;
  1972. v_OnEmptyToolbar();
  1973. _fEmptyingToolbar = FALSE;
  1974. }
  1975. }
  1976. void CMenuToolbarBase::v_Close()
  1977. {
  1978. EmptyToolbar();
  1979. if (_hwndMB)
  1980. {
  1981. //Kill timers to prevent race condition
  1982. KillTimer(_hwndMB, MBTIMER_POPOUT);
  1983. KillTimer(_hwndMB, MBTIMER_DRAGOVER);
  1984. KillTimer(_hwndMB, MBTIMER_EXPAND);
  1985. KillTimer(_hwndMB, MBTIMER_ENDEDIT);
  1986. KillTimer(_hwndMB, MBTIMER_CLOSE);
  1987. KillTimer(_hwndMB, MBTIMER_CLICKUNHANDLE);
  1988. KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);
  1989. DestroyWindow(_hwndMB);
  1990. _hwndMB = NULL;
  1991. }
  1992. }
  1993. void CMenuToolbarBase::Activate(BOOL fActivate)
  1994. {
  1995. if (fActivate == FALSE)
  1996. {
  1997. _fEditMode = FALSE;
  1998. }
  1999. }
  2000. int CMenuToolbarBase::_CalcChevronSize()
  2001. {
  2002. int dSeg;
  2003. int dxy = _pcmb->_pmbm->_cyChevron;
  2004. dxy -= 4;
  2005. dSeg = dxy / 4;
  2006. return dSeg * 4 + 4;
  2007. }
  2008. void CMenuToolbarBase::_DrawChevron(HDC hdc, LPRECT prect, BOOL fFocus, BOOL fSelected)
  2009. {
  2010. RECT rcBox = *prect;
  2011. RECT rcDrop;
  2012. const int dExtra = 3;
  2013. int dxy;
  2014. rcBox.left += dExtra;
  2015. rcBox.right -= dExtra;
  2016. dxy = _CalcChevronSize();
  2017. rcDrop.left = ((rcBox.right + rcBox.left) >> 1) - (dxy/4);
  2018. rcDrop.right = rcDrop.left + dxy - 1;
  2019. int dSeg = ((RECTWIDTH(rcDrop) - 2) >> 2);
  2020. rcDrop.top = (rcBox.top + rcBox.bottom)/2 - (2 * dSeg + 1);
  2021. //rcDrop.bottom = rcBox.top;
  2022. if (fFocus)
  2023. {
  2024. InflateRect(&rcBox, 0, -3);
  2025. SHFillRectClr(hdc, &rcBox, _pcmb->_pmbm->_clrDemoted);
  2026. DrawEdge(hdc, &rcBox, fSelected? BDR_SUNKENINNER : BDR_RAISEDINNER, BF_RECT);
  2027. if (fSelected)
  2028. {
  2029. rcDrop.top += 1;
  2030. rcDrop.left += 1;
  2031. }
  2032. }
  2033. HBRUSH hbrOld = SelectBrush(hdc, _pcmb->_pmbm->_hbrText);
  2034. int y = rcDrop.top + 1;
  2035. int xBase = rcDrop.left+ dSeg;
  2036. for (int x = -dSeg; x <= dSeg; x++)
  2037. {
  2038. PatBlt(hdc, xBase + x, y, 1, dSeg, PATCOPY);
  2039. PatBlt(hdc, xBase + x, y+(dSeg<<1), 1, dSeg, PATCOPY);
  2040. y += (x >= 0) ? -1 : 1;
  2041. }
  2042. SelectBrush(hdc, hbrOld);
  2043. }
  2044. // Takes into accout Separators, hidden and Disabled items
  2045. /*----------------------------------------------------------
  2046. Purpose: This function sets the nearest legal button to be
  2047. the hot item, skipping over any separators, or hidden
  2048. or disabled buttons.
  2049. */
  2050. int CMenuToolbarBase::GetValidHotItem(int iDir, int iIndex, int iCount, DWORD dwFlags)
  2051. {
  2052. if (iIndex == MBSI_LASTITEM)
  2053. {
  2054. // -2 is special value meaning "last item on toolbar"
  2055. int cButtons = (int)SendMessage(_hwndMB, TB_BUTTONCOUNT, 0, 0);
  2056. iIndex = cButtons - 1;
  2057. }
  2058. while ( (iCount == -1 || iIndex < iCount) && iIndex >= 0)
  2059. {
  2060. TBBUTTON tbb;
  2061. // Toolbar will trap out of bounds condition when iCount is -1
  2062. if (!SendMessage(_hwndMB, TB_GETBUTTON, iIndex, (LPARAM)&tbb))
  2063. return -1;
  2064. int idCmd = GetButtonCmd(_hwndMB, iIndex);
  2065. if (tbb.fsState & TBSTATE_ENABLED &&
  2066. !(tbb.fsStyle & TBSTYLE_SEP ||
  2067. tbb.fsState & TBSTATE_HIDDEN) &&
  2068. !(v_GetFlags(idCmd) & SMIF_DEMOTED && !_pcmb->_fExpanded) )
  2069. {
  2070. return iIndex;
  2071. }
  2072. else
  2073. iIndex += iDir;
  2074. }
  2075. return -1;
  2076. }
  2077. BOOL CMenuToolbarBase::SetHotItem(int iDir, int iIndex, int iCount, DWORD dwFlags)
  2078. {
  2079. int iPos = GetValidHotItem(iDir, iIndex, iCount, dwFlags);
  2080. if (iPos >= 0)
  2081. SendMessage(_hwndMB, TB_SETHOTITEM2, iPos, dwFlags);
  2082. return (BOOL)(iPos >= 0);
  2083. }
  2084. static const BYTE g_rgsStateMap[][3] =
  2085. {
  2086. #if defined(FIRST)
  2087. // T, I, F
  2088. { 0, 1, 2}, // State 0
  2089. { 3, 1, 2}, // State 1
  2090. { 4, 1, 2}, // State 2
  2091. { 11, 5, 2}, // State 3
  2092. { 10, 1, 6}, // State 4
  2093. { 7, 1, 2}, // State 5
  2094. { 8, 1, 2}, // State 6
  2095. { 11, 9, 2}, // State 7
  2096. { 10, 1, 10}, // State 8
  2097. { 11, 1, 2}, // State 9
  2098. { 10, 1, 2}, // State 10 // End State
  2099. { 12, 1, 2}, // State 11 // Flash.
  2100. { 10, 1, 2}, // State 12
  2101. #elif defined(SECOND)
  2102. // T, I, F
  2103. { 0, 1, 2}, // State 0
  2104. { 3, 1, 2}, // State 1
  2105. { 4, 1, 2}, // State 2
  2106. { 11, 5, 6}, // State 3
  2107. { 10, 5, 6}, // State 4
  2108. { 7, 5, 6}, // State 5
  2109. { 8, 9, 6}, // State 6
  2110. { 11, 9, 8}, // State 7
  2111. { 10, 9, 10}, // State 8
  2112. { 11, 9, 8}, // State 9
  2113. { 10, 10, 10}, // State 10 // End State
  2114. { 10, 9, 8}, // State 11 // Flash.
  2115. { 10, 9, 8}, // State 12
  2116. { 10, 9, 8}, // State 13
  2117. #elif defined(THIRD)
  2118. // T, I, F
  2119. { 0, 1, 2}, // State 0
  2120. { 3, 1, 2}, // State 1
  2121. { 12, 1, 2}, // State 2
  2122. { 11, 5, 6}, // State 3
  2123. { 10, 5, 6}, // State 4
  2124. { 7, 5, 6}, // State 5
  2125. { 13, 5, 6}, // State 6
  2126. { 11, 9, 8}, // State 7
  2127. { 10, 9, 10}, // State 8
  2128. { 11, 9, 8}, // State 9
  2129. { 10, 10, 10}, // State 10 // End State
  2130. { 10, 9, 8}, // State 11 // Flash.
  2131. { 4, 1, 2}, // State 12
  2132. { 8, 5, 6}, // State 13
  2133. #else
  2134. // T, I, F
  2135. { 0, 1, 2}, // State 0
  2136. { 3, 1, 2}, // State 1
  2137. { 4, 1, 2}, // State 2
  2138. { 11, 5, 6}, // State 3
  2139. { 10, 5, 6}, // State 4
  2140. { 7, 5, 6}, // State 5
  2141. { 8, 5, 6}, // State 6
  2142. { 11, 9, 8}, // State 7
  2143. { 10, 9, 10}, // State 8
  2144. { 11, 9, 8}, // State 9
  2145. { 10, 10, 10}, // State 10 // End State
  2146. { 4, 3, 4}, // State 11 // Flash.
  2147. #endif
  2148. };
  2149. #define MAX_STATE 13
  2150. void CMenuToolbarBase::_FireEvent(BYTE bEvent)
  2151. {
  2152. // We don't want to expand and cover up any dialogs.
  2153. if (_fSuppressUserMonitor)
  2154. return;
  2155. if (!_fHasDemotedItems)
  2156. return;
  2157. if (UEM_RESET == bEvent)
  2158. {
  2159. TraceMsg(TF_MENUBAND, "CMTB::UEM Reset state to 0");
  2160. _pcmb->_pmbState->SetUEMState(0);
  2161. return;
  2162. }
  2163. ASSERT(bEvent >= UEM_TIMEOUT &&
  2164. bEvent <= UEM_HOT_FOLDER);
  2165. BYTE bOldState = _pcmb->_pmbState->GetUEMState();
  2166. BYTE bNewState = g_rgsStateMap[_pcmb->_pmbState->GetUEMState()][bEvent];
  2167. ASSERT(bOldState >= 0 && bOldState <= MAX_STATE);
  2168. TraceMsg(TF_MENUBAND, "*** UEM OldState (%d), New State (%d) ***", bOldState, bNewState);
  2169. _pcmb->_pmbState->SetUEMState(bNewState);
  2170. switch (bNewState)
  2171. {
  2172. case 10: // End State
  2173. TraceMsg(TF_MENUBAND, "*** UEM Entering State 10. Expanding *** ", bOldState, bNewState);
  2174. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  2175. if (_pcmb->_fInSubMenu)
  2176. {
  2177. IUnknown_QueryServiceExec(_pcmb->_pmpSubMenu, SID_SMenuBandChild,
  2178. &CGID_MenuBand, MBANDCID_EXPAND, 0, NULL, NULL);
  2179. }
  2180. else
  2181. {
  2182. Expand(TRUE);
  2183. }
  2184. _pcmb->_pmbState->SetUEMState(0);
  2185. break;
  2186. case 11: // Flash
  2187. // This gets reset when the flash is done...
  2188. TraceMsg(TF_MENUBAND, "*** UEM Entering State 11 Flashing *** ");
  2189. KillTimer(_hwndMB, MBTIMER_UEMTIMEOUT);
  2190. _FlashChevron();
  2191. break;
  2192. }
  2193. }
  2194. void CMenuToolbarBase::_FlashChevron()
  2195. {
  2196. if (_idCmdChevron != -1)
  2197. {
  2198. _cFlashCount = 0;
  2199. ToolBar_MarkButton(_hwndMB, _idCmdChevron, FALSE);
  2200. SetTimer(_hwndMB, MBTIMER_FLASH, MBTIMER_FLASHTIME, NULL);
  2201. }
  2202. }
  2203. LRESULT CMenuToolbarBase::_DefWindowProcMB(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2204. {
  2205. // Are we being asked for the IAccessible for the client?
  2206. if (uMsg == WM_GETOBJECT && (OBJID_CLIENT == (DWORD)lParam))
  2207. {
  2208. // Don't process OBJID_MENU. By the time we get here, we ARE the menu.
  2209. LRESULT lres = 0;
  2210. CAccessible* pacc = new CAccessible(SAFECAST(_pcmb, IMenuBand*));
  2211. if (pacc)
  2212. {
  2213. lres = pacc->InitAcc();
  2214. if (SUCCEEDED((HRESULT)lres))
  2215. {
  2216. lres = LresultFromObject(IID_IAccessible, wParam, SAFECAST(pacc, IAccessible*));
  2217. // The correct OLEAcc has been checked into the NT builds, so Oleacc
  2218. // no longer assumes transfer sematics
  2219. if (FAILED((HRESULT)lres))
  2220. pacc->Release();
  2221. }
  2222. else
  2223. { // Failed to initialize
  2224. pacc->Release();
  2225. }
  2226. }
  2227. return lres;
  2228. }
  2229. return 0;
  2230. }
  2231. void CMenuToolbarBase::v_Show(BOOL fShow, BOOL fForceUpdate)
  2232. {
  2233. // HACKHACK (lamadio): When we create the menubands, we do not set the
  2234. // TOP level band's fonts until a refresh. This code here fixes it.
  2235. if (_fFirstTime && _pcmb->_fTopLevel)
  2236. {
  2237. SetMenuBandMetrics(_pcmb->_pmbm);
  2238. }
  2239. if (fShow)
  2240. {
  2241. SetKeyboardCue();
  2242. _pcmb->_pmbState->PutTipOnTop();
  2243. }
  2244. else
  2245. {
  2246. _fHasDrop = FALSE;
  2247. KillTimer(_hwndMB, MBTIMER_DRAGPOPDOWN);
  2248. KillTimer(_hwndMB, MBTIMER_INFOTIP); // Don't show it if we're not displayed :-)
  2249. _pcmb->_pmbState->HideTooltip(TRUE);
  2250. }
  2251. _fSuppressUserMonitor = FALSE;
  2252. #ifdef UNIX
  2253. if (_fVerticalMB)
  2254. {
  2255. ToolBar_SetHotItem(_hwndMB, 0);
  2256. }
  2257. #endif
  2258. }
  2259. void CMenuToolbarBase::SetKeyboardCue()
  2260. {
  2261. if (_pcmb->_pmbState)
  2262. {
  2263. SendMessage(GetParent(_hwndMB), WM_CHANGEUISTATE,
  2264. MAKEWPARAM(_pcmb->_pmbState->GetKeyboardCue() ? UIS_CLEAR : UIS_SET,
  2265. UISF_HIDEACCEL), 0);
  2266. }
  2267. }