MENUBAR.CPP
上传用户:lvjun8202
上传日期:2013-04-30
资源大小:797k
文件大小:22k
源码类别:

SNMP编程

开发平台:

C/C++

  1. ////////////////////////////////////////////////////////////////
  2. // Copyright 1998 Paul DiLascia
  3. // If this code works, it was written by Paul DiLascia.
  4. // If not, I don't know who wrote it.
  5. //
  6. // CMenuBar implements menu bar for MFC. See MenuBar.h for how
  7. // to use, and also the MBTest sample application.
  8. //
  9. #include "StdAfx.h"
  10. #include "MenuBar.h"
  11. #include "ModulVer.h"
  12. #include "Globals.h"
  13. const UINT MB_SET_MENU_NULL = WM_USER + 1100;
  14. #ifdef _DEBUG
  15. #define new DEBUG_NEW
  16. #undef THIS_FILE
  17. static char THIS_FILE[] = __FILE__;
  18. #endif
  19. // if you want to see extra TRACE diagnostics, set CMenuBar::bTRACE = TRUE
  20. BOOL CMenuBar::bTRACE = FALSE;
  21. #ifdef _DEBUG
  22. #define MBTRACEFN
  23. CTraceFn __fooble;
  24. if (CMenuBar::bTRACE)
  25. TRACE
  26. #define MBTRACE
  27. if (CMenuBar::bTRACE)
  28. TRACE
  29. #else
  30. #define MBTRACEFN TRACE
  31. #define MBTRACE   TRACE
  32. #endif
  33. IMPLEMENT_DYNAMIC(CMenuBar, COAMToolBar)
  34. BEGIN_MESSAGE_MAP(CMenuBar, COAMToolBar)
  35. //{{AFX_MSG_MAP(CMenuBar)
  36. ON_WM_CREATE()
  37. ON_WM_LBUTTONDOWN()
  38. ON_WM_MOUSEMOVE()
  39. ON_WM_SIZE()
  40. //}}AFX_MSG_MAP
  41. ON_UPDATE_COMMAND_UI_RANGE(0, 256, OnUpdateMenuButton)
  42. ON_MESSAGE(MB_SET_MENU_NULL, OnSetMenuNull)
  43. END_MESSAGE_MAP()
  44. static int GetVerComCtl32()
  45. {
  46. CModuleVersion ver;
  47. DLLVERSIONINFO dvi;
  48. VERIFY(ver.DllGetVersion(_T("comctl32.dll"), dvi));
  49. return dvi.dwMajorVersion*100 + dvi.dwMinorVersion;
  50. //return 1;
  51. }
  52. CMenuBar::CMenuBar()
  53. {
  54. // if (GetVerComCtl32() <= 470)
  55. // AfxMessageBox(_T("Warning: This program requires comctl32.dll version 4.71 or greater."));
  56. m_iTrackingState = TRACK_NONE;  // initial state: not tracking 
  57. m_iPopupTracking = m_iNewPopup = -1; // invalid
  58. m_hmenu = NULL;
  59. m_bAutoRemoveFrameMenu = TRUE;  // set frame's menu to NULL
  60. }
  61. CMenuBar::~CMenuBar()
  62. {
  63. }
  64. //////////////////
  65. // Menu bar was created: install hook into owner window
  66. //
  67. int CMenuBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
  68. {
  69. if (COAMToolBar::OnCreate(lpCreateStruct)==-1)
  70. return -1;
  71. UpdateFont();
  72. CWnd* pFrame = GetOwner();
  73. //ASSERT_VALID(pFrame);
  74. m_frameHook.Install(this, *pFrame);
  75. return 0; // OK
  76. }
  77. //////////////////
  78. // Set menu bar font from current system menu font
  79. //
  80. void CMenuBar::UpdateFont()
  81. {
  82. static CFont font;
  83. NONCLIENTMETRICS info;
  84. info.cbSize = sizeof(info);
  85. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
  86. if ((HFONT)font)
  87. font.DeleteObject();
  88. VERIFY(font.CreateFontIndirect(&info.lfMenuFont));
  89. SetFont(&font);
  90. }
  91. //////////////////
  92. // The reason for having this is so MFC won't automatically disable
  93. // the menu buttons. Assumes < 256 top-level menu items. The ID of
  94. // the ith menu button is i. IOW, the index and ID are the same.
  95. //
  96. void CMenuBar::OnUpdateMenuButton(CCmdUI* pCmdUI)
  97. {
  98. //ASSERT_VALID(this);
  99. if (IsValidButton(pCmdUI->m_nID))
  100. pCmdUI->Enable(TRUE);
  101. }
  102. //////////////////
  103. // Recompute layout of menu bar
  104. //
  105. void CMenuBar::RecomputeMenuLayout()
  106. {
  107. SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
  108. SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
  109. }
  110. //////////////////
  111. // Make frame recalculate control bar sizes after menu change
  112. //
  113. void CMenuBar::RecomputeToolbarSize()
  114. {
  115. // Force toolbar to recompute size
  116. CFrameWnd* pFrame = (CFrameWnd*)GetOwner();
  117. //ASSERT_VALID(pFrame);
  118. //SERT(pFrame->IsFrameWnd());
  119. pFrame->RecalcLayout();
  120. // floating frame
  121. pFrame = GetParentFrame();
  122. if (pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)))
  123. pFrame->RecalcLayout();
  124. }
  125. //////////////////
  126. // Set tracking state: none, button, or popup
  127. //
  128. void CMenuBar::SetTrackingState(TRACKINGSTATE iState, int iButton)
  129. {
  130. //SERT_VALID(this);
  131. if (iState != m_iTrackingState) {
  132. if (iState == TRACK_NONE)
  133. iButton = -1;
  134. #ifdef _DEBUG
  135. static LPCTSTR StateName[] = { _T("NONE"), _T("BUTTON"), _T("POPUP") };
  136. MBTRACE(_T("CMenuBar::SetTrackingState to %s, button=%dn"),
  137. StateName[iState], iButton);
  138. #endif
  139. GetToolBarCtrl().SetHotItem(iButton);  // could be none (-1)
  140. if (iState==TRACK_POPUP) {
  141. // set related state stuff
  142. m_bEscapeWasPressed = FALSE;  // assume Esc key not pressed
  143. m_bProcessRightArrow =  // assume left/right arrow..
  144. m_bProcessLeftArrow = TRUE; // ..will move to prev/next popup
  145. m_iPopupTracking = iButton;  // which popup I'm tracking
  146. }
  147. m_iTrackingState = iState;
  148. }
  149. }
  150. //////////////////
  151. // Toggle state from home state to button-tracking and back
  152. //
  153. void CMenuBar::ToggleTrackButtonMode()
  154. {
  155. //SERT_VALID(this);
  156. if (m_iTrackingState == TRACK_NONE || m_iTrackingState == TRACK_BUTTON) {
  157. SetTrackingState(m_iTrackingState == TRACK_NONE ?
  158. TRACK_BUTTON : TRACK_NONE, 0);
  159. }
  160. }
  161. //////////////////
  162. // Get button index before/after a given button
  163. //
  164. int CMenuBar::GetNextOrPrevButton(int iButton, BOOL bPrev)
  165. {
  166. //SERT_VALID(this);
  167. if (bPrev) {
  168. iButton--;
  169. if (iButton <0)
  170. iButton = GetToolBarCtrl().GetButtonCount() - 1;
  171. } else {
  172. iButton++;
  173. if (iButton >= GetToolBarCtrl().GetButtonCount())
  174. iButton = 0;
  175. }
  176. return iButton;
  177. }
  178. /////////////////
  179. // This is to correct a bug in the system toolbar control: TB_HITTEST only
  180. // looks at the buttons, not the size of the window. So it returns a button
  181. // hit even if that button is totally outside the size of the window!
  182. //
  183. int CMenuBar::HitTest(CPoint p) const
  184. {
  185. int iHit = GetToolBarCtrl().HitTest(&p);
  186. if (iHit>0) {
  187. CRect rc;
  188. GetClientRect(&rc);
  189. if (!rc.PtInRect(p)) // if point is outside window
  190. iHit = -1; // can't be a hit!
  191. }
  192. return iHit;
  193. }
  194. //////////////////
  195. // Load a different menu. The HMENU must not belong to any CMenu,
  196. // and you must free it when you're done. Returns old menu.
  197. //
  198. HMENU CMenuBar::LoadMenu(HMENU hmenu)
  199. {
  200. MBTRACEFN(_T("CMenuBar::LoadMenun"));
  201. UINT iPrevID=(UINT)-1;
  202. //ASSERT(::IsMenu(hmenu));
  203. //ASSERT_VALID(this);
  204. if (m_bAutoRemoveFrameMenu) {
  205. CFrameWnd* pFrame = GetParentFrame();
  206. if (::GetMenu(*pFrame)!=NULL) {
  207. // I would like to set the frame's menu to NULL now, but if I do, MFC
  208. // gets all upset: it calls GetMenu and expects to have a real menu.
  209. // So Instead, I post a message to myself. Because the message is
  210. // posted, not sent, I won't process it until MFC is done with all its
  211. // initialization stuff. (MFC needs to set CFrameWnd::m_hMenuDefault
  212. // to the menu, which it gets by calling GetMenu.)
  213. //
  214. PostMessage(MB_SET_MENU_NULL, (WPARAM)pFrame->GetSafeHwnd());
  215. }
  216. }
  217. HMENU hOldMenu = m_hmenu;
  218. m_hmenu = hmenu;
  219. // delete existing buttons
  220. int nCount = GetToolBarCtrl().GetButtonCount();
  221. while (nCount--) {
  222. VERIFY(GetToolBarCtrl().DeleteButton(0));
  223. }
  224. GetToolBarCtrl().SetImageList(NULL);
  225. // SetButtonSize(CSize(0,0)); // This barfs in VC 6.0
  226. DWORD dwStyle = GetStyle();
  227. BOOL bModifyStyle = ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT);
  228. // add text buttons
  229. UINT nMenuItems = hmenu ? ::GetMenuItemCount(hmenu) : 0;
  230. for (UINT i=0; i < nMenuItems; i++) {
  231. TCHAR name[64];
  232. memset(name, 0, sizeof(name)); // guarantees double-0 at end
  233. if (::GetMenuString(hmenu, i, name, countof(name)-1, MF_BYPOSITION)) {
  234. TBBUTTON tbb;
  235. memset(&tbb, 0, sizeof(tbb));
  236. tbb.idCommand = ::GetMenuItemID(hmenu, i);
  237. // Because the toolbar is too brain-damaged to know if it already has
  238. // a string, and is also too brain-dead to even let you delete strings,
  239. // I have to determine if each string has been added already. Otherwise
  240. // in a MDI app, as the menus are repeatedly switched between doc and
  241. // no-doc menus, I will keep adding strings until somebody runs out of
  242. // memory. Sheesh!
  243. // 
  244. int iString = -1;
  245. for (int j=0; j<m_arStrings.GetSize(); j++) {
  246. if (m_arStrings[j] == name) {
  247. iString = j; // found it
  248. break;
  249. }
  250. }
  251. if (iString <0) {
  252. // string not found: add it
  253. iString = GetToolBarCtrl().AddStrings(name);
  254. m_arStrings.SetAtGrow(iString, name);
  255. }
  256. tbb.iString = iString;
  257. tbb.fsState = TBSTATE_ENABLED;
  258. tbb.fsStyle = TBSTYLE_AUTOSIZE;
  259. tbb.iBitmap = -1;
  260. tbb.idCommand = i;
  261. VERIFY(GetToolBarCtrl().AddButtons(1, &tbb));
  262. }
  263. }
  264. if (bModifyStyle)
  265. SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
  266. if (hmenu) {
  267. GetToolBarCtrl().AutoSize();  // size buttons
  268. RecomputeToolbarSize();  // and menubar itself
  269. }
  270. return hOldMenu;
  271. }
  272. //////////////////
  273. // Load menu from resource
  274. //
  275. HMENU CMenuBar::LoadMenu(LPCSTR lpszMenuName)
  276. {
  277. return LoadMenu(::LoadMenu(AfxGetResourceHandle(), lpszMenuName));
  278. }
  279. //////////////////
  280. // Set the frame's menu to NULL. WPARAM is HWND of frame.
  281. //
  282. LRESULT CMenuBar::OnSetMenuNull(WPARAM wp, LPARAM lp)
  283. {
  284. HWND hwnd = (HWND)wp;
  285. //ASSERT(::IsWindow(hwnd));
  286. ::SetMenu(hwnd, NULL);
  287. return 0;
  288. }
  289. //////////////////
  290. // Handle mouse click: if clicked on button, press it
  291. // and go into main menu loop.
  292. //
  293. void CMenuBar::OnLButtonDown(UINT nFlags, CPoint pt)
  294. {
  295. MBTRACEFN(_T("CMenuBar::OnLButtonDownn"));
  296. //ASSERT_VALID(this);
  297. int iButton = HitTest(pt);
  298. if (iButton >= 0 && iButton<GetToolBarCtrl().GetButtonCount()) // if mouse is over a button:
  299. TrackPopup(iButton);  //   track it
  300. else  // otherwise:
  301. COAMToolBar::OnLButtonDown(nFlags, pt);  //   pass it on...
  302. }
  303. //////////////////
  304. // Handle mouse movement
  305. //
  306. void CMenuBar::OnMouseMove(UINT nFlags, CPoint pt)
  307. {
  308. //ASSERT_VALID(this);
  309. if (m_iTrackingState==TRACK_BUTTON) {
  310. // In button-tracking state, ignore mouse-over to non-button area.
  311. // Normally, the toolbar would de-select the hot item in this case.
  312. // 
  313. // Only change the hot item if the mouse has actually moved.
  314. // This is necessary to avoid a bug where the user moves to a different
  315. // button from the one the mouse is over, and presses arrow-down to get
  316. // the menu, then Esc to cancel it. Without this code, the button will
  317. // jump to wherever the mouse is--not right.
  318. int iHot = HitTest(pt);
  319. if (IsValidButton(iHot) && pt != m_ptMouse)
  320. GetToolBarCtrl().SetHotItem(iHot);
  321. return;  // don't let toolbar get it
  322. }
  323. m_ptMouse = pt; // remember point
  324. COAMToolBar::OnMouseMove(nFlags, pt);
  325. }
  326. //////////////////
  327. // Window was resized: need to recompute layout
  328. //
  329. void CMenuBar::OnSize(UINT nType, int cx, int cy)
  330. {
  331. COAMToolBar::OnSize(nType, cx, cy);
  332. RecomputeMenuLayout();
  333. }
  334. //////////////////
  335. // Bar style changed: eg, moved from left to right dock or floating
  336. //
  337. void CMenuBar::OnBarStyleChange(DWORD dwOldStyle, DWORD dwNewStyle)
  338. {
  339. COAMToolBar::OnBarStyleChange(dwOldStyle, dwNewStyle);
  340. RecomputeMenuLayout();
  341. }
  342. /////////////////
  343. // When user selects a new menu item, note whether it has a submenu
  344. // and/or parent menu, so I know whether right/left arrow should
  345. // move to the next popup.
  346. //
  347. void CMenuBar::OnMenuSelect(HMENU hmenu, UINT iItem)
  348. {
  349. if (m_iTrackingState > 0) {
  350. // process right-arrow iff item is NOT a submenu
  351. m_bProcessRightArrow = (::GetSubMenu(hmenu, iItem) == NULL);
  352. // process left-arrow iff curent menu is one I'm tracking
  353. m_bProcessLeftArrow = hmenu==m_hMenuTracking;
  354. }
  355. }
  356. // globals--yuk! But no other way using windows hooks.
  357. //
  358. static CMenuBar* g_pMenuBar = NULL;
  359. static HHOOK g_hMsgHook = NULL;
  360. ////////////////
  361. // Menu filter hook just passes to virtual CMenuBar function
  362. //
  363. LRESULT CALLBACK
  364. CMenuBar::MenuInputFilter(int code, WPARAM wp, LPARAM lp)
  365. {
  366. return (code==MSGF_MENU && g_pMenuBar &&
  367. g_pMenuBar->OnMenuInput(*((MSG*)lp))) ? TRUE
  368. : CallNextHookEx(g_hMsgHook, code, wp, lp);
  369. }
  370. //////////////////
  371. // Handle menu input event: Look for left/right to change popup menu,
  372. // mouse movement over over a different menu button for "hot" popup effect.
  373. // Returns TRUE if message handled (to eat it).
  374. //
  375. BOOL CMenuBar::OnMenuInput(MSG& m)
  376. {
  377. //ASSERT_VALID(this);
  378. //ASSERT(m_iTrackingState == TRACK_POPUP); // sanity check
  379. int msg = m.message;
  380. if (msg==WM_KEYDOWN) {
  381. // handle left/right-arow.
  382. TCHAR vkey = m.wParam;
  383. if ((vkey == VK_LEFT  && m_bProcessLeftArrow) ||
  384. (vkey == VK_RIGHT && m_bProcessRightArrow)) {
  385. MBTRACE(_T("CMenuBar::OnMenuInput: handle VK_LEFT/RIGHTn"));
  386. CancelMenuAndTrackNewOne(
  387. GetNextOrPrevButton(m_iPopupTracking, vkey==VK_LEFT));
  388. return TRUE; // eat it
  389. } else if (vkey == VK_ESCAPE) {
  390. m_bEscapeWasPressed = TRUE;  // (menu will abort itself)
  391. }
  392. } else if (msg==WM_MOUSEMOVE || msg==WM_LBUTTONDOWN) {
  393. // handle mouse move or click
  394. CPoint pt = m.lParam;
  395. ScreenToClient(&pt);
  396. if (msg == WM_MOUSEMOVE) {
  397. if (pt != m_ptMouse) {
  398. int iButton = HitTest(pt);
  399. if (IsValidButton(iButton) && iButton != m_iPopupTracking) {
  400. // user moved mouse over a different button: track its popup
  401. CancelMenuAndTrackNewOne(iButton);
  402. }
  403. m_ptMouse = pt;
  404. }
  405. } else if (msg == WM_LBUTTONDOWN) {
  406. if (HitTest(pt) == m_iPopupTracking) {
  407. // user clicked on same button I am tracking: cancel menu
  408. MBTRACE(_T("CMenuBar:OnMenuInput: handle mouse click to exit popupn"));
  409. CancelMenuAndTrackNewOne(-1);
  410. return TRUE; // eat it
  411. }
  412. }
  413. }
  414. return FALSE; // not handled
  415. }
  416. //////////////////
  417. // Cancel the current popup menu by posting WM_CANCELMODE, and track a new
  418. // menu. iNewPopup is which new popup to track (-1 to quit).
  419. //
  420. void CMenuBar::CancelMenuAndTrackNewOne(int iNewPopup)
  421. {
  422. MBTRACE(_T("CMenuBar::CancelMenuAndTrackNewOne: %dn"), iNewPopup);
  423. //ASSERT_VALID(this);
  424. if (iNewPopup != m_iPopupTracking) {
  425. GetOwner()->PostMessage(WM_CANCELMODE); // quit menu loop
  426. m_iNewPopup = iNewPopup;  // go to this popup (-1 = quit)
  427. }
  428. }
  429. //////////////////
  430. // Track the popup submenu associated with the i'th button in the menu bar.
  431. // This fn actually goes into a loop, tracking different menus until the user
  432. // selects a command or exits the menu.
  433. //
  434. void CMenuBar::TrackPopup(int iButton)
  435. {
  436. MBTRACE(_T("CMenuBar::TrackPopup %dn"), iButton);
  437. //ASSERT_VALID(this);
  438. //ASSERT(m_hmenu);
  439. CMenu menu;
  440. menu.Attach(m_hmenu);
  441. int nMenuItems = menu.GetMenuItemCount();
  442. while (iButton >= 0) {  // while user selects another menu
  443. m_iNewPopup = -1;  // assume quit after this
  444. GetToolBarCtrl().PressButton(iButton, TRUE);  // press the button
  445. UpdateWindow();  // and force repaint now
  446. // post a simulated arrow-down into the message stream
  447. // so TrackPopupMenu will read it and move to the first item
  448. GetOwner()->PostMessage(WM_KEYDOWN, VK_DOWN, 1);
  449. GetOwner()->PostMessage(WM_KEYUP, VK_DOWN, 1);
  450. SetTrackingState(TRACK_POPUP, iButton); // enter tracking state
  451. // Need to install a hook to trap menu input in order to make
  452. // left/right-arrow keys and "hot" mouse tracking work.
  453. //
  454. //ASSERT(g_pMenuBar == NULL);
  455. g_pMenuBar = this;
  456. //ASSERT(g_hMsgHook == NULL);
  457. g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER,
  458. MenuInputFilter, NULL, ::GetCurrentThreadId());
  459. // get submenu and display it beneath button
  460. TPMPARAMS tpm;
  461. CRect rcButton;
  462. GetToolBarCtrl().GetRect(iButton, rcButton);
  463. ClientToScreen(&rcButton);
  464. CPoint pt = ComputeMenuTrackPoint(rcButton, tpm);
  465. HMENU hMenuPopup = ::GetSubMenu(m_hmenu, iButton);
  466. //ASSERT(hMenuPopup);
  467. BOOL bRet = TrackPopupMenuEx(hMenuPopup,
  468. TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,
  469. pt.x, pt.y, GetOwner()->GetSafeHwnd(), &tpm);
  470. // uninstall hook.
  471. ::UnhookWindowsHookEx(g_hMsgHook);
  472. g_hMsgHook = NULL;
  473. g_pMenuBar = NULL;
  474. GetToolBarCtrl().PressButton(iButton, FALSE);  // un-press button
  475. UpdateWindow();  // and force repaint now
  476. // If the user exited the menu loop by pressing Escape,
  477. // return to track-button state; otherwise normal non-tracking state.
  478. SetTrackingState(m_bEscapeWasPressed ?
  479. TRACK_BUTTON : TRACK_NONE, iButton);
  480. // If the user moved mouse to a new top-level popup (eg from File to
  481. // Edit button), I will have posted a WM_CANCELMODE to quit
  482. // the first popup, and set m_iNewPopup to the new menu to show.
  483. // Otherwise, m_iNewPopup will be -1 as set above.
  484. // So just set iButton to the next popup menu and keep looping...
  485. iButton = m_iNewPopup;
  486. }
  487. menu.Detach();
  488. }
  489. //////////////////
  490. // Given button rectangle, compute point and "exclude rect" for
  491. // TrackPopupMenu, based on current docking style, so that the menu will
  492. // appear always inside the window.
  493. //
  494. CPoint CMenuBar::ComputeMenuTrackPoint(const CRect& rcButn, TPMPARAMS& tpm)
  495. {
  496. tpm.cbSize = sizeof(tpm);
  497. DWORD dwStyle = m_dwStyle;
  498. CPoint pt;
  499. CRect& rcExclude = (CRect&)tpm.rcExclude;
  500. rcExclude = rcButn;
  501. ::GetWindowRect(::GetDesktopWindow(), &rcExclude);
  502. switch (dwStyle & CBRS_ALIGN_ANY) {
  503. case CBRS_ALIGN_BOTTOM:
  504. pt = CPoint(rcButn.left, rcButn.top);
  505. rcExclude.top = rcButn.top;
  506. break;
  507. case CBRS_ALIGN_LEFT:
  508. pt = CPoint(rcButn.right, rcButn.top);
  509. rcExclude.right = rcButn.right;
  510. break;
  511. case CBRS_ALIGN_RIGHT:
  512. pt = CPoint(rcButn.left, rcButn.top);
  513. rcExclude.left = rcButn.left;
  514. break;
  515. default: // case CBRS_ALIGN_TOP:
  516. pt = CPoint(rcButn.left, rcButn.bottom);
  517. break;
  518. }
  519. return pt;
  520. }
  521. //////////////////
  522. // This function translates special menu keys and mouse actions.
  523. // You must call it from your frame's PreTranslateMessage.
  524. //
  525. BOOL CMenuBar::TranslateFrameMessage(MSG* pMsg)
  526. {
  527. //ASSERT_VALID(this);
  528. //ASSERT(pMsg);
  529. UINT msg = pMsg->message;
  530. if (WM_LBUTTONDOWN <= msg && msg <= WM_MOUSELAST) {
  531. if (pMsg->hwnd != m_hWnd && m_iTrackingState > 0) {
  532. // user clicked outside menu bar: exit tracking mode
  533. MBTRACE(_T("CMenuBar::TranslateFrameMessage: user clicked outside menu bar: end trackingn"));
  534. SetTrackingState(TRACK_NONE);
  535. }
  536. } else if (msg==WM_SYSKEYDOWN || msg==WM_SYSKEYUP || msg==WM_KEYDOWN) {
  537. BOOL bAlt = HIWORD(pMsg->lParam) & KF_ALTDOWN; // Alt key down
  538. TCHAR vkey = pMsg->wParam;   // get virt key
  539. if (vkey==VK_MENU ||
  540. (vkey==VK_F10 && !((GetKeyState(VK_SHIFT) & 0x80000000) ||
  541.                    (GetKeyState(VK_CONTROL) & 0x80000000) || bAlt))) {
  542. // key is VK_MENU or F10 with no alt/ctrl/shift: toggle menu mode
  543. if (msg==WM_SYSKEYUP) {
  544. MBTRACE(_T("CMenuBar::TranslateFrameMessage: handle menu keyn"));
  545. ToggleTrackButtonMode();
  546. }
  547. return TRUE;
  548. } else if ((msg==WM_SYSKEYDOWN || msg==WM_KEYDOWN)) {
  549. if (m_iTrackingState == TRACK_BUTTON) {
  550. // I am tracking: handle left/right/up/down/space/Esc
  551. switch (vkey) {
  552. case VK_LEFT:
  553. case VK_RIGHT:
  554. // left or right-arrow: change hot button if tracking buttons
  555. MBTRACE(_T("CMenuBar::TranslateFrameMessage: VK_LEFT/RIGHTn"));
  556. GetToolBarCtrl().SetHotItem(GetNextOrPrevButton(GetToolBarCtrl().GetHotItem(), vkey==VK_LEFT));
  557. return TRUE;
  558. case VK_SPACE:  // (personally, I like SPACE to enter menu too)
  559. case VK_UP:
  560. case VK_DOWN:
  561. // up or down-arrow: move into current menu, if any
  562. MBTRACE(_T("CMenuBar::TranslateFrameMessage: VK_UP/DOWN/SPACEn"));
  563. TrackPopup(GetToolBarCtrl().GetHotItem());
  564. return TRUE;
  565. case VK_ESCAPE:
  566. // escape key: exit tracking mode
  567. MBTRACE(_T("CMenuBar::TranslateFrameMessage: VK_ESCAPEn"));
  568. SetTrackingState(TRACK_NONE);
  569. return TRUE;
  570. }
  571. }
  572. // Handle alphanumeric key: invoke menu. Note that Alt-X
  573. // chars come through as WM_SYSKEYDOWN, plain X as WM_KEYDOWN.
  574. if ((bAlt || m_iTrackingState == TRACK_BUTTON) && isalnum(vkey)) {
  575. // Alt-X, or else X while in tracking mode
  576. UINT nID;
  577. if (GetToolBarCtrl().MapAccelerator(vkey, &nID)) {
  578. MBTRACE(_T("CMenuBar::TranslateFrameMessage: map accleratorn"));
  579. TrackPopup(nID);  // found menu mnemonic: track it
  580. return TRUE;  // handled
  581. } else if (m_iTrackingState==TRACK_BUTTON && !bAlt) {
  582. MessageBeep(0);
  583. return TRUE;
  584. }
  585. }
  586. // Default for any key not handled so far: return to no-menu state
  587. if (m_iTrackingState > 0) {
  588. MBTRACE(_T("CMenuBar::TranslateFrameMessage: unknown key, stop trackingn"));
  589. SetTrackingState(TRACK_NONE);
  590. }
  591. }
  592. }
  593. return FALSE; // not handled, pass along
  594. }
  595. #ifdef _DEBUG
  596. void CMenuBar::AssertValid() const
  597. {
  598. COAMToolBar::AssertValid();
  599. //ASSERT(m_hmenu==NULL || ::IsMenu(m_hmenu));
  600. //ASSERT(TRACK_NONE<=m_iTrackingState && m_iTrackingState<=TRACK_POPUP);
  601. m_frameHook.AssertValid();
  602. }
  603. void CMenuBar::Dump(CDumpContext& dc) const
  604. {
  605. COAMToolBar::Dump(dc);
  606. }
  607. #endif
  608. //////////////////////////////////////////////////////////////////
  609. // CMenuBarFrameHook is used to trap menu-related messages sent to the owning
  610. // frame. The same class is also used to trap messages sent to the MDI client
  611. // window in an MDI app. I should really use two classes for this,
  612. // but it uses less code to chare the same class. Note however: there
  613. // are two different INSTANCES of CMenuBarFrameHook in CMenuBar: one for
  614. // the frame and one for the MDI client window.
  615. //
  616. CMenuBarFrameHook::CMenuBarFrameHook()
  617. {
  618. }
  619. CMenuBarFrameHook::~CMenuBarFrameHook()
  620. {
  621. HookWindow((HWND)NULL); // (unhook)
  622. }
  623. //////////////////
  624. // Install hook to trap window messages sent to frame or MDI client.
  625. // 
  626. BOOL CMenuBarFrameHook::Install(CMenuBar* pMenuBar, HWND hWndToHook)
  627. {
  628. //ASSERT_VALID(pMenuBar);
  629. m_pMenuBar = pMenuBar;
  630. return HookWindow(hWndToHook);
  631. }
  632. //////////////////////////////////////////////////////////////////
  633. // Trap frame/MDI client messages specific to menubar. 
  634. //
  635. LRESULT CMenuBarFrameHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  636. {
  637. CMenuBar& mb = *m_pMenuBar;
  638. switch (msg) {
  639. // The following messages are trapped for the frame window
  640. case WM_SYSCOLORCHANGE:
  641. mb.UpdateFont();
  642. break;
  643. case WM_MENUSELECT:
  644. mb.OnMenuSelect((HMENU)lp, (UINT)LOWORD(wp));
  645. break;
  646. }
  647. return CSubclassWnd::WindowProc(msg, wp, lp);
  648. }