www.pudn.com > ODBCApi.rar > MenuXP.cpp


// CustMenu.cpp: implementation of the CMenuXP class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "MenuXP.h" 
#include "KeyHelper.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
// constants used for drawing 
const CXGAP = 0;				// num pixels between button and text 
const CXTEXTMARGIN = 2;		// num pixels after hilite to start text 
const CXBUTTONMARGIN = 2;	// num pixels wider button is than bitmap 
const CYBUTTONMARGIN = 2;	// ditto for height 
 
// DrawText flags 
const DT_MYSTANDARD = DT_SINGLELINE|DT_LEFT|DT_VCENTER; 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
IMPLEMENT_DYNAMIC(CMenuXP, CMenu) 
 
CMenuXP::CMenuXP() 
{ 
	//initialize menu font with the default 
	NONCLIENTMETRICS info; 
	info.cbSize = sizeof(info); 
	SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); 
	VERIFY(m_fontMenu.CreateFontIndirect(&info.lfMenuFont)); 
 
	//initialize colors with system default 
	m_clrBackGround = ::GetSysColor(COLOR_MENU); 
	m_clrSelectedBar = ::GetSysColor(COLOR_HIGHLIGHT); 
	m_clrSelectedText = ::GetSysColor(COLOR_HIGHLIGHTTEXT); 
	m_clrText = ::GetSysColor(COLOR_MENUTEXT); 
	m_clrDisabledText = ::GetSysColor(COLOR_GRAYTEXT); 
	m_clrIconArea = m_clrBackGround; 
 
	//initialize sidebar colors 
	m_clrSideBarStart = RGB(0, 0, 192); 
	m_clrSideBarEnd = RGB(0, 0, 0); 
 
	//the default sytle is office style 
	m_Style = STYLE_OFFICE; 
 
	m_bBreak = false; 
	m_bBreakBar = false; 
} 
 
CMenuXP::~CMenuXP() 
{ 
	m_fontMenu.DeleteObject(); 
	Clear(); 
} 
 
 
void CMenuXP::MeasureItem( LPMEASUREITEMSTRUCT lpms ) 
{ 
	if (lpms->CtlType != ODT_MENU) 
		return; 
 
	CMenuXPItem	*pItem = (CMenuXPItem *)lpms->itemData; 
	TRACE("pItem: 0x%x",(DWORD)pItem);	//This line prevent boundschecker from issue a resource leak 
 
	if (!pItem || !pItem->IsMyData()) 
		return; 
 
	if (pItem->m_bSideBar) 
	{ 
		lpms->itemWidth = pItem->m_nSize; 
		lpms->itemHeight = 0; 
	} 
	else if (pItem->m_bSeparator) 
	{ 
		// separator: use half system height and zero width 
		lpms->itemHeight = ::GetSystemMetrics(SM_CYMENUCHECK)>>1; 
		lpms->itemWidth  = 0; 
	} 
	else 
	{ 
		//calculate the size needed to draw the text: use DrawText with DT_CALCRECT 
 
		CWindowDC dc(NULL);	// screen DC--I won't actually draw on it 
		CRect rcText(0,0,0,0); 
		CFont* pOldFont = dc.SelectObject(&m_fontMenu); 
		dc.DrawText(pItem->m_strText, rcText, DT_MYSTANDARD|DT_CALCRECT); 
		dc.SelectObject(pOldFont); 
 
		// the height of the item should be the maximun of the text and the button 
		lpms->itemHeight = max(rcText.Height(), pItem->m_nSize + (CYBUTTONMARGIN<<1)); 
 
		if (pItem->m_bButtonOnly) 
		{	//for button only style, we set the item's width to be the same as its height 
			lpms->itemWidth = lpms->itemHeight; 
		} 
		else 
		{ 
			// width is width of text plus a bunch of stuff 
			int cx = rcText.Width();	// text width  
			cx += CXTEXTMARGIN<<1;		// L/R margin for readability 
			cx += CXGAP;					// space between button and menu text 
			cx += (pItem->m_nSize + CYBUTTONMARGIN * 2) <<1;		// button width (L=button; R=empty margin) 
 
			lpms->itemWidth = cx;		// done deal 
		} 
	} 
	 
	// whatever value I return in lpms->itemWidth, Windows will add the 
	// width of a menu checkmark, so I must subtract to defeat Windows. Argh. 
	// 
	lpms->itemWidth -= GetSystemMetrics(SM_CXMENUCHECK)-1; 
 
	TRACE("MeasureItem: ID(%d), Width(%d), Height(%d)\n",  
		lpms->itemID, 
		lpms->itemWidth, lpms->itemHeight); 
 
} 
 
void CMenuXP::DrawItem( LPDRAWITEMSTRUCT lpds ) 
{ 
	ASSERT(lpds); 
	if (lpds->CtlType != ODT_MENU) 
		return; // not handled by me 
	CMenuXPItem * pItem = (CMenuXPItem *)lpds->itemData; 
	if (!pItem) 
		return; 
	 
	ASSERT(lpds->itemAction != ODA_FOCUS); 
	ASSERT(lpds->hDC); 
	CDC dc; 
	dc.Attach(lpds->hDC); 
 
	//get the drawing area 
	CRect rcItem = lpds->rcItem; 
 
	TRACE("DrawItem: ID(%d), Widht(%d),  Height(%d)\n",  
		lpds->itemID, rcItem.Width(), rcItem.Height()); 
 
	if (pItem->m_bSideBar) 
	{ 
		CRect rcClipBox; 
		dc.GetClipBox(rcClipBox); 
		//before drawing the sidebar, we must fill the entire menu area with its backgroundcolor, 
		//orelse, the breakbar area will remain the the default menu color 
		//so, if you want to avoid strange color and don't want a sidebar, just add a sidebar with  
		//zero width 
		//dc.FillSolidRect(rcClipBox, m_Style==STYLE_XP? m_clrIconArea : m_clrBackGround); 
		 
		//draw the side bar 
		CRect rc = rcItem; 
		rc.top = rcClipBox.top; 
		rc.bottom = rcClipBox.bottom; 
		DrawSideBar(&dc, rc, pItem->m_hIcon, pItem->m_strText); 
	} 
	else if (pItem->m_bSeparator)  
	{ 
		//draw background first 
		DrawBackGround(&dc, rcItem, FALSE, FALSE); 
		// draw the background 
		CRect rc = rcItem;								// copy rect 
		rc.top += rc.Height()>>1;						// vertical center 
		dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP);		// draw separator line 
		 
		// in XP mode, fill the icon area with the iconarea color 
		if (m_Style == STYLE_XP) 
		{ 
			CRect rcArea(rcItem.TopLeft(), 
				CSize(pItem->m_nSize + (CYBUTTONMARGIN<<1),  
				pItem->m_nSize + (CYBUTTONMARGIN<<1))); 
			DrawIconArea(&dc, rcArea, FALSE, FALSE, FALSE); 
		} 
	}  
	else 
	{ 
		BOOL bDisabled = lpds->itemState & ODS_GRAYED; 
		BOOL bSelected = lpds->itemState & ODS_SELECTED; 
		BOOL bChecked  = lpds->itemState & ODS_CHECKED; 
 
		//draw the background first 
		DrawBackGround(&dc, rcItem, bSelected, bDisabled); 
		 
		//Draw the icon area for XP style 
		if (m_Style == STYLE_XP) 
		{ 
			CRect rcArea(rcItem.TopLeft(), CSize(rcItem.Height(), rcItem.Height())); 
			DrawIconArea(&dc, rcArea, bSelected, bDisabled, bChecked); 
		} 
 
		//draw the button, not the icon 
		CRect rcButton(rcItem.TopLeft(), CSize(rcItem.Height(), rcItem.Height())); 
		if (pItem->m_bButtonOnly) 
			rcButton = rcItem; 
		if (pItem->m_hIcon || bChecked) 
		{ 
			DrawButton(&dc, rcButton, bSelected, bDisabled, bChecked); 
		} 
		//draw the icon actually 
		if (pItem->m_hIcon) 
		{ 
			CRect	rcIcon = rcButton; 
			rcIcon.DeflateRect(2, 2); 
			DrawIcon(&dc, rcIcon, pItem->m_hIcon, bSelected, bDisabled); 
		} 
		else if (bChecked)	 
		{ 
			//draw the check mark 
			CRect	rcCheck = rcButton; 
			rcCheck.DeflateRect(2, 2); 
			DrawCheckMark(&dc, rcCheck, bSelected); 
		} 
 
		//draw text finally 
		if (!pItem->m_bButtonOnly) 
		{ 
			CRect rcText = rcItem;				 // start w/whole item 
			rcText.left += rcButton.Width() + CXGAP + CXTEXTMARGIN; // left margin 
			rcText.right -= pItem->m_nSize;				 // right margin 
			DrawText(&dc, rcText, pItem->m_strText, bSelected, bDisabled, lpds->itemState&ODS_DEFAULT ? 1 : 0); 
		} 
 
	} 
 
	dc.Detach(); 
} 
 
//draw background 
void CMenuXP::DrawBackGround(CDC *pDC, CRect rect, BOOL bSelected, BOOL bDisabled) 
{ 
	if (bSelected) 
	{ 
		FillRect(pDC, rect, bDisabled? ((m_Style==STYLE_XP)?m_clrBackGround:m_clrSelectedBar) : m_clrSelectedBar); 
	} 
	else 
	{ 
		FillRect(pDC, rect, m_clrBackGround); 
	} 
 
	//in XP mode, draw a line rectangle around 
	if (m_Style == STYLE_XP && bSelected && !bDisabled) 
	{ 
		CGdiObject *pOldBrush = pDC->SelectStockObject(HOLLOW_BRUSH); 
		CGdiObject	*pOldPen = pDC->SelectStockObject(BLACK_PEN); 
		pDC->Rectangle(rect); 
		pDC->SelectObject(pOldBrush); 
		pDC->SelectObject(pOldPen); 
	} 
} 
 
//draw the icon button, the icon is not included 
void CMenuXP::DrawButton(CDC *pDC, CRect rect, BOOL bSelected, BOOL bDisabled, BOOL bChecked) 
{ 
	if (m_Style == STYLE_OFFICE) 
	{ 
		// normal: fill BG depending on state 
		FillRect(pDC, rect, (bChecked && !bSelected) ? m_clrBackGround+RGB(2, 2, 2) : m_clrBackGround); 
	 
		// draw pushed-in or popped-out edge 
		if (!bDisabled && (bSelected || bChecked) ) 
		{ 
			pDC->DrawEdge(rect, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER, 
				BF_RECT); 
		} 
	} 
	else if (m_Style == STYLE_XP && !bSelected) 
	{ 
		if (bChecked && !bDisabled) 
		{ 
			DrawBackGround(pDC, rect, TRUE, FALSE); 
		} 
	} 
	 
} 
 
//draw the icon area, the icon is not included, only in XP style 
void CMenuXP::DrawIconArea(CDC *pDC, CRect rect, BOOL bSelected, BOOL bDisabled, BOOL bChecked) 
{ 
	if (m_Style != STYLE_XP) 
		return; 
 
	// normal: fill BG depending on state 
	if (!bSelected || bDisabled) 
	{ 
		FillRect(pDC, rect, m_clrIconArea); 
	} 
} 
 
//draw the icon 
void CMenuXP::DrawIcon(CDC *pDC, CRect rect, HICON hIcon, BOOL bSelected, BOOL bDisabled) 
{ 
	if (bDisabled) 
	{ 
		DrawEmbossed(pDC, hIcon, rect); 
	} 
	else 
	{ 
		::DrawIconEx(pDC->m_hDC, rect.left, rect.top, hIcon, 
			rect.Width(), rect.Height(), 0, NULL, 
			DI_NORMAL); 
	} 
} 
 
//draw the sidebar 
void CMenuXP::DrawSideBar(CDC *pDC, CRect rect, HICON hIcon, CString strText) 
{ 
	rect.right += 3;	//fill the gap produced by the menubreak 
 
	HBITMAP	bmpBar = CreateGradientBMP( 
		pDC->m_hDC, m_clrSideBarStart, m_clrSideBarEnd, 
		rect.Width(), rect.Height(), 
		0, 256); 
	if (bmpBar) 
	{ 
		CDC memDC; 
		memDC.CreateCompatibleDC(pDC); 
		HBITMAP hOldBmp = (HBITMAP)::SelectObject(memDC.m_hDC, bmpBar); 
		pDC->BitBlt(rect.left, rect.top, 
			rect.Width(), rect.Height(), 
			&memDC, 0, 0, SRCCOPY); 
		::SelectObject(memDC, hOldBmp); 
		::DeleteObject(bmpBar); 
	} 
	//Draw Sidebar text 
	CFont	vertFont; 
	vertFont.CreateFont(16, 0, 900, 900, FW_BOLD, 
		0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 
		CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
		DEFAULT_PITCH, "Arial"); 
	CFont *pOldFont = pDC->SelectObject(&vertFont); 
	COLORREF oldColor = pDC->GetTextColor(); 
	pDC->SetTextColor(RGB(255, 255, 255)); 
	pDC->SetBkMode(TRANSPARENT); 
	pDC->TextOut(rect.left+2, rect.bottom-4, strText); 
	pDC->SetTextColor(oldColor);	 
	pDC->SelectObject(pOldFont); 
	vertFont.DeleteObject(); 
 
} 
 
//draw the check mark 
void CMenuXP::DrawCheckMark(CDC *pDC, CRect rect, BOOL bSelected) 
{ 
/*	CBitmap bmp;	//Check mark bitmap 
	//"#define OEMRESOURCE" must be in the begining of your stdafx.h 
	//for the LoadOEMBitmap to work 
	VERIFY(bmp.LoadOEMBitmap(OBM_CHECK));	 
 
	// center bitmap in caller's rectangle 
	BITMAP bm; 
	bmp.GetBitmap(&bm); 
	int cx = bm.bmWidth; 
	int cy = bm.bmHeight; 
	CRect rcDest = rect; 
	CPoint p(0,0); 
	CSize delta(CPoint((rect.Width() - cx)/2, (rect.Height() - cy)/2)); 
	if (rect.Width() > cx) 
		rcDest = CRect(rect.TopLeft() + delta, CSize(cx, cy)); 
	else 
		p -= delta; 
 
	// select checkmark into memory DC 
	CDC memdc; 
	memdc.CreateCompatibleDC(pDC); 
	CBitmap *pOldBmp = memdc.SelectObject(&bmp); 
 
	COLORREF colorOld = 
		pDC->SetBkColor(GetSysColor(bSelected ? COLOR_MENU : COLOR_3DLIGHT)); 
	pDC->BitBlt(rcDest.left, rcDest.top, rcDest.Width(), rcDest.Height(), 
		&memdc, p.x, p.y, SRCCOPY); 
	pDC->SetBkColor(colorOld); 
 
	memdc.SelectObject(pOldBmp); 
	bmp.DeleteObject(); 
*/ 
	CRect	rcDest = rect; 
	pDC->DrawFrameControl(rcDest, DFC_MENU, DFCS_MENUCHECK); 
} 
 
//Draw menu text 
void CMenuXP::DrawText(CDC *pDC, CRect rect, CString strText, BOOL bSelected, BOOL bDisabled, BOOL bBold) 
{ 
	CFont*	pOldFont; 
	CFont	fontBold; 
 
	if (bBold) 
	{ 
		LOGFONT	logFont; 
		m_fontMenu.GetLogFont(&logFont); 
		logFont.lfWeight = FW_BOLD; 
		fontBold.CreateFontIndirect(&logFont); 
 
		pOldFont = pDC->SelectObject(&fontBold); 
	} 
	else 
	{ 
		pOldFont = pDC->SelectObject(&m_fontMenu); 
	} 
 
	pDC->SetBkMode(TRANSPARENT); 
	if (bDisabled && (!bSelected || m_Style == STYLE_XP)) 
	{ 
		DrawMenuText(*pDC, rect + CPoint(1, 1), strText, m_clrSelectedText); 
	} 
	if (bDisabled) 
	{ 
		DrawMenuText(*pDC, rect, strText, m_clrDisabledText); 
	} 
	else 
	{ 
		DrawMenuText(*pDC, rect, strText, bSelected? m_clrSelectedText : m_clrText); 
	} 
 
	pDC->SelectObject(pOldFont); 
 
	if (bBold) 
		fontBold.DeleteObject(); 
} 
 
//set menu font 
BOOL CMenuXP::SetMenuFont(LOGFONT	lgfont) 
{ 
	m_fontMenu.DeleteObject(); 
	return m_fontMenu.CreateFontIndirect(&lgfont); 
} 
 
 
//clear all memory and handles 
void CMenuXP::Clear(void) 
{ 
	UINT	nCount = GetMenuItemCount(); 
	for (UINT i=0; iIsMyData()) 
		{ 
			delete pData; 
		} 
 
		CMenu	*pSubMenu = GetSubMenu(i); 
		if (pSubMenu && pSubMenu->IsKindOf(RUNTIME_CLASS(CMenuXP))) 
			delete pSubMenu; 
	} 
} 
 
//draw embossed icon for the disabled item 
const DWORD		MAGICROP		= 0xb8074a; 
const COLORREF CWHITE  = RGB(255,255,255); 
 
void CMenuXP::DrawEmbossed(CDC *pDC, HICON hIcon, CRect rect, BOOL bColor) 
{ 
	CDC	memdc; 
	memdc.CreateCompatibleDC(pDC); 
	int cx = rect.Width(); 
	int cy = rect.Height(); 
 
 
	// create mono or color bitmap 
	CBitmap bm; 
	if (bColor) 
		bm.CreateCompatibleBitmap(pDC, cx, cy); 
	else 
		bm.CreateBitmap(cx, cy, 1, 1, NULL); 
 
	// draw image into memory DC--fill BG white first 
	CBitmap* pOldBitmap = memdc.SelectObject(&bm); 
	//FillRect(&memdc, CRect(0, 0, cx, cy), m_clrBackGround); 
	memdc.PatBlt(0, 0, cx, cy, WHITENESS); 
	::DrawIconEx(memdc.m_hDC, 0, 0, hIcon, cx, cy, 1, NULL, DI_NORMAL); 
 
	// This seems to be required. Why, I don't know. ??? 
	COLORREF colorOldBG = pDC->SetBkColor(CWHITE); 
 
	// Draw using hilite offset by (1,1), then shadow 
	CBrush brShadow(GetSysColor(COLOR_3DSHADOW)); 
	CBrush brHilite(GetSysColor(COLOR_3DHIGHLIGHT)); 
	CBrush* pOldBrush = pDC->SelectObject(&brHilite); 
	pDC->BitBlt(rect.left+1, rect.top+1, cx, cy, &memdc, 0, 0, MAGICROP); 
	pDC->SelectObject(&brShadow); 
	pDC->BitBlt(rect.left, rect.top, cx, cy, &memdc, 0, 0, MAGICROP); 
	pDC->SelectObject(pOldBrush); 
	pDC->SetBkColor(colorOldBG);				 // restore 
	memdc.SelectObject(pOldBitmap);		 // ... 
	bm.DeleteObject(); 
	brShadow.DeleteObject(); 
	brHilite.DeleteObject(); 
 
} 
 
////////////////// 
// Shorthand to fill a rectangle with a solid color. 
// 
void CMenuXP::FillRect(CDC *pDC, const CRect& rc, COLORREF color) 
{ 
	CBrush brush(color); 
	CBrush* pOldBrush = pDC->SelectObject(&brush); 
	pDC->PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY); 
	pDC->SelectObject(pOldBrush); 
	brush.DeleteObject(); 
} 
 
 
HBITMAP CMenuXP::CreateGradientBMP(HDC hDC,COLORREF cl1,COLORREF cl2,int nWidth,int nHeight,int nDir,int nNumColors) 
{ 
	if(nNumColors > 256) 
		nNumColors = 256; 
 
	COLORREF		PalVal[256]; 
	memset(PalVal, 0, sizeof(COLORREF)*256); 
 
	int nIndex; 
	BYTE peRed=0,peGreen=0,peBlue=0; 
 
	int r1=GetRValue(cl1); 
	int r2=GetRValue(cl2); 
	int g1=GetGValue(cl1); 
	int g2=GetGValue(cl2); 
	int b1=GetBValue(cl1); 
	int b2=GetBValue(cl2); 
 
    for (nIndex = 0; nIndex < nNumColors; nIndex++) 
    { 
        peRed = (BYTE) (r1 + MulDiv((r2-r1),nIndex,nNumColors-1)); 
        peGreen = (BYTE) (g1 + MulDiv((g2-g1),nIndex,nNumColors-1)); 
        peBlue = (BYTE) (b1 + MulDiv((b2-b1),nIndex,nNumColors-1)); 
 
		PalVal[nIndex]=(peRed << 16) | (peGreen << 8) | (peBlue); 
	} 
 
	int x,y,w,h; 
	w=nWidth; 
	h=nHeight; 
	 
	LPDWORD			pGradBits; 
	BITMAPINFO		GradBitInfo; 
 
	pGradBits=(DWORD*) malloc(w*h*sizeof(DWORD)); 
	memset(&GradBitInfo,0,sizeof(BITMAPINFO)); 
 
	GradBitInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); 
	GradBitInfo.bmiHeader.biWidth=w; 
	GradBitInfo.bmiHeader.biHeight=h; 
	GradBitInfo.bmiHeader.biPlanes=1; 
	GradBitInfo.bmiHeader.biBitCount=32; 
	GradBitInfo.bmiHeader.biCompression=BI_RGB; 
	 
	if(nDir==0)  
	{ 
		for(y=0;yGetMenuItemCount(); 
	for (UINT i=0; i< nItem; i++)  
	{ 
		MENUITEMINFO	info; 
		memset(&info, 0, sizeof(info)); 
		info.cbSize = sizeof(info); 
		info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE; 
		::GetMenuItemInfo(*pMenu, i, TRUE, &info); 
 
		CMenuXPItem	*pData = (CMenuXPItem *)info.dwItemData; 
		if ((info.fType & MFT_OWNERDRAW) && pData && pData->IsMyData()) 
		{ 
			CString	text = pData->m_strText; 
			int iAmpersand = text.Find('&'); 
			if (iAmpersand >=0 && toupper(nChar)==toupper(text[iAmpersand+1])) 
				arItemsMatched.Add(i); 
		} 
		if (info.fState & MFS_HILITE) 
			iCurrentItem = i; // note index of current item 
	} 
	 
 
	// arItemsMatched now contains indexes of items that match the char typed. 
	// 
	//   * if none: beep 
	//   * if one:  execute it 
	//   * if more than one: hilite next 
	// 
	UINT nFound = arItemsMatched.GetSize(); 
	if (nFound == 0) 
		return 0; 
 
	else if (nFound==1) 
		return MAKELONG(arItemsMatched[0], MNC_EXECUTE); 
 
	// more than one found--return 1st one past current selected item; 
	UINT iSelect = 0; 
	for (i=0; i < nFound; i++) { 
		if (arItemsMatched[i] > iCurrentItem) { 
			iSelect = i; 
			break; 
		} 
	} 
	return MAKELONG(arItemsMatched[iSelect], MNC_SELECT); 
} 
 
void CMenuXP::DrawMenuText(CDC& dc, CRect rc, CString text, 
	COLORREF color) 
{ 
	CString left = text; 
	CString right; 
	int iTabPos = left.Find('\t'); 
	if (iTabPos >= 0) { 
		right = left.Right(left.GetLength() - iTabPos - 1); 
		left  = left.Left(iTabPos); 
	} 
	dc.SetTextColor(color); 
	dc.DrawText(left, &rc, DT_MYSTANDARD); 
	if (iTabPos > 0) 
		dc.DrawText(right, &rc, DT_MYSTANDARD|DT_RIGHT); 
} 
 
//find a popupmenu from a menuitem id 
CMenuXP *CMenuXP::FindSubMenuFromID(DWORD dwID) 
{ 
	CMenuXP	*pSubMenu; 
	CMenuXP	*pResult; 
	for (UINT i=0; iFindSubMenuFromID(dwID); 
			if (pResult) 
				return pResult; 
		} 
	} 
 
	return NULL; 
} 
 
//Add a gradient sidebar, it must be the first item in a popupmenu 
BOOL CMenuXP::AddSideBar(CMenuXPSideBar *pItem) 
{ 
	ASSERT(pItem); 
 
	m_bBreak = TRUE; 
	m_bBreakBar = FALSE; 
 
	return AppendMenu(MF_OWNERDRAW, pItem->m_dwID, (LPCTSTR)pItem); 
} 
 
//add a normal menuitem, an accelerator key could be specified, and the accel text will 
//be added automatically 
BOOL CMenuXP::AppendXPMenu(UINT nFlags, CMenuXPItem *pItem, ACCEL *pAccel) 
{ 
	ASSERT(pItem); 
 
	nFlags |= MF_OWNERDRAW; 
	if (m_bBreak)  
		nFlags |= MF_MENUBREAK; 
	if (m_bBreakBar) 
		nFlags |= MF_MENUBARBREAK; 
	m_bBreak = m_bBreakBar = FALSE; 
 
	if (pAccel) 
	{ 
		CBCGKeyHelper	keyhelper(pAccel); 
		CString	strAccel; 
		keyhelper.Format(strAccel); 
		if (strAccel.GetLength()>0) 
		{ 
			pItem->m_strText += _T("\t"); 
			pItem->m_strText += strAccel; 
		} 
	} 
 
	return AppendMenu(nFlags, pItem->m_dwID, (LPCTSTR)pItem); 
} 
 
//Add a separator line 
BOOL CMenuXP::AppendSeparator(void)	 
{ 
	m_bBreak = m_bBreakBar = FALSE; 
 
	CMenuXPSeparator *pItem = new CMenuXPSeparator; 
 
	return AppendMenu(MF_OWNERDRAW | MF_SEPARATOR, 0, (LPCTSTR)pItem); 
} 
 
//add a popup menu 
BOOL CMenuXP::AppendODPopup(UINT nFlags, CMenuXP *pPopup, CMenuXPItem *pItem) 
{ 
	ASSERT(pPopup); 
	ASSERT(pItem); 
 
	nFlags |= MF_OWNERDRAW; 
	nFlags |= MF_POPUP; 
	if (m_bBreak)  
		nFlags |= MF_MENUBREAK; 
	if (m_bBreakBar) 
		nFlags |= MF_MENUBARBREAK; 
	m_bBreak = m_bBreakBar = FALSE; 
 
	return AppendMenu(nFlags, (UINT)pPopup->m_hMenu, (LPCTSTR)pItem); 
} 
 
//Change column, the next item added will be in the next column 
void CMenuXP::Break(void) 
{ 
	m_bBreak = TRUE; 
} 
 
//same as Break(), except that a break line will appear between the two columns 
void CMenuXP::BreakBar(void)	 
{ 
	m_bBreakBar = TRUE; 
}