📄 menubar.cpp
字号:
/////////////////////////////////////////////////////////////////////////////
// MenuBar.cpp: implementation of the CMenuBar class.
//
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001 by Nikolay Denisov. All rights reserved.
//
// This code is free for personal and commercial use, providing this
// notice remains intact in the source files and all eventual changes are
// clearly marked with comments.
//
// You must obtain the author's consent before you can include this code
// in a software library.
//
// No warrantee of any kind, express or implied, is included with this
// software; use at your own risk, responsibility for damages (if any) to
// anyone resulting from the use of this software rests entirely with the
// user.
//
// Please email bug reports, bug fixes, enhancements, requests and
// comments to: nick@actor.ru
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CommonRes.h"
#include "MenuBar.h"
#include "SizableReBar.h"
#include "WinAppEx.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
/////////////////////////////////////////////////////////////////////////////
// CMenuBarButton
CMenuBarButton::CMenuBarButton()
{
m_bHidden = true;
m_bPushed = false;
SetMDIChild( 0 );
SetMenuBarRect( 0 );
}
bool CMenuBarButton::HitTest( CPoint pt ) const
{
return ( IsVisible() && GetButtonRect().PtInRect( pt ) );
}
void CMenuBarButton::SetMDIChild( HWND hWndMDIChild )
{
m_hWndMDIChild = hWndMDIChild;
}
void CMenuBarButton::SetMenuBarRect( LPCRECT lpRect )
{
if ( lpRect != 0 )
{
m_rcMenuBar = *lpRect;
}
else
{
m_rcMenuBar.SetRectEmpty();
}
}
DWORD CMenuBarButton::GetMDIChildStyle() const
{
return ( DWORD )::GetWindowLong( m_hWndMDIChild, GWL_STYLE );
}
bool CMenuBarButton::IsEnabled() const
{
return true;
}
bool CMenuBarButton::IsVisible() const
{
return ( !m_bHidden && ::IsWindow( m_hWndMDIChild ) && ( GetMDIChildStyle() & WS_SYSMENU ) );
}
bool CMenuBarButton::IsPushed() const
{
return m_bPushed;
}
bool CMenuBarButton::HideButton( bool bHide )
{
bool bWasHidden = m_bHidden;
m_bHidden = bHide;
return bWasHidden;
}
bool CMenuBarButton::PushButton( bool bPush )
{
bool bWasPushed = m_bPushed;
m_bPushed = bPush;
return bWasPushed;
}
CSize CMenuBarButton::GetButtonSize()
{
// Sys-menu icon and caption buttons are all inside
// a rectangle of the following size:
const NONCLIENTMETRICS& info = CWinAppEx::GetInstance()->GetNonClientMetrics();
return CSize(
info.iMenuWidth, //::GetSystemMetrics( SM_CXMENUSIZE ),
info.iMenuHeight ); //::GetSystemMetrics( SM_CYMENUSIZE ) );
}
void CMenuBarButton::DrawButton( CDC* pDC )
{
if ( IsVisible() )
{
UINT nState;
switch ( GetSysCommandID() )
{
case SC_CLOSE:
nState = DFCS_CAPTIONCLOSE;
break;
case SC_MINIMIZE:
nState = DFCS_CAPTIONMIN;
break;
case SC_MAXIMIZE:
nState = DFCS_CAPTIONMAX;
break;
case SC_RESTORE:
nState = DFCS_CAPTIONRESTORE;
break;
default:
ASSERT( false );
return;
}
CRect rc = GetButtonRect();
VERIFY( pDC->DrawFrameControl( rc, DFC_CAPTION, nState |
( IsPushed() ? DFCS_PUSHED : 0 ) |
( IsEnabled() ? 0 : DFCS_INACTIVE ) ) );
}
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBarButtonMin
CRect CMenuBarButtonMin::GetButtonRect() const
{
// Minimize box has 2 pixel border on all sides but right
CSize szButton = GetButtonSize();
szButton.cx -= 2;
szButton.cy -= 4;
CPoint ptTopLeft;
ptTopLeft.x = m_rcMenuBar.right - ( szButton.cx + 2 ) * 3 + 2;
ptTopLeft.y = m_rcMenuBar.top + ( m_rcMenuBar.Height() - szButton.cy ) / 2;
return CRect( ptTopLeft, szButton );
}
UINT CMenuBarButtonMin::GetSysCommandID() const
{
return ::IsIconic( m_hWndMDIChild ) ? SC_RESTORE : SC_MINIMIZE;
}
bool CMenuBarButtonMin::IsEnabled() const
{
return ( ( GetMDIChildStyle() & WS_MINIMIZEBOX ) != 0 );
}
bool CMenuBarButtonMin::IsVisible() const
{
return CMenuBarButton::IsVisible() &&
( ( GetMDIChildStyle() & ( WS_MINIMIZEBOX | WS_MAXIMIZEBOX ) ) != 0 );
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBarButtonMax
CRect CMenuBarButtonMax::GetButtonRect() const
{
// Max box has a 2 pixel border on all sides but left, which is zero
CSize szButton = GetButtonSize();
szButton.cx -= 2;
szButton.cy -= 4;
CPoint ptTopLeft;
ptTopLeft.x = m_rcMenuBar.right - ( szButton.cx + 2 ) * 2;
ptTopLeft.y = m_rcMenuBar.top + ( m_rcMenuBar.Height() - szButton.cy ) / 2;
return CRect( ptTopLeft, szButton );
}
UINT CMenuBarButtonMax::GetSysCommandID() const
{
return ::IsZoomed( m_hWndMDIChild ) ? SC_RESTORE : SC_MAXIMIZE;
}
bool CMenuBarButtonMax::IsEnabled() const
{
return ( ( GetMDIChildStyle() & WS_MAXIMIZEBOX ) != 0 );
}
bool CMenuBarButtonMax::IsVisible() const
{
return CMenuBarButton::IsVisible() &&
( ( GetMDIChildStyle() & ( WS_MINIMIZEBOX | WS_MAXIMIZEBOX ) ) != 0 );
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBarButtonClose
CRect CMenuBarButtonClose::GetButtonRect() const
{
// Close box has a 2 pixel border on all sides but left, which is zero
CSize szButton = GetButtonSize();
szButton.cx -= 2;
szButton.cy -= 4;
CPoint ptTopLeft;
ptTopLeft.x = m_rcMenuBar.right - ( szButton.cx + 2 );
ptTopLeft.y = m_rcMenuBar.top + ( m_rcMenuBar.Height() - szButton.cy ) / 2;
return CRect( ptTopLeft, szButton );
}
UINT CMenuBarButtonClose::GetSysCommandID() const
{
return SC_CLOSE;
}
/////////////////////////////////////////////////////////////////////////////
// CMenuBar
#define IDBUTTON_FIRST 65000
#define IDBUTTON_SYSMENU 65000
#define IDBUTTON_LAST 65100
// Static member variables
HHOOK CMenuBar::m_hMsgHook = 0;
CMenuBar* CMenuBar::m_pMenuBar = 0;
IMPLEMENT_DYNAMIC( CMenuBar, CToolBar )
CMenuBar::CMenuBar()
{
m_hWndMDIChild = 0;
m_hWndOldFocus = 0;
m_hMenu = 0;
m_hMenuTracking = 0;
m_bItemTracking = false;
m_bItemDropped = false;
m_bButtonCapture = false;
m_bFrameActive = true;
// Create menu bar buttons
m_aMenuBarButtons.Add( new CMenuBarButtonMin );
m_aMenuBarButtons.Add( new CMenuBarButtonMax );
m_aMenuBarButtons.Add( new CMenuBarButtonClose );
}
CMenuBar::~CMenuBar()
{
if ( m_fontMenu.GetSafeHandle() != 0 )
{
VERIFY( m_fontMenu.DeleteObject() );
}
for ( int nIndex = 0; nIndex <= m_aMenuBarButtons.GetUpperBound(); nIndex++ )
{
delete m_aMenuBarButtons[ nIndex ];
}
m_aMenuBarButtons.RemoveAll();
}
/////////////////////////////////////////////////////////////////////////////
// Operations
bool CMenuBar::SetMenu( HMENU hMenu )
{
// Delete old buttons
while ( m_nCount > 0 )
{
VERIFY( GetToolBarCtrl().DeleteButton( --m_nCount ) );
}
CMenu* pMenu = CMenu::FromHandle( hMenu );
if ( pMenu != 0 )
{
// Allocate space for buttons
UINT nItems = pMenu->GetMenuItemCount();
VERIFY( SetButtons( 0, nItems + 1 ) );
// Add sys-menu button which is the leftmost
SetButtonInfo( 0, IDBUTTON_SYSMENU,
TBBS_BUTTON | TBBS_DROPDOWN | TBBS_NOPREFIX, 0 );
// Add all other buttons
for ( UINT nIndex = 0; nIndex < nItems; nIndex++ )
{
UINT nID = pMenu->GetMenuItemID( nIndex );
UINT nStyle = TBBS_BUTTON | TBBS_AUTOSIZE | TBBS_DROPDOWN;
switch ( nID )
{
case -1:
nID = IDBUTTON_SYSMENU + 1 + nIndex;
break;
case 0:
nStyle = TBBS_SEPARATOR;
break;
default:
nStyle ^= TBBS_DROPDOWN;
ASSERT( false ); // not supported yet
break;
}
CString strMenu;
pMenu->GetMenuString( nIndex, strMenu, MF_BYPOSITION );
SetButtonInfo( nIndex + 1, nID, nStyle, 0 /*-2=I_IMAGENONE*/ );
VERIFY( SetButtonText( nIndex + 1, strMenu ) );
}
}
UpdateMenuBar();
m_hMenu = hMenu;
return true;
}
HMENU CMenuBar::GetMenu() const
{
return m_hMenu;
}
/////////////////////////////////////////////////////////////////////////////
// Implementation
CReBarCtrl& CMenuBar::GetParentReBarCtrl() const
{
return STATIC_DOWNCAST( CReBar, GetParent() )->GetReBarCtrl();
}
int CMenuBar::GetParentBandIndex() const
{
CReBarCtrl *ass=(CReBarCtrl *)&GetParentReBarCtrl();
int nBand = GetParentReBarCtrl().IDToIndex( ( UINT )GetDlgCtrlID() );
ASSERT( nBand != -1 );
return nBand;
}
void CMenuBar::SetButtonWidth( UINT nID, int nWidth )
{
TBBUTTONINFO tbbi;
tbbi.cbSize = sizeof( tbbi );
tbbi.dwMask = TBIF_SIZE;
tbbi.cx = ( WORD )nWidth;
VERIFY( GetToolBarCtrl().SetButtonInfo( nID, &tbbi ) );
}
void CMenuBar::UpdateMenuBar()
{
// Set new font
if ( m_fontMenu.GetSafeHandle() != 0 )
{
VERIFY( m_fontMenu.DeleteObject() );
}
VERIFY( m_fontMenu.CreateFontIndirect(
&CWinAppEx::GetInstance()->GetNonClientMetrics().lfMenuFont ) );
SetFont( &m_fontMenu, TRUE );
// Calc row height
int cyMenu = ::GetSystemMetrics( SM_CYMENU );
int cyRow = cyMenu + HIWORD( SendMessage( TB_GETPADDING ) );
CToolBarCtrl& tbCtrl = GetToolBarCtrl();
VERIFY( tbCtrl.SetBitmapSize( CSize( 0, cyMenu ) ) );
// Adjust appearance of sys-menu button
bool bSysMenu = ( m_hWndMDIChild != 0 );
int cxButton = bSysMenu ? CMenuBarButton::GetButtonSize().cx : 0;
if ( GetCount() > 0 )
{
VERIFY( tbCtrl.HideButton( IDBUTTON_SYSMENU, !bSysMenu ) );
if ( bSysMenu )
{
// Adjust sys-menu button width
SetButtonWidth( IDBUTTON_SYSMENU, cxButton );
}
}
// Calc minimal and ideal width of the menu bar
int cxIdeal = cxButton * 3;
for ( int nIndex = 0; nIndex < GetCount(); nIndex++ )
{
CRect rcItem;
if ( tbCtrl.GetItemRect( nIndex, rcItem ) )
{
cxIdeal += rcItem.Width();
}
}
// Our parent must be a re-bar control by design.
// So, minimal and ideal width as well as minimal height of
// the parent band should also be adjusted accordingly.
REBARBANDINFO rbbi;
rbbi.cbSize = sizeof( rbbi );
rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE;
rbbi.cxMinChild = cxButton;
rbbi.cyMinChild = cyRow;
rbbi.cxIdeal = cxIdeal;
VERIFY( GetParentReBarCtrl().SetBandInfo( GetParentBandIndex(), &rbbi ) );
CRect rcMenuBar;
GetClientRect( rcMenuBar );
RepositionSysButtons( rcMenuBar );
}
void CMenuBar::RepositionSysButtons( CRect rcMenuBar )
{
CRect rcItem;
GetToolBarCtrl().GetItemRect( GetCount() - 1, rcItem );
int cxAvailable = ( rcMenuBar.right - rcItem.right );
int cxNeeded = ( CMenuBarButton::GetButtonSize().cx * 3 );
if ( cxAvailable < cxNeeded )
{
// Without this sys-buttons would overlap menu items
rcMenuBar.right = rcItem.right + cxNeeded;
}
for ( int nIndex = 0; nIndex <= m_aMenuBarButtons.GetUpperBound(); nIndex++ )
{
CMenuBarButton* pButton = m_aMenuBarButtons[ nIndex ];
pButton->SetMenuBarRect( rcMenuBar );
pButton->HideButton( m_hWndMDIChild == 0 );
}
Invalidate();
}
void CMenuBar::EnterTrackingMode( int nItem )
{
if ( !m_bItemTracking )
{
m_bItemTracking = true;
m_hWndOldFocus = 0;
// Gain focus
if ( GetFocus() != this )
{
m_hWndOldFocus = SetFocus()->GetSafeHwnd();
}
// Capture mouse
if ( GetCapture() != this )
{
SetCapture();
SendMessage( WM_SETCURSOR, ( WPARAM )m_hWnd, MAKELPARAM( HTCLIENT, 0 ) );
}
GetToolBarCtrl().SetHotItem( nItem );
}
}
void CMenuBar::TrackChevronMenu( CRect& rcChevron, int nItem )
{
ExitTrackingMode();
CMenu* pMenu = CMenu::FromHandle( m_hMenu );
if ( pMenu != 0 )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -