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.

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