Click here to Skip to main content
15,896,545 members
Articles / Desktop Programming / MFC

A ToolBarCtrl Based Menubar Control

Rate me:
Please Sign up or sign in to vote.
4.85/5 (19 votes)
3 Dec 20011 min read 212.8K   4.3K   67  
A ToolBarCtrl based menubar control using CMenuXP
// MenuBarXP.cpp : implementation file
//

#include "stdafx.h"
#include "MenuBarXP.h"
#include "MenuXP.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMenuBarXP

CMenuBarXP::CMenuBarXP()
{
	m_pMenu = NULL;
	m_bTrack = FALSE;
	m_nItemCount = 0;
	m_nPressed = 0;
	m_ptMouse.x = 0;
	m_ptMouse.y = 0;
}

CMenuBarXP::~CMenuBarXP()
{
	if (m_pMenu)
	{
		delete m_pMenu;
	}
}


BEGIN_MESSAGE_MAP(CMenuBarXP, CToolBarCtrl)
	//{{AFX_MSG_MAP(CMenuBarXP)
	ON_WM_LBUTTONDOWN()
	ON_WM_MEASUREITEM()
	ON_WM_MENUCHAR()
	ON_WM_INITMENUPOPUP()
	//}}AFX_MSG_MAP
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, CToolBarXP::OnCustomDraw)
	ON_MESSAGE(MB_POPUPMENU, OnPopupMenu)
	ON_WM_EXITMENULOOP()
	ON_WM_ENTERMENULOOP()
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMenuBarXP message handlers

/////////////////////////////////////////////////////////////////////////////
//Load from a menu, can be a CMenuXP instance or just a CMenu
//the pMenu object must be constructed on the heap, it will
//automatically be destoyed when the MenuBar being destroyed
BOOL CMenuBarXP::LoadMenu(CMenu *pMenu)
{
	if (!m_hWnd)
		return FALSE;

	if (!pMenu)
		return FALSE;

	if (m_pMenu)
		delete m_pMenu;

	m_pMenu = pMenu;

	TRACE("MainMenu Handle: 0x%x\n", m_pMenu->GetSafeHmenu());

	TBBUTTON		tbb; 
	int				i,nStr;
	BOOL			bRet = FALSE;
	char			szText[_MAX_PATH];

	memset(&tbb, 0, sizeof(TBBUTTON));

	tbb.fsState = TBSTATE_ENABLED;
	tbb.fsStyle = TBSTYLE_BUTTON|TBSTYLE_AUTOSIZE;

	m_nItemCount = m_pMenu->GetMenuItemCount();//Count the main menu items
	SetBitmapSize(CSize(0, 0));//no icons for the main menu

	m_ilIcons.Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);

	for(i=0; i<m_nItemCount; i++)
	{
		memset(szText, 0, _MAX_PATH);

		MENUITEMINFO	info;
		memset(&info, 0, sizeof(MENUITEMINFO));
		info.cbSize = sizeof(MENUITEMINFO);
		info.fMask = MIIM_DATA | MIIM_TYPE;
		m_pMenu->GetMenuItemInfo(i, &info, TRUE);
		
		CMenuXPItem *pData = (CMenuXPItem *)info.dwItemData;
		if (pData && (info.fType & MFT_OWNERDRAW))
		{
			strcpy(szText, pData->m_strText);
			if (pData->m_hIcon)
			{
				tbb.iBitmap = m_ilIcons.Add(pData->m_hIcon);
			}
			else
				tbb.iBitmap = -1;
		}
		else
		{
			m_pMenu->GetMenuString(i, szText, 80, MF_BYPOSITION);
		}

		nStr = AddStrings(szText);

		tbb.dwData = NULL;
		tbb.iString = nStr;
		tbb.idCommand = i;

		bRet = AddButtons(1, &tbb);

		if(!bRet)
			return FALSE;
	}

	if (m_ilIcons.GetImageCount()>0)
		SetImageList(&m_ilIcons);

	return bRet;
}

////////////////////////////////////////////////////////////////////////////////
CMenuBarXP*		g_pMenuBar	= NULL;
HHOOK				g_hMsgHook	= NULL;
////////////////////////////////////////////////////////////////////////////////
//	The hook, used to process message when menu is visible
LRESULT CMenuBarXP::MenuInputFilter(int nCode, WPARAM wParam, LPARAM lParam)
{
	MSG* pMsg = (MSG*)lParam;

	if(!g_pMenuBar || nCode!=MSGF_MENU)
		return CallNextHookEx(g_hMsgHook,nCode,wParam,lParam);
	if(g_pMenuBar->OnMenuInput(pMsg))
		return TRUE;
	else
		return CallNextHookEx(g_hMsgHook,nCode,wParam,lParam);
}

//////////////////////////////////////////////////////////////////////////
// Show popupmenu
void CMenuBarXP::TrackPopup()
{
	CMenu	*pSubMenu = m_pMenu->GetSubMenu(m_nPressed);
	if(pSubMenu == NULL)return;

	TRACE("Tracking Menu handle: 0x%x\n", pSubMenu->GetSafeHmenu());

	m_bTrack = TRUE;
	PressButton(m_nPressed,TRUE);


	//Get the rect of the button
	CRect rc;
	GetItemRect(m_nPressed,&rc);
	ClientToScreen(&rc);

	//get the in-screen part of the button
	CRect rcScreen;
	GetDesktopWindow()->GetWindowRect(rcScreen);
	rc.IntersectRect(rc, rcScreen);

	TPMPARAMS tpm;
	tpm.cbSize = sizeof(tpm);
	tpm.rcExclude = rc;

	//Install the hook
	g_pMenuBar = this;
	g_hMsgHook = SetWindowsHookEx(WH_MSGFILTER,
			MenuInputFilter, NULL, GetCurrentThreadId());

	//Show menu
	TrackPopupMenuEx(pSubMenu->GetSafeHmenu(),
				TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_VERTICAL,
				rc.left, rc.bottom, m_hWnd, &tpm);

	PressButton(m_nPressed,FALSE);
	UnhookWindowsHookEx(g_hMsgHook);//Uninstall the hook

	g_hMsgHook = NULL;
	g_pMenuBar = NULL;
}

/////////////////////////////////////////////////////////////////////////////////
//	Actually process messages, called by the hook
BOOL CMenuBarXP::OnMenuInput(MSG* pMsg)
{
	BOOL bResult = FALSE;

	switch(pMsg->message)
	{
	case WM_MOUSEMOVE:
		{
			POINT pt;
			pt.x = LOWORD(pMsg->lParam);
			pt.y = HIWORD(pMsg->lParam);
			ScreenToClient(&pt);
			if(m_ptMouse.x == pt.x && m_ptMouse.y == pt.y)
				return TRUE;
		
			m_ptMouse.x = pt.x;
			m_ptMouse.y = pt.y;

			int nTest = HitTest(&pt);

			if(nTest>=0 && nTest<m_nItemCount && nTest != m_nPressed)
			{
				PressButton(m_nPressed,FALSE);
				SendMessage(WM_CANCELMODE,0,0);
				m_nPressed = nTest;
				PostMessage(MB_POPUPMENU,0,0);
				bResult = TRUE;
			}
		}
		break;
	case WM_LBUTTONDOWN:
		{
			POINT pt;
			pt.x = LOWORD(pMsg->lParam);
			pt.y = HIWORD(pMsg->lParam);
			ScreenToClient(&pt);

			int nTest = HitTest(&pt);

			if(nTest<0)
				m_bTrack = FALSE;
			else if(nTest == m_nPressed)
			{
				m_bTrack = FALSE;
				PostMessage(WM_CANCELMODE,0,0);
				bResult = TRUE;
			}
		}
		break;
	case WM_KEYDOWN:
		{
			TCHAR vkey = pMsg->wParam;
			if(vkey == VK_LEFT)
			{
				PressButton(m_nPressed,FALSE);
				m_nPressed --;
				PostMessage(WM_CANCELMODE,0,0);
				PostMessage(MB_POPUPMENU,0,0);
				bResult = TRUE;
			}
			else if(vkey == VK_RIGHT)
			{
				PressButton(m_nPressed,FALSE);
				m_nPressed ++;
				PostMessage(WM_CANCELMODE,0,0);
				PostMessage(MB_POPUPMENU,0,0);
				bResult = TRUE;
			}
			else if (vkey == VK_ESCAPE)
			{
				PostMessage(WM_CANCELMODE,0,0);
				m_bTrack = FALSE;
				bResult = TRUE;
			}
		}
		break;
	case WM_MENUSELECT:
		GetOwner()->SendMessage(WM_MENUSELECT, pMsg->wParam, pMsg->lParam);
		bResult = TRUE;
		break;
	default:
		break;
	}

	return bResult;
}

void CMenuBarXP::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	int	nTest = HitTest(&point);
	if(nTest<0 || nTest>=m_nItemCount)
		return;

	m_nPressed = nTest;
	TrackPopup();
	
}

LRESULT CMenuBarXP::OnPopupMenu(WPARAM wParam, LPARAM lParam)
{
	if(m_nPressed<0)
		m_nPressed = m_nItemCount - 1;
	if(m_nPressed>=m_nItemCount)
		m_nPressed = 0;
	TrackPopup();

	return 0;
}


void CMenuBarXP::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
{
	// TODO: Add your message handler code here and/or call default
	if (nIDCtl == 0 && m_pMenu)
	{
		m_pMenu->MeasureItem(lpMeasureItemStruct);
	}
	
}

void CMenuBarXP::OnExitMenuLoop(BOOL bIsTrackPopupMenu)
{
	GetOwner()->SendMessage(WM_EXITMENULOOP, (WPARAM)bIsTrackPopupMenu);
}

void CMenuBarXP::OnEnterMenuLoop(BOOL bIsTrackPopupMenu)
{
	GetOwner()->SendMessage(WM_ENTERMENULOOP, (WPARAM)bIsTrackPopupMenu);
}


LRESULT CMenuBarXP::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu) 
{
	return CMenuXP::OnMenuChar(nChar, nFlags, pMenu);
}

//If there's a main menuitem with a mnemonic key nChar, then open it
BOOL CMenuBarXP::OpenMenu(UINT nChar)
{
	int		nCount = (int)m_pMenu->GetMenuItemCount();
	char	szText[80];

	for(int i=0; i<m_nItemCount; i++)
	{
		MENUITEMINFO	info;
		memset(&info, 0, sizeof(MENUITEMINFO));
		info.cbSize = sizeof(MENUITEMINFO);
		info.fMask = MIIM_DATA | MIIM_TYPE;
		m_pMenu->GetMenuItemInfo(i, &info, TRUE);
		
		CMenuXPItem *pData = (CMenuXPItem *)info.dwItemData;
		if (pData && (info.fType & MFT_OWNERDRAW))
		{
			strcpy(szText, pData->m_strText);
		}
		else
		{
			m_pMenu->GetMenuString(i, szText, 80, MF_BYPOSITION);
		}

		CString	text = szText;
		int iAmpersand = text.Find('&');
		if (iAmpersand >=0 && toupper(nChar)==toupper(text[iAmpersand+1]))
		{
			m_nPressed = i;
			PostMessage(MB_POPUPMENU,0,0);
			return TRUE;
		}
	}
	return FALSE;
}

void CMenuBarXP::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) 
{
	CToolBarCtrl::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
	
	// TODO: Add your message handler code here
	GetOwner()->SendMessage(WM_INITMENUPOPUP, (WPARAM)pPopupMenu->GetSafeHmenu(), MAKELONG(nIndex, bSysMenu));
}



By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
China China
I'm a chinese programer living in Shanghai, currently working for a software company whose main business is to deliver computer based testing. Software simulation for computer based testing and certifications is my main responsibility in this company. Execpt for software development, I like out-door activities and photography. I am willing to make friends in China and all over the world, so contact me if you have anything in common with meSmile | :)

Comments and Discussions