📄 menubar.cpp
字号:
// MenuBar.cpp : implementation file
#include "stdafx.h"
#include "MenuBar.h"
#include <afxole.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#ifdef _DEBUG
const BOOL bTraceOn = FALSE;
#define LTRACE if (bTraceOn) TRACE
const BOOL bTraceOn2 = TRUE;
#define LTRACE2 if (bTraceOn2) TRACE
#else
#define LTRACE
#define LTRACE2
#endif
//////////////////////////////////////////////////////////////////////
// I've found string resource of "More windows" in "user.exe".
// But I can't load it, so please replace a following with your language.
static const TCHAR _szMoreWindows[] = _T("&More windows...");
//////////////////////////////////////////////////////////////////////
// used for OLE menu (easy fix)
static BOOL _bWindowMenuSendCmd = FALSE;
static int _nPrevIndexForCmd = -1;
//////////////////////////////////////////////////////////////////////
// hook
static CMenuBar* g_pMenuBar = NULL;
static HHOOK g_hMsgHook = NULL;
// message
const UINT CMenuBar::WM_GETMENU = ::RegisterWindowMessage(_T("CMenuBar::WM_GETMENU"));
const UINT MB_SET_MENU_NULL = ::RegisterWindowMessage(_T("CMenuBar::MB_SET_MENU_NULL"));
const int cxBorder2 = ::GetSystemMetrics(SM_CXBORDER)*2;
const int cyBorder2 = ::GetSystemMetrics(SM_CYBORDER)*2;
// common resources
static CFont _fontHorzMenu, _fontVertMenu;
static int _cyHorzFont, _cyMenuOnBar, _cyTextMargin;
const int CXTEXTMARGIN = 5;
static BOOL _InitCommonResources(BOOL bForce = FALSE)
{
if (bForce == FALSE && _fontHorzMenu.m_hObject != NULL)
return TRUE;// no need to reinitialize
// clean up
_fontHorzMenu.DeleteObject();
_fontVertMenu.DeleteObject();
// create fonts
NONCLIENTMETRICS info; info.cbSize = sizeof(info);
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
if (!_fontHorzMenu.CreateFontIndirect(&info.lfMenuFont))
return FALSE;
// create vertical font
info.lfMenuFont.lfEscapement = -900;
info.lfMenuFont.lfOrientation = -900;
if (!_fontVertMenu.CreateFontIndirect(&info.lfMenuFont))
return FALSE;
// get font height
_cyHorzFont = abs(info.lfMenuFont.lfHeight);
// calc Y text margin
_cyMenuOnBar = info.iMenuHeight;
_cyMenuOnBar = max(_cyMenuOnBar, ::GetSystemMetrics(SM_CYSMICON));
_cyTextMargin = (_cyMenuOnBar - _cyHorzFont) / 2;
return TRUE;
}
// I wanted to control popup point, but I've fount we can never get popupmenu rect before popup.
// even if not owner draw menu...
static CPoint _ComputeMenuTrackPoint(const CRect& rcItem, DWORD dwStyle, UINT& fuFlags, TPMPARAMS& tpm)
{
fuFlags = TPM_LEFTBUTTON | TPM_LEFTALIGN | TPM_HORIZONTAL;
tpm.cbSize = sizeof(tpm);
CPoint pt;
CRect& rcExclude = (CRect&)tpm.rcExclude;
CWnd::GetDesktopWindow()->GetWindowRect(&rcExclude);
CRect rcFrame;
AfxGetMainWnd()->GetWindowRect(&rcFrame);
switch (dwStyle & CBRS_ALIGN_ANY) {
case CBRS_ALIGN_RIGHT:
case CBRS_ALIGN_LEFT:
pt = CPoint(rcItem.right+1, rcItem.top-1);
// to avoid strange menu flip, won't do : [rcExclude.right = rcItem.right+1;]
// I want to use : fuFlags |= TPM_HORNEGANIMATION;
break;
default: // case CBRS_ALIGN_TOP:
pt = CPoint(rcItem.left-1, rcItem.bottom);
rcExclude.bottom = rcItem.bottom+1;// <- insead of [fuFlags |= TPM_VERPOSANIMATION;]
break;
}
return pt;
}
static int _CalcTextWidth(const CString& strText)
{
CWindowDC dc(NULL);
CRect rcText(0, 0, 0, 0);
CFont* pOldFont = dc.SelectObject(&_fontHorzMenu);
dc.DrawText(strText, &rcText, DT_SINGLELINE | DT_CALCRECT);
dc.SelectObject(pOldFont);
return rcText.Width();
}
// grippers pasted from MFC6
#define CX_GRIPPER 3
#define CY_GRIPPER 3
#define CX_BORDER_GRIPPER 2
#define CY_BORDER_GRIPPER 2
#define CX_GRIPPER_ALL CX_GRIPPER + CX_BORDER_GRIPPER*2
#define CY_GRIPPER_ALL CY_GRIPPER + CY_BORDER_GRIPPER*2
/////////////////////////////////////////////////////////////////////////////
// CMenuBar
BEGIN_MESSAGE_MAP(CMenuBar, CControlBar)
//{{AFX_MSG_MAP(CMenuBar)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_CREATE()
ON_WM_LBUTTONUP()
ON_WM_TIMER()
ON_WM_DESTROY()
ON_WM_NCCALCSIZE()
ON_WM_NCPAINT()
ON_WM_NCHITTEST()
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE(MB_SET_MENU_NULL, OnSetMenuNull)
END_MESSAGE_MAP()
IMPLEMENT_DYNAMIC(CMenuBar, CControlBar)
/////////////////////////////////////////////////////////////////////////////
// CMenuBar Construction
CMenuBar::CMenuBar()
{
m_nCurIndex = -1;
m_nTrackingState = none;
m_bProcessRightArrow = m_bProcessLeftArrow = TRUE;
m_bIgnoreAlt = FALSE;
m_hMenu = NULL;
m_nIDEvent = NULL;
m_bMDIMaximized = FALSE;
m_hWndMDIClient = NULL;
m_hWndActiveChild = NULL;
m_pMenuIcon = NULL;
m_pMenuControl = NULL;
m_bDelayedButtonLayout = TRUE;
m_dwExStyle = 0;
m_bFrameActive = FALSE;
m_bMDIApp = FALSE;
}
BOOL CMenuBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
{
return CreateEx(pParentWnd, dwStyle,
CRect(m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder), nID);
}
BOOL CMenuBar::CreateEx(CWnd* pParentWnd, DWORD dwStyle, CRect rcBorders, UINT nID)
{
ASSERT_VALID(pParentWnd);// must have a parent
ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC)));
SetBorders(rcBorders);
// save the original style
m_dwExStyle = dwStyle;
// save the style
m_dwStyle = (dwStyle & CBRS_ALL);// ******fixed by Mark Gentry, thanx!******
dwStyle &= ~CBRS_ALL;
CString strClass = AfxRegisterWndClass(
CS_HREDRAW | CS_VREDRAW |
CS_DBLCLKS,// don't forget!
AfxGetApp()->LoadStandardCursor(IDC_ARROW),
(HBRUSH)(COLOR_BTNFACE+1));
return CWnd::Create(strClass, _T("MenuBar"), dwStyle, CRect(), pParentWnd, nID);
}
int CMenuBar::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CControlBar::OnCreate(lpCreateStruct) == -1)
return -1;
CWnd* pFrame = GetOwner();
ASSERT_VALID(pFrame);
// hook frame window to trap WM_MENUSELECT
m_hookFrame.Install(this, pFrame->GetSafeHwnd());
// If this is an MDI app, hook client window to trap WM_MDISETMENU
if (pFrame->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))) {
m_bMDIApp = TRUE;
m_hWndMDIClient = ((CMDIFrameWnd*)pFrame)->m_hWndMDIClient;
ASSERT(::IsWindow(m_hWndMDIClient));
m_hookMDIClient.Install(this, m_hWndMDIClient);
}
if (!_InitCommonResources()) {
TRACE(_T("Failed to create menubar resource\n"));
return FALSE;
}
return 0;
}
BOOL CMenuBar::InitItems()
{
ASSERT(m_hMenu);
// clean up all items
DeleteItems();
// buttons
for (int i = 0; i < ::GetMenuItemCount(m_hMenu); ++i) {
m_arrItem.Add(new CMenuButton(m_hMenu, i));
}
if (m_bMDIApp) {
// icon
m_pMenuIcon = new CMenuIcon(this);
m_arrItem.InsertAt(0, m_pMenuIcon);
// frame control
m_pMenuControl = new CMenuControl(this);
m_arrItem.Add(m_pMenuControl);
// reinitializing
m_pMenuIcon->OnActivateChildWnd();
m_pMenuControl->OnActivateChildWnd();
}
return TRUE;
}
BOOL CMenuBar::LoadMenuBar(UINT nIDResource)
{
ASSERT(m_hMenu == NULL);
// if (m_hMenu) {
// ::DestroyMenu(m_hMenu);
// m_hMenu = NULL;
// }
ASSERT_VALID(m_pDockSite);
if (m_pDockSite->GetMenu()) {
PostMessage(MB_SET_MENU_NULL, (WPARAM)m_pDockSite->GetSafeHwnd());
}
m_hMenu = ::LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource));
if (m_hMenu == NULL) {
TRACE(_T("Failed to load menu\n"));
return FALSE;
}
return InitItems();
}
HMENU CMenuBar::LoadMenu(HMENU hMenu, HMENU hWindowMenu)
{
ASSERT(::IsMenu(hMenu));
ASSERT_VALID(this);
CFrameWnd* pFrame = GetParentFrame();
if (::GetMenu(pFrame->GetSafeHwnd()) != NULL) {
// not to make MFC ignore SetMenu(NULL), post it.
PostMessage(MB_SET_MENU_NULL, (WPARAM)pFrame->GetSafeHwnd());
}
HMENU hOldMenu = m_hMenu;
m_hMenu = hMenu;// menu is shared with MFC
// initialize Items
VERIFY(InitItems());
if (hMenu) {
m_hWindowMenu = hWindowMenu;
RefreshBar();// and menubar itself
}
return hOldMenu;
}
void CMenuBar::RefreshBar()
{
InvalidateRect(NULL);
#if _MFC_VER >= 0x600
if (GetParent()->IsKindOf(RUNTIME_CLASS(CReBar))) {
m_bDelayedButtonLayout = TRUE;// to avoid ASSERTION
Layout();
}
#endif
CFrameWnd* pFrame = (CFrameWnd*)GetTopLevelFrame();
ASSERT_VALID(pFrame); ASSERT(pFrame->IsFrameWnd());
pFrame->RecalcLayout();
// floating frame
CFrameWnd* pMiniFrame = GetParentFrame();
if (pMiniFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)))
pMiniFrame->RecalcLayout();
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar clean up
CMenuBar::~CMenuBar()
{
}
void CMenuBar::DeleteItems()
{
for(int i = 0; i < m_arrItem.GetSize(); ++i) {
CMenuItem* pItem = m_arrItem[i];
delete pItem;
}
m_arrItem.RemoveAll();
m_pMenuIcon = NULL;
m_pMenuControl = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar draw
void CMenuBar::DoPaint(CDC* pDC)
{
// if (m_bDelayedButtonLayout)
// Layout();
//LTRACE(_T("CMenuBar::DoPaint\n"));
CRect rect; GetClientRect(rect);
// draw items
for (int i = 0; i < m_arrItem.GetSize(); ++i) {
m_arrItem[i]->Update(pDC);
}
// delay draw captions
if (m_pMenuControl) {
if (IsFloating()) {
m_pMenuControl->DelayLayoutAndDraw(pDC, rect.Size());
}
else {
if (m_dwStyle & CBRS_ORIENT_HORZ)
m_pMenuControl->DelayLayoutAndDraw(pDC, CSize(GetClipBoxLength(TRUE), rect.Height()));
else
m_pMenuControl->DelayLayoutAndDraw(pDC, CSize(rect.Width(), GetClipBoxLength(FALSE)));
}
}
}
void CMenuBar::UpdateBar(TrackingState nState, int nNewIndex)
{
if (m_nTrackingState == buttonmouse)
m_bIgnoreAlt = FALSE;// if prev state is BUTTONMOUSE, always should be FALSE!
m_nTrackingState = nState;
#ifdef _DEBUG
// static LPCTSTR lpszStates[] = { _T("NONE"), _T("BUTTON"), _T("POPUP"), _T("BUTTONMOUSE") };
// LTRACE(_T("CMenuBar::UpdateBar state to %s, button=%d\n"),
// lpszStates[nState], nNewIndex);
#endif
// clean up
if (IsValidIndex(m_nCurIndex)) {
CMenuItem* pItem = m_arrItem[m_nCurIndex];
CClientDC dc(this);
pItem->ModifyState(MISTATE_HOT | MISTATE_PRESSED, 0);
pItem->Update(&dc);
// draw captions forcefully
if (m_pMenuControl) {
CRect rcCross = m_pMenuControl->GetRect() & m_arrItem[m_nCurIndex]->GetRect();
if (!rcCross.IsRectEmpty()) {
m_pMenuControl->ForceDrawControl(&dc);
}
}
m_nCurIndex = -1;
}
if (nState != none) {
ASSERT(IsValidIndex(nNewIndex));
m_nCurIndex = nNewIndex;
CMenuItem* pItem = m_arrItem[m_nCurIndex];
CClientDC dc(this);
if (nState == button || nState == buttonmouse) {
pItem->ModifyState(MISTATE_PRESSED, MISTATE_HOT);
pItem->Update(&dc);
}
else if (nState == popup) {
pItem->ModifyState(MISTATE_HOT, MISTATE_PRESSED);
pItem->Update(&dc);
}
// draw captions forcefully
if (m_pMenuControl) {
CRect rcCross = m_pMenuControl->GetRect() & m_arrItem[m_nCurIndex]->GetRect();
if (!rcCross.IsRectEmpty()) {
m_pMenuControl->ForceDrawControl(&dc);
}
}
}
else {
// must be default parameter
ASSERT(nNewIndex == -1);
}
m_bProcessRightArrow = m_bProcessLeftArrow = TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar message handler
int CMenuBar::HitTestOnTrack(CPoint point)
{
for (int i = 0; i < GetItemCount(); ++i) {
CMenuItem* pItem = m_arrItem[i];
CRect rcItem = pItem->GetRect();
if ((pItem->GetStyle() & MISTYLE_TRACKABLE) &&
rcItem.PtInRect(point))
return i;
}
return -1;
}
void CMenuBar::OnLButtonDown(UINT nFlags, CPoint point)
{
// LTRACE("CMenuBar::OnLButtonDown\n");
if (m_pMenuControl && m_pMenuControl->OnMouseMsg(WM_LBUTTONDOWN, nFlags, point)) {
return; // eat it!
}
int nIndex = HitTestOnTrack(point);
if (IsValidIndex(nIndex) && (m_arrItem[nIndex]->GetStyle() & MISTYLE_TRACKABLE)) {
TrackPopup(nIndex);
return; // eat it!
}
CControlBar::OnLButtonDown(nFlags, point);
}
void CMenuBar::OnMouseMove(UINT nFlags, CPoint point)
{
// LTRACE(_T("CMenuBar::OnMouseMove\n"));
if (m_pMenuControl && m_pMenuControl->OnMouseMsg(WM_MOUSEMOVE, nFlags, point)) {
CControlBar::OnMouseMove(nFlags, point);
return;
}
int nIndex = HitTestOnTrack(point);
if (IsValidIndex(nIndex)) {
if (m_nCurIndex == -1 || m_nCurIndex != nIndex) {// if other button
UpdateBar(buttonmouse, nIndex);// button made hot with mouse
if (!m_nIDEvent)
m_nIDEvent = SetTimer(1, 100, NULL);
}
}
else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -