// CJMenuBar.cpp : implementation file
// Copyright � 1998-1999 CodeJock.com, All Rights Reserved.
// See ReadMe.txt for TERMS OF USE.
//
// Based on the code written by Paul DiLascia for MSJ.
//
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
*
* $Date: 10/24/99 9:12p $
* $Revision: 11 $
* $Archive: /CodeJock/CJLibrary/CJMenuBar.cpp $
*
* $History: CJMenuBar.cpp $
*
* ***************** Version 11 *****************
* User: Kirk Stowell Date: 10/24/99 Time: 9:12p
* Updated in $/CodeJock/CJLibrary
* Fixed potential resource and memory leak problems.
*
* ***************** Version 10 *****************
* User: Kirk Stowell Date: 8/31/99 Time: 1:11a
* Updated in $/CodeJockey/CJLibrary
* Updated copyright and contact information.
*
* ***************** Version 9 *****************
* User: Kirk Stowell Date: 8/29/99 Time: 9:36p
* Updated in $/CodeJockey/CJLibrary
* Fixed bug with left arrow tracking - Tim Lewis <timlewis@home.com>
*
* ***************** Version 8 *****************
* User: Kirk Stowell Date: 8/29/99 Time: 9:14p
* Updated in $/CodeJockey/CJLibrary
* Added Unicode compliance, thanks to Barry Burton for his help with
* this.
*
*
* ***************** Version 7 *****************
* User: Kirk Stowell Date: 6/23/99 Time: 12:04a
* Updated in $/CodeJockey/CJLibrary
*
* ***************** Version 6 *****************
* User: Kirk Stowell Date: 7/25/99 Time: 10:00p
* Updated in $/CodeJockey/CJLibrary
*
* ***************** Version 5 *****************
* User: Kirk Stowell Date: 7/25/99 Time: 12:30a
* Updated in $/CodeJockey/CJLibrary
*
* ***************** Version 4 *****************
* User: Kirk Stowell Date: 7/25/99 Time: 12:15a
* Updated in $/CodeJockey/CJLibrary
*
* ***************** Version 3 *****************
* User: Kirk Stowell Date: 6/23/99 Time: 12:33a
* Updated in $/CodeJockey/CJLibrary
*
* ***************** Version 2 *****************
* User: Kirk Stowell Date: 7/18/99 Time: 10:28p
* Updated in $/CodeJockey/CJLibrary
* Further cleanup to handle CCJMenu class.
*
* ***************** Version 1 *****************
* User: Kirk Stowell Date: 7/15/99 Time: 12:34a
* Created in $/CodeJockey/CJLibrary
* Initial creation. Extended the original class written by Paul DiLascia
* for MSJ. Class uses CCJMenu and fixes some bugs with the original
* class.
*
***************************************************************************/
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "CJMenuBar.h"
#include "CJMenu.h" // CCJMenu class declaration
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CCJMenuBar
CCJMenuBar::CCJMenuBar()
{
m_iTrackingState = TRACK_NONE; // initial state: not tracking
m_iPopupTracking = -1;
m_iNewPopup = -1; // invalid
m_bAutoRemoveFrameMenu = TRUE; // set frame's menu to NULL
}
CCJMenuBar::~CCJMenuBar()
{
// fix potential resource leak - KStowell - 10-15-99
m_Font.DeleteObject();
}
IMPLEMENT_DYNAMIC(CCJMenuBar, CCJToolBar)
BEGIN_MESSAGE_MAP(CCJMenuBar, CCJToolBar)
//{{AFX_MSG_MAP(CCJMenuBar)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_SIZE()
ON_UPDATE_COMMAND_UI_RANGE(0, 256, OnUpdateMenuButton)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BOOL CCJMenuBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
{
return CreateEx(pParentWnd, NULL, dwStyle,
CRect(m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder), nID);
}
BOOL CCJMenuBar::CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle, DWORD dwStyle, CRect rcBorders, UINT nID)
{
if( !CCJToolBar::CreateEx( pParentWnd, dwCtrlStyle, dwStyle, rcBorders, nID))
return FALSE;
UpdateFont();
m_frameHook.Install(this, *pParentWnd);
SetWindowText(_T("Menu Bar"));
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CCJMenuBar message handlers
CSize CCJMenuBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
if( m_bInReBar ) {
return CCJToolBar::CalcFixedLayout(bStretch, bHorz);
}
else
{
CSize size = CCJToolBar::CalcFixedLayout(bStretch, bHorz);
CRect rcClient;
GetParentFrame()->GetClientRect(&rcClient);
if( bHorz ) {
size.cx = rcClient.Width();
}
else {
size.cy = rcClient.Height();
}
return size;
}
}
//////////////////
// Set menu bar font from current system menu font
//
void CCJMenuBar::UpdateFont()
{
m_Font.DeleteObject();
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
VERIFY(m_Font.CreateFontIndirect(&ncm.lfMenuFont));
SetFont(&m_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 CCJMenuBar::OnUpdateMenuButton(CCmdUI* pCmdUI)
{
ASSERT_VALID(this);
if (IsValidButton(pCmdUI->m_nID))
pCmdUI->Enable(TRUE);
}
//////////////////
// Recompute layout of menu bar
//
void CCJMenuBar::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 CCJMenuBar::RecomputeToolbarSize()
{
// Force toolbar to recompute size
CFrameWnd* pFrame = (CFrameWnd*)GetParentFrame();
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 CCJMenuBar::SetTrackingState(TRACKINGSTATE iState, int iButton)
{
ASSERT_VALID(this);
if (iState != m_iTrackingState) {
if (iState == TRACK_NONE)
iButton = -1;
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 CCJMenuBar::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 CCJMenuBar::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 CCJMenuBar::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 menu from resource
//
BOOL CCJMenuBar::LoadMenu(UINT nID)
{
CFrameWnd* pFrame = GetParentFrame();
ASSERT_VALID(pFrame);
pFrame->GetMenu()->DestroyMenu();
pFrame->SetMenu(NULL);
CMenu menu;
menu.LoadMenu(nID);
m_nMenuID = nID;
// delete existing buttons
int nCount = GetButtonCount();
while (nCount--) {
VERIFY(DeleteButton(0));
}
DWORD dwStyle = GetStyle();
BOOL bModifyStyle = ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT);
SetImageList(NULL);
UINT nMenuItems = menu.GetMenuItemCount();
for( UINT i = 0; i < nMenuItems; i++ )
{
CString strName;
if( menu.GetMenuString(i, strName, MF_BYPOSITION ))
{
TBBUTTON tbb;
memset( &tbb, 0, sizeof( TBBUTTON ));
tbb.idCommand = menu.GetMenuItemID(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] == strName) {
iString = j; // found it
break;
}
}
if (iString <0) {
// string not found: add it
iString = AddStrings(strName);
m_arStrings.SetAtGrow(iString, strName);
}
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( ::IsMenu( menu )) {
AutoSize(); // size buttons
RecomputeToolbarSize(); // and menubar itself
}
menu.DestroyMenu();
return TRUE;
}
//////////////////
// Handle mouse click: if clicked on button, press it
// and go into main menu loop.
//
void CCJMenuBar::OnLButtonDown(UINT nFlags, CPoint pt)
{
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 CCJMenuBar::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 CCJMenuBar::OnSize(UINT nType, int cx, int cy)
{
CCJToolBar::OnSize(nType, cx, cy);
RecomputeMenuLayout();
}
//////////////////
// Bar style changed: eg, moved from left to right dock or floating
//
void CCJMenuBar::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 CCJMenuBar::OnMenuSelect(HMENU hmenu, UINT iItem)
{
if (m_iTrackingState > 0) {
// process right-arrow if item is NOT a submenu
m_bProcessRightArrow = (::GetSubMenu(hmenu, iItem) == NULL);
// process left-arrow if curent menu is one I'm tracking
m_bProcessLeftArrow = TRUE;
}
}
// globals--yuk! But no other way using windows hooks.
//
static CCJMenuBar* g_pMenuBar = NULL;
static HHOOK g_hMsgHook = NULL;
////////////////
// Menu filter hook just passes to virtual CCJMenuBar function
//
LRESULT CALLBACK
CCJMenuBar::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 CCJMenuBar::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 = static_cast<TCHAR>(m.wParam);
if ((vkey == VK_LEFT && m_bProcessLeftArrow) ||
(vkey == VK_RIGHT && m_bProcessRightArrow)) {
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
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 CCJMenuBar::CancelMenuAndTrackNewOne(int iNewPopup)
{
ASSERT_VALID(this);
if (iNewPopup != m_iPopupTracking) {
GetParentFrame()->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 CCJMenuBar::TrackPopup(int iButton)
{
ASSERT_VALID(this);
m_popupMenu.LoadMenu(m_nMenuID);
m_popupMenu.LoadToolbar(m_nMenuID);
ASSERT(::IsMenu(m_popupMenu));
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
GetParentFrame()->PostMessage(WM_KEYDOWN, VK_DOWN, 1);
GetParentFrame()->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);
CCJMenu* pPopupMenu = (CCJMenu*)m_popupMenu.GetSubMenu(iButton);
pPopupMenu->TrackPopupMenu( TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL,
pt.x, pt.y, GetParentFrame());
// 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;
}
// fix potential resource leak - KStowell - 10-15-99
m_popupMenu.DestroyMenu();
}
//////////////////
// 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 CCJMenuBar::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 CCJMenuBar::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
SetTrackingState(TRACK_NONE);
}
} else if (msg==WM_SYSKEYDOWN || msg==WM_SYSKEYUP || msg==WM_KEYDOWN) {
TCHAR vkey = static_cast<TCHAR>(pMsg->wParam); // get virt key
BOOL bAlt = HIWORD(pMsg->lParam) & KF_ALTDOWN; // Alt key down
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) {
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
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
TrackPopup(GetHotItem());
return TRUE;
case VK_ESCAPE:
// escape key: exit tracking mode
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)) {
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) {
SetTrackingState(TRACK_NONE);
}
}
}
return FALSE; // not handled, pass along
}
#ifdef _DEBUG
void CCJMenuBar::AssertValid() const
{
CCJToolBar::AssertValid();
ASSERT(TRACK_NONE<=m_iTrackingState && m_iTrackingState<=TRACK_POPUP);
m_frameHook.AssertValid();
}
void CCJMenuBar::Dump(CDumpContext& dc) const
{
CCJToolBar::Dump(dc);
}
#endif
//////////////////////////////////////////////////////////////////
// CCJMenuBarFrameHook 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 CCJMenuBarFrameHook in CCJMenuBar: one for
// the frame and one for the MDI client window.
//
CCJMenuBarFrameHook::CCJMenuBarFrameHook()
{
}
CCJMenuBarFrameHook::~CCJMenuBarFrameHook()
{
HookWindow((HWND)NULL); // (unhook)
}
//////////////////
// Install hook to trap window messages sent to frame or MDI client.
//
BOOL CCJMenuBarFrameHook::Install(CCJMenuBar* pMenuBar, HWND hWndToHook)
{
ASSERT_VALID(pMenuBar);
m_pMenuBar = pMenuBar;
return HookWindow(hWndToHook);
}
//////////////////////////////////////////////////////////////////
// Trap frame/MDI client messages specific to menubar.
//
LRESULT CCJMenuBarFrameHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
CCJMenuBar& 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);
}