📄 menubar.cpp
字号:
////////////////////////////////////////////////////////////////
// Copyright 1998 Paul DiLascia
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// CMenuBar implements menu bar for MFC. See MenuBar.h for how
// to use, and also the MBTest sample application.
//
#include "StdAfx.h"
#include "MenuBar.h"
const UINT MB_SET_MENU_NULL = WM_USER + 1100;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// if you want to see extra TRACE diagnostics, set CMenuBar::bTRACE = TRUE
BOOL CMenuBar::bTRACE = FALSE;
#ifdef _DEBUG
#define MBTRACEFN \
CTraceFn __fooble; \
if (CMenuBar::bTRACE)\
TRACE
#define MBTRACE \
if (CMenuBar::bTRACE)\
TRACE
#else
#define MBTRACEFN TRACE
#define MBTRACE TRACE
#endif
IMPLEMENT_DYNAMIC(CMenuBar, CCJToolBar)
BEGIN_MESSAGE_MAP(CMenuBar, CCJToolBar)
ON_WM_CREATE()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_SIZE()
ON_UPDATE_COMMAND_UI_RANGE(0, 256, OnUpdateMenuButton)
ON_MESSAGE(MB_SET_MENU_NULL, OnSetMenuNull)
END_MESSAGE_MAP()
CMenuBar::CMenuBar()
{
if (iVerComCtl32 <= 470)
AfxMessageBox(_T("Warning: This program requires comctl32.dll version 4.71 or greater."));
m_iTrackingState = TRACK_NONE; // initial state: not tracking
m_iPopupTracking = m_iNewPopup = -1; // invalid
m_hmenu = NULL;
m_bAutoRemoveFrameMenu = TRUE; // set frame's menu to NULL
}
CMenuBar::~CMenuBar()
{
}
//////////////////
// Menu bar was created: install hook into owner window
//
int CMenuBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CCJToolBar::OnCreate(lpCreateStruct)==-1)
return -1;
UpdateFont();
CWnd* pFrame = GetOwner();
ASSERT_VALID(pFrame);
m_frameHook.Install(this, *pFrame);
return 0; // OK
}
//////////////////
// Set menu bar font from current system menu font
//
void CMenuBar::UpdateFont()
{
static CFont font;
NONCLIENTMETRICS info;
info.cbSize = sizeof(info);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
if ((HFONT)font)
font.DeleteObject();
VERIFY(font.CreateFontIndirect(&info.lfMenuFont));
SetFont(&font);
}
//////////////////
// The reason for having this is so MFC won't automatically disable
// the menu buttons. Assumes < 256 top-level menu items. The ID of
// the ith menu button is i. IOW, the index and ID are the same.
//
void CMenuBar::OnUpdateMenuButton(CCmdUI* pCmdUI)
{
ASSERT_VALID(this);
if (IsValidButton(pCmdUI->m_nID))
pCmdUI->Enable(TRUE);
}
//////////////////
// Recompute layout of menu bar
//
void CMenuBar::RecomputeMenuLayout()
{
SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
}
//////////////////
// Make frame recalculate control bar sizes after menu change
//
void CMenuBar::RecomputeToolbarSize()
{
// Force toolbar to recompute size
CFrameWnd* pFrame = (CFrameWnd*)GetOwner();
ASSERT_VALID(pFrame);
ASSERT(pFrame->IsFrameWnd());
pFrame->RecalcLayout();
// floating frame
pFrame = GetParentFrame();
if (pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)))
pFrame->RecalcLayout();
}
//////////////////
// Set tracking state: none, button, or popup
//
void CMenuBar::SetTrackingState(TRACKINGSTATE iState, int iButton)
{
ASSERT_VALID(this);
if (iState != m_iTrackingState) {
if (iState == TRACK_NONE)
iButton = -1;
#ifdef _DEBUG
static LPCTSTR StateName[] = { _T("NONE"), _T("BUTTON"), _T("POPUP") };
MBTRACE(_T("CMenuBar::SetTrackingState to %s, button=%d\n"),
StateName[iState], iButton);
#endif
SetHotItem(iButton); // could be none (-1)
if (iState==TRACK_POPUP) {
// set related state stuff
m_bEscapeWasPressed = FALSE; // assume Esc key not pressed
m_bProcessRightArrow = // assume left/right arrow..
m_bProcessLeftArrow = TRUE; // ..will move to prev/next popup
m_iPopupTracking = iButton; // which popup I'm tracking
}
m_iTrackingState = iState;
}
}
//////////////////
// Toggle state from home state to button-tracking and back
//
void CMenuBar::ToggleTrackButtonMode()
{
ASSERT_VALID(this);
if (m_iTrackingState == TRACK_NONE || m_iTrackingState == TRACK_BUTTON) {
SetTrackingState(m_iTrackingState == TRACK_NONE ?
TRACK_BUTTON : TRACK_NONE, 0);
}
}
//////////////////
// Get button index before/after a given button
//
int CMenuBar::GetNextOrPrevButton(int iButton, BOOL bPrev)
{
ASSERT_VALID(this);
if (bPrev) {
iButton--;
if (iButton <0)
iButton = GetButtonCount() - 1;
} else {
iButton++;
if (iButton >= GetButtonCount())
iButton = 0;
}
return iButton;
}
/////////////////
// This is to correct a bug in the system toolbar control: TB_HITTEST only
// looks at the buttons, not the size of the window. So it returns a button
// hit even if that button is totally outside the size of the window!
//
int CMenuBar::HitTest(CPoint p) const
{
int iHit = CCJToolBar::HitTest(p);
if (iHit>0) {
CRect rc;
GetClientRect(&rc);
if (!rc.PtInRect(p)) // if point is outside window
iHit = -1; // can't be a hit!
}
return iHit;
}
//////////////////
// Load a different menu. The HMENU must not belong to any CMenu,
// and you must free it when you're done. Returns old menu.
//
HMENU CMenuBar::LoadMenu(HMENU hmenu)
{
MBTRACEFN(_T("CMenuBar::LoadMenu\n"));
UINT iPrevID=(UINT)-1;
ASSERT(::IsMenu(hmenu));
ASSERT_VALID(this);
if (m_bAutoRemoveFrameMenu) {
CFrameWnd* pFrame = GetParentFrame();
if (::GetMenu(*pFrame)!=NULL) {
// I would like to set the frame's menu to NULL now, but if I do, MFC
// gets all upset: it calls GetMenu and expects to have a real menu.
// So Instead, I post a message to myself. Because the message is
// posted, not sent, I won't process it until MFC is done with all its
// initialization stuff. (MFC needs to set CFrameWnd::m_hMenuDefault
// to the menu, which it gets by calling GetMenu.)
//
PostMessage(MB_SET_MENU_NULL, (WPARAM)pFrame->GetSafeHwnd());
}
}
HMENU hOldMenu = m_hmenu;
m_hmenu = hmenu;
// delete existing buttons
int nCount = GetButtonCount();
while (nCount--) {
VERIFY(DeleteButton(0));
}
SetImageList(NULL);
// SetButtonSize(CSize(0,0)); // This barfs in VC 6.0
DWORD dwStyle = GetStyle();
BOOL bModifyStyle = ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT);
// add text buttons
UINT nMenuItems = hmenu ? ::GetMenuItemCount(hmenu) : 0;
for (UINT i=0; i < nMenuItems; i++) {
TCHAR name[64];
memset(name, 0, sizeof(name)); // guarantees double-0 at end
if (::GetMenuString(hmenu, i, name, countof(name)-1, MF_BYPOSITION)) {
TBBUTTON tbb;
memset(&tbb, 0, sizeof(tbb));
tbb.idCommand = ::GetMenuItemID(hmenu, i);
// Because the toolbar is too brain-damaged to know if it already has
// a string, and is also too brain-dead to even let you delete strings,
// I have to determine if each string has been added already. Otherwise
// in a MDI app, as the menus are repeatedly switched between doc and
// no-doc menus, I will keep adding strings until somebody runs out of
// memory. Sheesh!
//
int iString = -1;
for (int j=0; j<m_arStrings.GetSize(); j++) {
if (m_arStrings[j] == name) {
iString = j; // found it
break;
}
}
if (iString <0) {
// string not found: add it
iString = AddStrings(name);
m_arStrings.SetAtGrow(iString, name);
}
tbb.iString = iString;
tbb.fsState = TBSTATE_ENABLED;
tbb.fsStyle = TBSTYLE_AUTOSIZE;
tbb.iBitmap = -1;
tbb.idCommand = i;
VERIFY(AddButtons(1, &tbb));
}
}
if (bModifyStyle)
SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
if (hmenu) {
AutoSize(); // size buttons
RecomputeToolbarSize(); // and menubar itself
}
return hOldMenu;
}
//////////////////
// Load menu from resource
//
HMENU CMenuBar::LoadMenu(LPCSTR lpszMenuName)
{
return LoadMenu(::LoadMenu(AfxGetResourceHandle(), lpszMenuName));
}
//////////////////
// Set the frame's menu to NULL. WPARAM is HWND of frame.
//
LRESULT CMenuBar::OnSetMenuNull(WPARAM wp, LPARAM lp)
{
HWND hwnd = (HWND)wp;
ASSERT(::IsWindow(hwnd));
::SetMenu(hwnd, NULL);
return 0;
}
//////////////////
// Handle mouse click: if clicked on button, press it
// and go into main menu loop.
//
void CMenuBar::OnLButtonDown(UINT nFlags, CPoint pt)
{
MBTRACEFN(_T("CMenuBar::OnLButtonDown\n"));
ASSERT_VALID(this);
int iButton = HitTest(pt);
if (iButton >= 0 && iButton<GetButtonCount()) // if mouse is over a button:
TrackPopup(iButton); // track it
else // otherwise:
CCJToolBar::OnLButtonDown(nFlags, pt); // pass it on...
}
//////////////////
// Handle mouse movement
//
void CMenuBar::OnMouseMove(UINT nFlags, CPoint pt)
{
ASSERT_VALID(this);
if (m_iTrackingState==TRACK_BUTTON) {
// In button-tracking state, ignore mouse-over to non-button area.
// Normally, the toolbar would de-select the hot item in this case.
//
// Only change the hot item if the mouse has actually moved.
// This is necessary to avoid a bug where the user moves to a different
// button from the one the mouse is over, and presses arrow-down to get
// the menu, then Esc to cancel it. Without this code, the button will
// jump to wherever the mouse is--not right.
int iHot = HitTest(pt);
if (IsValidButton(iHot) && pt != m_ptMouse)
SetHotItem(iHot);
return; // don't let toolbar get it
}
m_ptMouse = pt; // remember point
CCJToolBar::OnMouseMove(nFlags, pt);
}
//////////////////
// Window was resized: need to recompute layout
//
void CMenuBar::OnSize(UINT nType, int cx, int cy)
{
CCJToolBar::OnSize(nType, cx, cy);
RecomputeMenuLayout();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -