Source code of Windows XP (NT5)
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.

2940 lines
88 KiB

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