📄 menubar.cpp
字号:
// MenuBar.cpp :
//
#include "stdafx.h"
#include "MenuBar.h"
#include <afxpriv.h>
#define _AfxGetDlgCtrlID(hWnd) ((UINT)(WORD)::GetDlgCtrlID(hWnd))
#define HORZF(dw) (dw & CBRS_ORIENT_HORZ)
#define VERTF(dw) (dw & CBRS_ORIENT_VERT)
static void AdjustRectangle(CRect& rect, CPoint pt)
{
int nXOffset = (pt.x < rect.left) ? (pt.x - rect.left) :
(pt.x > rect.right) ? (pt.x - rect.right) : 0;
int nYOffset = (pt.y < rect.top) ? (pt.y - rect.top) :
(pt.y > rect.bottom) ? (pt.y - rect.bottom) : 0;
rect.OffsetRect(nXOffset, nYOffset);
}
#include <math.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMenuBar
// I want overide EndDrag, but it's not virtual.
// So I have to overide StartDrag!
class CMenuBarDockContext : public CDockContext
{
public:
CMenuBarDockContext(CControlBar* pBar) : CDockContext(pBar) { }
virtual void StartDrag(CPoint pt);
private:
BOOL _Track();
void _EndDrag();
};
namespace {
// hook
CMenuBar* g_pMenuBar = NULL;
HHOOK g_hMsgHook = NULL;
// message
const UINT MB_SET_MENU_NULL = WM_USER + 1100;
// layout
const int CXGAP = 5;
const int CYGAP = 5;
const int CYGAPVERT = 3;
const int CXGRIPPER = 7;
int cyMenuButton = 0;
int cxBorder2 = ::GetSystemMetrics(SM_CXBORDER) * 2;//bWin4 ? CX_BORDER*2 : CX_BORDER;
int cyBorder2 = ::GetSystemMetrics(SM_CYBORDER) * 2;//bWin4 ? CY_BORDER*2 : CY_BORDER;
#ifdef _DEBUG
// if you won't output TRACE in debug mode, make it FALSE;
BOOL bTraceOn = TRUE;
#endif
}
#ifdef _DEBUG
#define LTRACE if (bTraceOn) TRACE
#else
#define LTRACE
#endif
BOOL CMenuBar::m_bMDIApp = FALSE;
BEGIN_MESSAGE_MAP(CMenuBar, CControlBar)
//{{AFX_MSG_MAP(CMenuBar)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_KILLFOCUS()
ON_WM_CREATE()
ON_WM_LBUTTONUP()
ON_WM_DESTROY()
ON_MESSAGE(MB_SET_MENU_NULL, OnSetMenuNull)
ON_MESSAGE(WM_SYSCOLORCHANGE, OnSettingChange)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_SETTINGCHANGE, OnSettingChange)
END_MESSAGE_MAP()
CMenuBar::CMenuBar()
{
m_nCurIndex = -1;
m_nTrackingState = none;
m_bProcessRightArrow = m_bProcessLeftArrow = TRUE;
m_bIgnoreAlt = FALSE;
m_bDown = FALSE;
m_hMenu = NULL;
m_nIDEvent = NULL;
m_bIcon = FALSE;
m_bMDIMaximized = FALSE;
m_hWndMDIClient = NULL;
m_hWndActiveChild = NULL;
m_pMenuIcon = NULL;
m_pMenuControl = NULL;
m_nCmdShow = SW_SHOW;
}
BOOL CMenuBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
{
ASSERT_VALID(pParentWnd); // must have a parent
ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC)));
// save the style
m_dwStyle = dwStyle & CBRS_ALL; // fixed by Mark Gentry, thanx!
m_dwStyle |= CBRS_SIZE_DYNAMIC;
CString strClass = AfxRegisterWndClass(
CS_HREDRAW | CS_VREDRAW , // 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))) {
CMenuBar::m_bMDIApp = TRUE;
m_hWndMDIClient = ((CMDIFrameWnd*)pFrame)->m_hWndMDIClient;
ASSERT(m_hWndMDIClient);
m_hookMDIClient.Install(this, m_hWndMDIClient);
}
// my own DockContext!
m_pDockContext = new CMenuBarDockContext(this);
return 0;
}
BOOL CMenuBar::InitItems()
{
ASSERT(m_hMenu);
// clean up all items
DeleteItems();
// a little suitable
int nCount = ::GetMenuItemCount(m_hMenu);
ASSERT(nCount > 0);
m_arrItem.SetSize(nCount);
if (!CMenuButton::InitCommonResource()) {
TRACE0("Failed to create bar resource\n");
return FALSE;
}
// buttons
for (int i = 0; i < nCount; ++i) {
m_arrItem[i] = new CMenuButton(m_hMenu, i);
}
cyMenuButton = m_arrItem[0]->m_sizeHorz.cy;
// 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_hWndActiveChild = GetActiveChildWnd(m_bMDIMaximized);
if (m_hWndActiveChild) {// re set
m_pMenuIcon->OnActivateChildWnd(m_hWndActiveChild);
}
if (m_bMDIMaximized) {
m_pMenuIcon->Validate(TRUE);
m_pMenuControl->Validate(TRUE);
}
return TRUE;
}
BOOL CMenuBar::LoadMenuBar(UINT nIDResource)
{
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) {
TRACE0("Failed to load menu\n");
return FALSE;
}
return InitItems();
}
void CMenuBar::RefreshBar()
{
InvalidateRect(NULL);
CFrameWnd* pFrame = (CFrameWnd*)GetOwner();
ASSERT_VALID(pFrame);
ASSERT(pFrame->IsFrameWnd());
pFrame->RecalcLayout();
// floating frame
pFrame = GetParentFrame();
if (pFrame->IsKindOf(RUNTIME_CLASS(CMiniFrameWnd)))
pFrame->RecalcLayout();
}
HMENU CMenuBar::LoadMenu(HMENU hMenu, HMENU hWindowMenu)
{
LTRACE("CMenuBar::LoadMenu\n");
UINT iPrevID=(UINT)-1;
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;
}
CMenuBar::~CMenuBar()
{
if (m_bMDIApp == FALSE && m_hMenu != NULL)
::DestroyMenu(m_hMenu);
m_bitmap.Detach();
if(m_hBmp)
::DeleteObject(m_hBmp);
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar 傾僀僥儉偺忣曬
/////////////////////////////////////////////////////////////////////////////
// CMenuBar 傾僀僥儉昤夋
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)) {
CDC* pDC = GetDC();
m_arrItem[m_nCurIndex]->SetState(CMenuButton::none);
m_arrItem[m_nCurIndex]->Update(pDC);
CRect rcCross = m_pMenuControl->m_rcItem & m_arrItem[m_nCurIndex]->m_rcItem;
if (!rcCross.IsRectEmpty()) {
m_pMenuControl->ForceDrawControl(pDC);
}
ReleaseDC(pDC);
m_nCurIndex = -1;
}
if (nState != none) {
ASSERT(IsValidIndex(nNewIndex));
m_nCurIndex = nNewIndex;
CDC* pDC = GetDC();
if (nState == button || nState == buttonmouse) {
m_arrItem[m_nCurIndex]->SetState(CMenuButton::hot);
m_arrItem[m_nCurIndex]->Update(pDC);
}
else if (nState == popup) {
m_arrItem[m_nCurIndex]->SetState(CMenuButton::select);
m_arrItem[m_nCurIndex]->Update(pDC);
}
CRect rcCross = m_pMenuControl->m_rcItem & m_arrItem[m_nCurIndex]->m_rcItem;
if (!rcCross.IsRectEmpty()) {
m_pMenuControl->ForceDrawControl(pDC);
}
ReleaseDC(pDC);
}
else {
// must be default parameter
ASSERT(nNewIndex == -1);
}
m_bProcessRightArrow = m_bProcessLeftArrow = TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar 儊僢僙乕僕 僴儞僪儔
int CMenuBar::HitTestOnTrack(CPoint point)
{
for (int i = 0; i < GetItemCount(); ++i) {
CMenuItem* pItem = m_arrItem[i];
CRect rcItem = pItem->GetItemRect();
if (pItem->IsValid() && pItem->CanTrack() &&
rcItem.PtInRect(point))
return i;
}
return -1;
}
void CMenuBar::OnLButtonDown(UINT nFlags, CPoint point)
{
// LTRACE("CMenuBar::OnLButtonDown\n");
ASSERT(m_pMenuControl);
if (m_pMenuControl->OnMouseMsg(WM_LBUTTONDOWN, nFlags, point)) {
return; // eat it!
}
int nIndex = HitTestOnTrack(point);
if (IsValidIndex(nIndex) && m_arrItem[nIndex]->CanTrack()) {
/* HMENU hSubMenu = ::GetSubMenu(m_hMenu, nIndex);
if (hSubMenu == NULL) {
UpdateWindow(); // force to repaint
CDC* pDC = GetDC();
m_arrItem[m_nCurIndex]->SetState(CMenuButton::select);
m_arrItem[nIndex]->Update(pDC);
ReleaseDC(pDC);
m_bDown = TRUE;
}
else {
TrackPopup(nIndex);
}
*/
TrackPopup(nIndex);
return; // eat it!
}
CControlBar::OnLButtonDown(nFlags, point);
}
void CMenuBar::OnMouseMove(UINT nFlags, CPoint point)
{
// TRACE("CMenuBar::OnMouseMove\n");
if (!IsTopParentActive() || !GetTopLevelParent()->IsWindowEnabled()) {
// window is not active, ignore
CControlBar::OnMouseMove(nFlags, point);
return;
}
ASSERT(m_pMenuControl);
if (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) { // other button
UpdateBar(buttonmouse, nIndex); // button tracked with mouse
// I wanna know when mouse is away,
// but SetCapture makes ALT+F4 uncatchable
// and WM_CAPTURECHANGED is never sent(why?), so we have to set timer
_KillTimer();
m_nIDEvent = SetTimer(1, 250, 0);
}
}
else {
UpdateBar();
}
CControlBar::OnMouseMove(nFlags, point);
}
/*//////////////////////////////Libin
//也可不用时间片的方法,可重载OnUpdateCmdUI(..)
void CMenuBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{
if (m_nTrackingState == buttonmouse)
{
CPoint pt; ::GetCursorPos(&pt);
CRect rect;
GetWindowRect(&rect);
if (!rect.PtInRect(pt))
UpdateBar();
}
}
//////////////////////////////*/
void CMenuBar::OnTimer(UINT nIDEvent)
{
if( nIDEvent == m_nIDEvent && m_nTrackingState == buttonmouse) {
CPoint pt; ::GetCursorPos(&pt);
CRect rect;
GetWindowRect(&rect);
if (!rect.PtInRect(pt)) {
UpdateBar();
_KillTimer();
}
}
CControlBar::OnTimer(nIDEvent);
}
void CMenuBar::OnKillFocus(CWnd* pNewWnd)
{
CControlBar::OnKillFocus(pNewWnd);
// TODO:
UpdateBar();
}
LRESULT CMenuBar::OnSetMenuNull(WPARAM wParam, LPARAM)
{
HWND hWnd = (HWND)wParam;
ASSERT(::IsWindow(hWnd));
::SetMenu(hWnd, NULL);
return 0;
}
LRESULT CMenuBar::OnSettingChange(WPARAM wParam, LPARAM lParam)
{
InitItems();
CFrameWnd* pFrame = GetParentFrame();
ASSERT_VALID(pFrame);
pFrame->RecalcLayout();
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar
void CMenuBar::OnMenuSelect(HMENU hMenu, UINT nIndex)
{
if (m_nTrackingState == popup) {
m_bProcessRightArrow = (::GetSubMenu(hMenu, nIndex) == NULL);
HMENU hSubMenu = ::GetSubMenu(hMenu, m_nCurIndex);
if (hSubMenu == NULL)
return;
m_bProcessLeftArrow = (hMenu == hSubMenu);
}
}
LRESULT CALLBACK CMenuBar::MenuInputFilter(int code, WPARAM wParam, LPARAM lParam)
{
return (
code == MSGF_MENU &&
g_pMenuBar &&
g_pMenuBar->OnMenuInput( *((MSG*)lParam) )
) ? TRUE : CallNextHookEx(g_hMsgHook, code, wParam, lParam);
}
void CMenuBar::TrackPopup(int nIndex)
{
ASSERT_VALID(this);
m_nCurIndex = nIndex;
m_bLoop = TRUE;
while (m_bLoop == TRUE) {
UpdateWindow(); // force to repaint when button hidden by other window
UpdateBar(popup, m_nCurIndex);
// install hook
ASSERT(g_pMenuBar == NULL);
g_pMenuBar = this;
ASSERT(g_hMsgHook == NULL);
m_bLoop = FALSE;
g_hMsgHook = ::SetWindowsHookEx(WH_MSGFILTER,
MenuInputFilter, NULL, AfxGetApp()->m_nThreadID);// m_bLoop may become TRUE
// popup!!
m_nTrackingState = popup;
m_arrItem[m_nCurIndex]->TrackPopup(this);
// uninstall hook
::UnhookWindowsHookEx(g_hMsgHook);
g_hMsgHook = NULL;
g_pMenuBar = NULL;
}
UpdateBar();
}
BOOL CMenuBar::OnMenuInput(MSG& m)
{
ASSERT_VALID(this);
int nMsg = m.message;
CPoint pt = m.lParam;
ScreenToClient(&pt);
switch (nMsg) {
case WM_MOUSEMOVE:
if (pt != m_ptMouse) {
int nIndex = HitTestOnTrack(pt);
if (IsValidIndex(nIndex) && nIndex != m_nCurIndex) {
// defferent button clicked
GetOwner()->PostMessage(WM_CANCELMODE); // destroy popupped menu
UpdateBar(); // clean up
m_nCurIndex = nIndex;
m_bLoop = TRUE; // continue loop
}
m_ptMouse = pt;
}
break;
case WM_LBUTTONDOWN:
if (HitTestOnTrack(pt) != -1 && HitTestOnTrack(pt) == m_nCurIndex) {
// same button clicked
GetOwner()->PostMessage(WM_CANCELMODE); // destroy popupped menu
UpdateBar(button, m_nCurIndex);
return TRUE; // eat it!
}
break;
case WM_KEYDOWN: {
TCHAR vKey = m.wParam;
if (m_dwStyle & CBRS_ORIENT_VERT) { // if vertical
break; // do nothing
}
if ((vKey == VK_LEFT && m_bProcessLeftArrow) ||
(vKey == VK_RIGHT && m_bProcessRightArrow)) {
// no submenu
int nNewIndex = GetNextOrPrevButton(m_nCurIndex, vKey==VK_LEFT);
GetOwner()->PostMessage(WM_CANCELMODE); // destroy popupped menu
UpdateBar();
m_nCurIndex = nNewIndex;
m_bLoop = TRUE; // continue loop
return TRUE; // eat it!
}
}
break;
case WM_SYSKEYDOWN:
// LTRACE(" m_bIgnore = TRUE\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -