⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 menubar.cpp

📁 对c++类库cj60的简化包装
💻 CPP
📖 第 1 页 / 共 2 页
字号:

//////////////////
// Bar style changed: eg, moved from left to right dock or floating
//
void CMenuBar::OnBarStyleChange(DWORD dwOldStyle, DWORD dwNewStyle)
{
	CCJToolBar::OnBarStyleChange(dwOldStyle, dwNewStyle);
	RecomputeMenuLayout();
}

/////////////////
// When user selects a new menu item, note whether it has a submenu
// and/or parent menu, so I know whether right/left arrow should
// move to the next popup.
//
void CMenuBar::OnMenuSelect(HMENU hmenu, UINT iItem)
{
	if (m_iTrackingState > 0) {
		// process right-arrow iff item is NOT a submenu
		m_bProcessRightArrow = (::GetSubMenu(hmenu, iItem) == NULL);
		// process left-arrow iff curent menu is one I'm tracking
		m_bProcessLeftArrow = hmenu==m_hMenuTracking;
	}
}

// globals--yuk! But no other way using windows hooks.
//
static CMenuBar*	g_pMenuBar = NULL;
static HHOOK		g_hMsgHook = NULL;

////////////////
// Menu filter hook just passes to virtual CMenuBar function
//
LRESULT CALLBACK
CMenuBar::MenuInputFilter(int code, WPARAM wp, LPARAM lp)
{
	return (code==MSGF_MENU && g_pMenuBar &&
		g_pMenuBar->OnMenuInput(*((MSG*)lp))) ? TRUE
		: CallNextHookEx(g_hMsgHook, code, wp, lp);
}

//////////////////
// Handle menu input event: Look for left/right to change popup menu,
// mouse movement over over a different menu button for "hot" popup effect.
// Returns TRUE if message handled (to eat it).
//
BOOL CMenuBar::OnMenuInput(MSG& m)
{
	ASSERT_VALID(this);
	ASSERT(m_iTrackingState == TRACK_POPUP); // sanity check
	int msg = m.message;

	if (msg==WM_KEYDOWN) {
		// handle left/right-arow.
		TCHAR vkey = m.wParam;
		if ((vkey == VK_LEFT  && m_bProcessLeftArrow) ||
			(vkey == VK_RIGHT && m_bProcessRightArrow)) {

			MBTRACE(_T("CMenuBar::OnMenuInput: handle VK_LEFT/RIGHT\n"));
			CancelMenuAndTrackNewOne(
				GetNextOrPrevButton(m_iPopupTracking, vkey==VK_LEFT));
			return TRUE; // eat it

		} else if (vkey == VK_ESCAPE) {
			m_bEscapeWasPressed = TRUE;	 // (menu will abort itself)
		}

	} else if (msg==WM_MOUSEMOVE || msg==WM_LBUTTONDOWN) {
		// handle mouse move or click
		CPoint pt = m.lParam;
		ScreenToClient(&pt);

		if (msg == WM_MOUSEMOVE) {
			if (pt != m_ptMouse) {
				int iButton = HitTest(pt);
				if (IsValidButton(iButton) && iButton != m_iPopupTracking) {
					// user moved mouse over a different button: track its popup
					CancelMenuAndTrackNewOne(iButton);
				}
				m_ptMouse = pt;
			}

		} else if (msg == WM_LBUTTONDOWN) {
			if (HitTest(pt) == m_iPopupTracking) {
				// user clicked on same button I am tracking: cancel menu
				MBTRACE(_T("CMenuBar:OnMenuInput: handle mouse click to exit popup\n"));
				CancelMenuAndTrackNewOne(-1);
				return TRUE; // eat it
			}
		}
	}
	return FALSE; // not handled
}

//////////////////
// Cancel the current popup menu by posting WM_CANCELMODE, and track a new
// menu. iNewPopup is which new popup to track (-1 to quit).
//
void CMenuBar::CancelMenuAndTrackNewOne(int iNewPopup)
{
	MBTRACE(_T("CMenuBar::CancelMenuAndTrackNewOne: %d\n"), iNewPopup);
	ASSERT_VALID(this);
	if (iNewPopup != m_iPopupTracking) {
		GetOwner()->PostMessage(WM_CANCELMODE); // quit menu loop
		m_iNewPopup = iNewPopup;					 // go to this popup (-1 = quit)
	}
}

//////////////////
// Track the popup submenu associated with the i'th button in the menu bar.
// This fn actually goes into a loop, tracking different menus until the user
// selects a command or exits the menu.
//
void CMenuBar::TrackPopup(int iButton)
{
	MBTRACE(_T("CMenuBar::TrackPopup %d\n"), iButton);
	ASSERT_VALID(this);
	ASSERT(m_hmenu);

	CMenu menu;
	menu.Attach(m_hmenu);
	int nMenuItems = menu.GetMenuItemCount();

	while (iButton >= 0) {					 // while user selects another menu

		m_iNewPopup = -1;						 // assume quit after this
		PressButton(iButton, TRUE);		 // press the button
		UpdateWindow();						 // and force repaint now

		// post a simulated arrow-down into the message stream
		// so TrackPopupMenu will read it and move to the first item
		GetOwner()->PostMessage(WM_KEYDOWN, VK_DOWN, 1);
		GetOwner()->PostMessage(WM_KEYUP, VK_DOWN, 1);

		SetTrackingState(TRACK_POPUP, iButton); // enter tracking state

		// Need to install a hook to trap menu input in order to make
		// left/right-arrow keys and "hot" mouse tracking work.
		//
		ASSERT(g_pMenuBar == NULL);
		g_pMenuBar = this;
		ASSERT(g_hMsgHook == NULL);
		g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER,
			MenuInputFilter, NULL, ::GetCurrentThreadId());

		// get submenu and display it beneath button
		TPMPARAMS tpm;
		CRect rcButton;
		GetRect(iButton, rcButton);
		ClientToScreen(&rcButton);
		CPoint pt = ComputeMenuTrackPoint(rcButton, tpm);
		HMENU hMenuPopup = ::GetSubMenu(m_hmenu, iButton);
		ASSERT(hMenuPopup);
		BOOL bRet = TrackPopupMenuEx(hMenuPopup,
			TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,
			pt.x, pt.y, GetOwner()->GetSafeHwnd(), &tpm);

		// uninstall hook.
		::UnhookWindowsHookEx(g_hMsgHook);
		g_hMsgHook = NULL;
		g_pMenuBar = NULL;

		PressButton(iButton, FALSE);	 // un-press button
		UpdateWindow();					 // and force repaint now

		// If the user exited the menu loop by pressing Escape,
		// return to track-button state; otherwise normal non-tracking state.
		SetTrackingState(m_bEscapeWasPressed ?
			TRACK_BUTTON : TRACK_NONE, iButton);

		// If the user moved mouse to a new top-level popup (eg from File to
		// Edit button), I will have posted a WM_CANCELMODE to quit
		// the first popup, and set m_iNewPopup to the new menu to show.
		// Otherwise, m_iNewPopup will be -1 as set above.
		// So just set iButton to the next popup menu and keep looping...
		iButton = m_iNewPopup;
	}
	menu.Detach();
}

//////////////////
// Given button rectangle, compute point and "exclude rect" for
// TrackPopupMenu, based on current docking style, so that the menu will
// appear always inside the window.
//
CPoint CMenuBar::ComputeMenuTrackPoint(const CRect& rcButn, TPMPARAMS& tpm)
{
	tpm.cbSize = sizeof(tpm);
	DWORD dwStyle = m_dwStyle;
	CPoint pt;
	CRect& rcExclude = (CRect&)tpm.rcExclude;
	rcExclude = rcButn;
	::GetWindowRect(::GetDesktopWindow(), &rcExclude);

	switch (dwStyle & CBRS_ALIGN_ANY) {
	case CBRS_ALIGN_BOTTOM:
		pt = CPoint(rcButn.left, rcButn.top);
		rcExclude.top = rcButn.top;
		break;

	case CBRS_ALIGN_LEFT:
		pt = CPoint(rcButn.right, rcButn.top);
		rcExclude.right = rcButn.right;
		break;

	case CBRS_ALIGN_RIGHT:
		pt = CPoint(rcButn.left, rcButn.top);
		rcExclude.left = rcButn.left;
		break;

	default: //	case CBRS_ALIGN_TOP:
		pt = CPoint(rcButn.left, rcButn.bottom);
		break;
	}
	return pt;
}

//////////////////
// This function translates special menu keys and mouse actions.
// You must call it from your frame's PreTranslateMessage.
//
BOOL CMenuBar::TranslateFrameMessage(MSG* pMsg)
{
	ASSERT_VALID(this);
	ASSERT(pMsg);
	UINT msg = pMsg->message;
	if (WM_LBUTTONDOWN <= msg && msg <= WM_MOUSELAST) {
		if (pMsg->hwnd != m_hWnd && m_iTrackingState > 0) {
			// user clicked outside menu bar: exit tracking mode
			MBTRACE(_T("CMenuBar::TranslateFrameMessage: user clicked outside menu bar: end tracking\n"));
			SetTrackingState(TRACK_NONE);
		}

	} else if (msg==WM_SYSKEYDOWN || msg==WM_SYSKEYUP || msg==WM_KEYDOWN) {

		BOOL bAlt = HIWORD(pMsg->lParam) & KF_ALTDOWN; // Alt key down
		TCHAR vkey = pMsg->wParam;							  // get virt key
		if (vkey==VK_MENU ||
			(vkey==VK_F10 && !((GetKeyState(VK_SHIFT) & 0x80000000) ||
			                   (GetKeyState(VK_CONTROL) & 0x80000000) || bAlt))) {

			// key is VK_MENU or F10 with no alt/ctrl/shift: toggle menu mode
			if (msg==WM_SYSKEYUP) {
				MBTRACE(_T("CMenuBar::TranslateFrameMessage: handle menu key\n"));
				ToggleTrackButtonMode();
			}
			return TRUE;

		} else if ((msg==WM_SYSKEYDOWN || msg==WM_KEYDOWN)) {
			if (m_iTrackingState == TRACK_BUTTON) {
				// I am tracking: handle left/right/up/down/space/Esc
				switch (vkey) {
				case VK_LEFT:
				case VK_RIGHT:
					// left or right-arrow: change hot button if tracking buttons
					MBTRACE(_T("CMenuBar::TranslateFrameMessage: VK_LEFT/RIGHT\n"));
					SetHotItem(GetNextOrPrevButton(GetHotItem(), vkey==VK_LEFT));
					return TRUE;

				case VK_SPACE:  // (personally, I like SPACE to enter menu too)
				case VK_UP:
				case VK_DOWN:
					// up or down-arrow: move into current menu, if any
					MBTRACE(_T("CMenuBar::TranslateFrameMessage: VK_UP/DOWN/SPACE\n"));
					TrackPopup(GetHotItem());
					return TRUE;

				case VK_ESCAPE:
					// escape key: exit tracking mode
					MBTRACE(_T("CMenuBar::TranslateFrameMessage: VK_ESCAPE\n"));
					SetTrackingState(TRACK_NONE);
					return TRUE;
				}
			}

			// Handle alphanumeric key: invoke menu. Note that Alt-X
			// chars come through as WM_SYSKEYDOWN, plain X as WM_KEYDOWN.
			if ((bAlt || m_iTrackingState == TRACK_BUTTON) && isalnum(vkey)) {
				// Alt-X, or else X while in tracking mode
				UINT nID;
				if (MapAccelerator(vkey, nID)) {
					MBTRACE(_T("CMenuBar::TranslateFrameMessage: map acclerator\n"));
					TrackPopup(nID);	 // found menu mnemonic: track it
					return TRUE;		 // handled
				} else if (m_iTrackingState==TRACK_BUTTON && !bAlt) {
					MessageBeep(0);
					return TRUE;
				}
			}

			// Default for any key not handled so far: return to no-menu state
			if (m_iTrackingState > 0) {
				MBTRACE(_T("CMenuBar::TranslateFrameMessage: unknown key, stop tracking\n"));
				SetTrackingState(TRACK_NONE);
			}
		}
	}
	return FALSE; // not handled, pass along
}

#ifdef _DEBUG
void CMenuBar::AssertValid() const
{
	CCJToolBar::AssertValid();
	ASSERT(m_hmenu==NULL || ::IsMenu(m_hmenu));
	ASSERT(TRACK_NONE<=m_iTrackingState && m_iTrackingState<=TRACK_POPUP);
	m_frameHook.AssertValid();
}

void CMenuBar::Dump(CDumpContext& dc) const
{
	CCJToolBar::Dump(dc);
}
#endif

//////////////////////////////////////////////////////////////////
// CMenuBarFrameHook is used to trap menu-related messages sent to the owning
// frame. The same class is also used to trap messages sent to the MDI client
// window in an MDI app. I should really use two classes for this,
// but it uses less code to chare the same class. Note however: there
// are two different INSTANCES of CMenuBarFrameHook in CMenuBar: one for
// the frame and one for the MDI client window.
//
CMenuBarFrameHook::CMenuBarFrameHook()
{
}

CMenuBarFrameHook::~CMenuBarFrameHook()
{
	HookWindow((HWND)NULL); // (unhook)
}

//////////////////
// Install hook to trap window messages sent to frame or MDI client.
// 
BOOL CMenuBarFrameHook::Install(CMenuBar* pMenuBar, HWND hWndToHook)
{
	ASSERT_VALID(pMenuBar);
	m_pMenuBar = pMenuBar;
	return HookWindow(hWndToHook);
}

//////////////////////////////////////////////////////////////////
// Trap frame/MDI client messages specific to menubar. 
//
LRESULT CMenuBarFrameHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
	CMenuBar& mb = *m_pMenuBar;

	switch (msg) {
	// The following messages are trapped for the frame window
	case WM_SYSCOLORCHANGE:
		mb.UpdateFont();
		break;

	case WM_MENUSELECT:
		mb.OnMenuSelect((HMENU)lp, (UINT)LOWORD(wp));
		break;
	}
	return CSubclassWnd::WindowProc(msg, wp, lp);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -