www.gusucode.com > 改函数包包装了GPRS通信的接口源码程序 > 改函数包包装了GPRS通信的接口源码程序/GPRS开发包/VC_DEMO_源代码/MenuBar.cpp

    // MenuBar.cpp : 
//

#include "stdafx.h"
#include "MenuBar.h"
#include "resource.h"

#include <afxpriv.h>

// pasted from MFC source
#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 , this will change the height of menubar
	const int CXGAP		   = 5;
	const int CYGAP		   = 6;
	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)
	//}}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 |
		CS_DBLCLKS,	// 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()) {
		//TRACE("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) {
		//TRACE("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);
}

/////////////////////////////////////////////////////////////////////////////
// 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);
}

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");
		m_bIgnoreAlt = TRUE;					// next SysKeyUp will be ignored
		break;
	}

	return FALSE;	// pass along...
}

BOOL CMenuBar::TranslateFrameMessage(MSG* pMsg)
{
	ASSERT_VALID(this);
	ASSERT(pMsg);

	UINT nMsg = pMsg->message;
	if (WM_LBUTTONDOWN <= nMsg && nMsg <= WM_MOUSELAST) {
		if (pMsg->hwnd != m_hWnd && m_nTrackingState > 0) {
			// clicked outside
			UpdateBar();
		}
	}
	else if (nMsg == WM_SYSKEYDOWN || nMsg == WM_SYSKEYUP || nMsg == WM_KEYDOWN) {
		BOOL bAlt = HIWORD(pMsg->lParam) & KF_ALTDOWN;	// Alt key presed?
		TCHAR vkey = pMsg->wParam;						// + X key
		if (vkey == VK_MENU ||
			(vkey == VK_F10 && !((GetKeyState(VK_SHIFT) & 0x80000000) ||
			                   (GetKeyState(VK_CONTROL) & 0x80000000) || bAlt))) {
			// only alt key pressed
			if (nMsg == WM_SYSKEYUP) {
				switch (m_nTrackingState) {
				case none:
					if (m_bIgnoreAlt == TRUE) {
//						LTRACE("    ignore ALT key up\n");
						m_bIgnoreAlt = FALSE;
						break;
					}

					UpdateBar(button, GetNextOrPrevButton(0, FALSE));

					break;
				case button:
					UpdateBar();
					break;
				case buttonmouse:
					break;	// do nothing
				}
			}
			return TRUE;
		}
		else if ((nMsg == WM_SYSKEYDOWN || nMsg == WM_KEYDOWN)) {
			if (m_nTrackingState == button) {
				if (m_dwStyle & CBRS_ORIENT_HORZ) {	// if horizontal
					switch (vkey) {
					case VK_LEFT:
					case VK_RIGHT: {
						int nNewIndex  = GetNextOrPrevButton(m_nCurIndex, vkey == VK_LEFT);
						UpdateBar(button, nNewIndex);
						return TRUE;
								   }
					case VK_SPACE:
					case VK_UP:
					case VK_DOWN:
						TrackPopup(m_nCurIndex);
						return TRUE;

					case VK_ESCAPE:
						UpdateBar();
						return TRUE;
					}
				}
				else {								// if vertical
					switch (vkey) {
					case VK_UP:
					case VK_DOWN:{
						int nNewIndex = GetNextOrPrevButton(m_nCurIndex, vkey == VK_UP);
						UpdateBar(button, nNewIndex);
						return TRUE;
								   }
					case VK_SPACE:
					case VK_RIGHT:
					case VK_LEFT:
						TrackPopup(m_nCurIndex);
						return TRUE;

					case VK_ESCAPE:
						UpdateBar();
						return TRUE;
					}
				}
			}

			// Alt + X pressed
			if ((bAlt || m_nTrackingState == button) && isalnum(vkey)) {
				int nIndex;
				if (MapAccessKey(vkey, nIndex) == TRUE) {
					UpdateBar();
					TrackPopup(nIndex);
					return TRUE;		// eat it!
				}
				else if (m_nTrackingState==button && !bAlt) {
//					MessageBeep(0);		// if you want
					return TRUE;
				}
			}

			if (m_nTrackingState > 0) {	// unknown key
				if (m_nTrackingState != buttonmouse) {	// if tracked with mouse, don't update bar
					UpdateBar();
				}
			}
		}
	}
	return FALSE;	// pass along...
}

BOOL CMenuBar::MapAccessKey(TCHAR cAccessKey, int& nIndex)
{
	for (int i = 0; i < GetItemCount(); ++i) {
		TCHAR cKey;
		if (m_arrItem[i]->GetAccessKey(cKey) == TRUE &&
			cKey == cAccessKey) {
			nIndex = i;
			return TRUE;
		}
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CMenuBar layout

// calculate only layout from m_bWrapped
void CMenuBar::CalcFloatingLayout()
{
	ASSERT_VALID(this);
	ASSERT(::IsWindow(m_hWnd));

	int xStart = CXGAP;

	if (!IsFloating()) {
		xStart += CXGRIPPER;
	}

	int xOffset = xStart;
	int yOffset = CYGAP;

	for (int i = 0; i < GetItemCount(); ++i) {
		CMenuItem* pItem = m_arrItem[i];
		if (pItem->IsValid()) {
			CPoint ptItem(xOffset, yOffset);
			pItem->Layout(ptItem, TRUE);	// layout by itself!
			if (pItem->m_bWrapped == TRUE) {
				xOffset = xStart;			// reset xOffset
				yOffset += pItem->m_sizeHorz.cy;
			}
			else
				xOffset += pItem->m_sizeHorz.cx;
		}
	}
}

// calulate ordinary layout and size without m_bWrapped 
CSize CMenuBar::CalcLayout(DWORD dwMode, int nLength)
{
	ASSERT_VALID(this);
	ASSERT(::IsWindow(m_hWnd));
	if (dwMode & LM_HORZDOCK)
		ASSERT(dwMode & LM_HORZ);
	CSize sizeResult(0, 0);

	if (!(dwMode & LM_HORZ)) 	{				// if vertical
		int yOffset = CXGAP;
		if (!IsFloating())
			yOffset += CXGRIPPER;

		for (int i = 0; i < GetItemCount(); ++i) {
			CMenuItem* pItem = m_arrItem[i];
			if (pItem->IsValid()) {
				CPoint	ptItem(CYGAPVERT, yOffset);
				pItem->Layout(ptItem, FALSE);	// layout by itself
				yOffset += pItem->m_rcItem.Height();
			}
		}
		int cxBar = max(::GetSystemMetrics(SM_CXSMICON), cyMenuButton + (CYGAPVERT)*2);
		sizeResult =  CSize(cxBar, yOffset+CXGAP);
	}
	else {										// if horizontal
		int xOffset = CXGAP;
		if (!IsFloating())
			xOffset += CXGRIPPER;

		for (int i = 0; i < GetItemCount(); ++i) {
			CMenuItem* pItem = m_arrItem[i];
			if (pItem->IsValid()) {
				CPoint ptItem(xOffset, CYGAP);
				pItem->Layout(ptItem, TRUE);	// layout by itself
				xOffset += pItem->m_rcItem.Width();
			}
		}
		int cyBar = max(::GetSystemMetrics(SM_CYSMICON), cyMenuButton + CYGAP*2);
		sizeResult = CSize(xOffset+CXGAP, cyBar);
	}

	return sizeResult;
	//return CSize(400,100);
}

// in fact, it's never called
CSize CMenuBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
//	LTRACE("CMenuBar::CalcFixedLayout\n");

	DWORD dwMode = bStretch ? LM_STRETCH : 0;
	dwMode |= bHorz ? LM_HORZ : 0;

	return CalcLayout(dwMode);
}

CSize CMenuBar::CalcDynamicLayout(int nLength, DWORD dwMode)
{
	LTRACE("CMenuBar::CalcDynamicLayout\n");

	// Who can understand what "dwMode" means?
	// 
	// If you want it be pricisely same as DevStudio style,
	// I think you have to create a "CDockContext" derived class.
	// This is the reason why everyone think "CControlBar class" junk ? 

	ASSERT_VALID(this);
	ASSERT_VALID(m_pDockSite);
	ASSERT_VALID(m_pDockBar);

	if (m_hMenu == NULL)	// if have no menu yet, just return
		return CSize(0, 0);

	if (IsFloating()) {
		CFrameWnd* pFrame = GetParentFrame();
		ASSERT_VALID(pFrame);
		CMenu* pSysMenu = pFrame->GetSystemMenu(FALSE);
		ASSERT_VALID(pSysMenu);
		pSysMenu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_DISABLED);
	}

	if (dwMode & LM_HORZ) {						// horizontal
	//	LTRACE("    horizontal\n");
		if (dwMode & LM_HORZDOCK) {
			if (IsFloating() || (m_dwStyle & CBRS_ORIENT_VERT)) {	// if Floating->Docking, return min size
	//			LTRACE("    return min size\n");
				return CalcLayout(dwMode);
				//return CalcSize(GetItemCount()); //Modify by xhs
			}
			else {
				if (m_pDockContext->m_pDC) {	// now dragging
					// return DockBar size
					//LTRACE("    now dragging, so calc with DockBar\n");
					CSize size = CalcSize(GetItemCount());//CalcLayout(dwMode);
					CRect rcFrame;
					m_pDockBar->GetWindowRect(rcFrame);
					CRect rcBar;
					GetWindowRect(rcBar);
					rcBar.right = rcFrame.right;
					return CSize(rcBar.Width() + cxBorder2, size.cy);
					//return CalcSize(GetItemCount()); //Modify by xhs
				}
				else {
					//LTRACE("    no draggin calc bigger size\n");
					CRect rcFrame;
					m_pDockSite->GetWindowRect(rcFrame);
					SizeMenuBar(GetItemCount(), rcFrame.Width() - cxBorder2*2, FALSE);
					CalcFloatingLayout();
					CSize size1 = CalcSize(GetItemCount());
					return CSize(rcFrame.Width(), size1.cy);
				    //return CalcSize(GetItemCount()); //Modify by xhs
				}
			}
		}
		else if (dwMode & LM_MRUWIDTH) {		// floating size
			//LTRACE("    return floating size\n");
			SizeMenuBar(GetItemCount(), m_nMRUWidth, FALSE);	// load floating Bar Width! 
			CalcFloatingLayout();
			return CalcSize(GetItemCount());
		}
		else if (dwMode & LM_COMMIT) {
			//LTRACE("    commit, just calc: %d\n", nLength);
			m_nMRUWidth = nLength;								// save floating Bar Width!!! (used when SaveBarState)
			CalcFloatingLayout();
			return CSize(0, 0);									// MFC does'nt use this Size
		}
		else if (dwMode & LM_LENGTHY) {
			//LTRACE("    nLength is height : %d\n", nLength);
			SizeMenuBar(GetItemCount(), nLength, TRUE);
			return CalcSize(GetItemCount());
		}
		else {
			//LTRACE("    nLength is width  : %d\n", nLength);
			SizeMenuBar(GetItemCount(), nLength, FALSE);
			return CalcSize(GetItemCount());
		}
		ASSERT(TRUE);
	}
	else {														// vertical
		InvalidateRect(NULL);									// force to repaint!!!
		//LTRACE("    vertical\n");
		if (IsFloating() || (m_dwStyle & CBRS_ORIENT_HORZ)) {	// if Floating->Docking, return min size
			//LTRACE("    return min size\n");
			return CalcLayout(dwMode);
		}
		else {									// return large size
			if (m_pDockContext->m_pDC) {		// CDockContext::m_bDragging is not helpful :(
				//LTRACE("    now dragging, so calc with DockBar\n");
				CSize size = CalcLayout(dwMode);
				CRect rcFrame;
				m_pDockBar->GetWindowRect(rcFrame);
				CRect rcBar;
				GetWindowRect(rcBar);
				rcBar.bottom = rcFrame.bottom;
				return CSize(size.cx, rcBar.Height());
			}
			else {
				//LTRACE("    no dragging, return biggest size\n");
				
				CSize size = CalcLayout(dwMode);
				CRect rcFrame;
				m_pDockSite->GetWindowRect(rcFrame);
				CRect rcBar;
				GetWindowRect(rcBar);
				rcBar.bottom = rcFrame.bottom;
				return CSize(size.cx, rcFrame.Height());
			}
		}
	}

	ASSERT(TRUE);								// never come here
	return CalcLayout(dwMode, nLength);
}

void CMenuBar::DrawGripper(CDC* pDC)
{
	if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
		return;

	CRect rcGrip;
	GetClientRect(&rcGrip);

	if(m_dwStyle & CBRS_ORIENT_HORZ) {
		// gripper at left
		//rcGrip.DeflateRect(4, 3);
		rcGrip.right = rcGrip.left + 3;
		//pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
		rcGrip.OffsetRect(4, 0);
        pDC->DrawEdge(rcGrip,	BDR_RAISEDINNER, BF_RECT);
	}
	else {
		// gripper at top
		rcGrip.DeflateRect(3, 4);
		rcGrip.bottom = rcGrip.top + 3;
		pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
		//rcGrip.OffsetRect(0, 4);
		//pDC->DrawEdge(rcGrip, BDR_RAISEDINNER, BF_RECT);
	}
}

#define CX_BORDER   1
#define CY_BORDER   1

void CMenuBar::DrawBorder(CDC* pDC)
{
	if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
		return;

	CRect rect;
	GetClientRect(rect);

	DWORD dwStyle = m_dwStyle;
	if (!(dwStyle & CBRS_BORDER_ANY))
		return;

	CRect rect3 = rect;
	if (m_dwStyle & CBRS_ORIENT_HORZ) {
		rect3.left += cxBorder2;//rect.DeflateRect(2, 0);
		pDC->DrawEdge(rect3, BDR_RAISEDINNER ,BF_LEFT);	
	}
	else {
		rect3.top += cyBorder2;//rect.DeflateRect(0, 2);
		pDC->DrawEdge(rect3,BDR_RAISEDINNER ,  BF_TOP);	
	}

	// prepare for dark lines
	ASSERT(rect.top == 0 && rect.left == 0);
	CRect rect1, rect2;
	rect1 = rect;
	rect2 = rect;
	COLORREF clr = ::GetSysColor(COLOR_BTNSHADOW);//afxData.bWin4 ? afxData.clrBtnShadow : afxData.clrWindowFrame;

	// draw dark line one pixel back/up
	if (dwStyle & CBRS_BORDER_3D)
	{
		rect1.right -= CX_BORDER;
		rect1.bottom -= CY_BORDER;
	}
	if (dwStyle & CBRS_BORDER_TOP)
		rect2.top += CY_BORDER;//cyBorder2;

	if (dwStyle & CBRS_BORDER_BOTTOM)
		rect2.bottom -= CY_BORDER;

	if (dwStyle & CBRS_BORDER_LEFT)
		rect2.left += CX_BORDER;

	if (dwStyle & CBRS_BORDER_RIGHT)
		rect2.right -= CX_BORDER;

	if (dwStyle & CBRS_BORDER_3D)
	{
		// draw left and top
		if (dwStyle & CBRS_BORDER_LEFT)
			pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_LEFT);//pDC->FillSolidRect(1, rect2.top, CX_BORDER, rect2.Height(), clr);
		if (dwStyle & CBRS_BORDER_TOP)
			pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_TOP);//pDC->FillSolidRect(0, 1, rect.right, CY_BORDER, clr);

		// draw right and bottom
		if (dwStyle & CBRS_BORDER_RIGHT)
			pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_RIGHT);//pDC->FillSolidRect(rect.right, rect2.top, -CX_BORDER, rect2.Height(), clr);
		if (dwStyle & CBRS_BORDER_BOTTOM)
			pDC->DrawEdge(rect2, BDR_RAISEDINNER, BF_BOTTOM);//pDC->FillSolidRect(0, rect.bottom, rect.right, -CY_BORDER, clr);
	}
	
}

/////////////////////////////////////////////////////////////////////////////
// CMenuBarFrameHook implementation
CMenuBarFrameHook::CMenuBarFrameHook()
{
	m_pMenuBar = NULL;
}

BOOL CMenuBarFrameHook::Install(CMenuBar* pMenuBar, HWND hWndToHook)
{
	ASSERT_VALID(pMenuBar);
	ASSERT(m_pMenuBar == NULL);
	m_pMenuBar = pMenuBar;
	return HookWindow(hWndToHook);//CWnd::FromHandlePermanent(hWndToHook));
}

CMenuBarFrameHook::~CMenuBarFrameHook()
{
}

LRESULT CMenuBarFrameHook::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	ASSERT_VALID(m_pMenuBar);

	switch (nMsg) {
	case WM_MENUSELECT:
		m_pMenuBar->OnMenuSelect((HMENU)lParam, (UINT)LOWORD(wParam));
		break;

	// The following messages are trapped for the MDI client window
	case WM_INITMENUPOPUP:
		LTRACE("CMenuBar::WM_INITMENUPOPUP\n");
		if (!HIWORD(lParam) && (HMENU)wParam == m_pMenuBar->m_hWindowMenu)
			m_pMenuBar->OnInitMenuPopup();
		break;


	case WM_MDISETMENU:			// only sent to MDI client window
		// Setting new frame/window menu: bypass MDI. wParam is new menu.
		if (wParam) {
			LTRACE("CMenuBar::WM_MDISETMENU 0x%04x\n", wParam);
//			HMENU hNewMenu = (HMENU)wParam;
			m_pMenuBar->OnSetMenu((HMENU)wParam, (HMENU)lParam);			
		}
		return 0;

	case WM_MDIREFRESHMENU:		// only sent to MDI client window
		// Normally, would call DrawMenuBar, but I have the menu, so eat it.
//		LTRACE("CMenuBar::WM_MDIREFRESHMENU\n");
		return 0;

	case WM_PAINT:
		// After changing the MDI maximized state, the client window gets a
		// paint message. This is the most convenient place to find out; there
		// is no WM_MDIMAXIMIZED message.
		if (/*m_pWndHooked->*/m_hWnd == m_pMenuBar->m_hWndMDIClient)
			m_pMenuBar->OnPaintMDIClient();//CheckMinMaxState();
		break;
	}

	return CSubclassWnd::WindowProc(nMsg, wParam, lParam);
}

void CMenuBar::OnLButtonUp(UINT nFlags, CPoint point) 
{
	//LTRACE("CMenuBar::OnLButtonUp\n");
	// TODO: 
	ASSERT(m_pMenuControl);
	if (m_pMenuControl->OnMouseMsg(WM_LBUTTONUP, nFlags, point)) {
		CControlBar::OnLButtonUp(nFlags, point);
		return;
	}	

	int nIndex = HitTestOnTrack(point);
/*
	if (IsValidIndex(nIndex) && m_bDown == TRUE) {
		HMENU hSubMenu = ::GetSubMenu(m_hMenu, nIndex);
		if (hSubMenu == NULL) {
			UINT nID = ::GetMenuItemID(m_hMenu, nIndex);
			ASSERT(nID != -1);
			GetOwner()->SendMessage(WM_COMMAND, (WPARAM)nID, (LPARAM)GetSafeHwnd());

			UpdateBar();
		}
	}
*/
	CControlBar::OnLButtonUp(nFlags, point);
}



void CMenuBar::DoPaint(CDC* pDC)
{
	//LTRACE("CMenuBar::DoPaint\n");
	CRect rect;
	GetClientRect(rect);

	// draw buttons
	for (int i = 0; i < m_arrItem.GetSize(); ++i) {
		CMenuItem* pItem = m_arrItem[i];
		if (pItem->IsValid())
			m_arrItem[i]->Update(pDC);
	}

	// draw decorations
	DrawGripper(pDC);
	DrawBorder(pDC);

	// draw captions
	if (m_pMenuControl) {
		CRect rcDockBar;
		m_pDockBar->GetClientRect(rcDockBar);
		if (m_dwStyle & CBRS_ORIENT_HORZ) {
			m_pMenuControl->DelayLayoutAndDraw(pDC, CSize(rcDockBar.Width() + cxBorder2, rect.Height()));
		}
		else {
			m_pMenuControl->DelayLayoutAndDraw(pDC, CSize(rect.Width(), rcDockBar.Height()));
		}
	}
}

HMENU CMenuBar::GetMyMenu()
{
	return m_hMenu;
}

void CMenuBar::_KillTimer()
{
	if (m_nIDEvent) {
		KillTimer(m_nIDEvent);
		m_nIDEvent = NULL;
	}
}


int getPrevValidIndex(int nIndex)
{
	--nIndex;




	return nIndex;
}

// set only m_bWrapped by nWidth
int CMenuBar::WrapMenuBar(int nCount, int nWidth)
{
//	LTRACE("CMenuBar::WrapMenuBar\n");
	int nResult = 0;
	int xStart = CXGAP;
	if (!IsFloating() && !m_pDockContext->m_pDC)	// if not floating and ****not dragging!!!****
		xStart += CXGRIPPER;
	
	int x = xStart;
	
	for (int i = 0; i < nCount; ++i) {
		CMenuItem* pItem = m_arrItem[i];
		if (pItem->IsValid()) {

			if (i+1 == nCount)
				return ++nResult;
			
			if (x + pItem->m_sizeHorz.cx + CXGAP> nWidth) {// itself is over
				if (pItem->CanWrap()) {
					pItem->m_bWrapped = TRUE;
					++nResult;
					x = xStart;
				}
			}
			else if (x + pItem->m_sizeHorz.cx + m_arrItem[i+1]->m_sizeHorz.cx + CXGAP > nWidth) {
				if (pItem->CanWrap()) {
					pItem->m_bWrapped = TRUE;
					++nResult;
					x = xStart;
				}
			}
			else {
				pItem->m_bWrapped = FALSE;
				x += pItem->m_sizeHorz.cx;
			}
		}
	}
/*			//another algorithm
			int dx = pItem->m_sizeHorz.cx;
			if (dx + CXGAP > nWidth) {		// bigger than nWidth
				if (pItem->CanWrap()) {
					pItem->m_bWrapped = TRUE;
//					if (i != nCount - 1)
						++nResult;
				}
			}
			else if (dx + x + CXGAP > nWidth) {
				if (i > 0 && m_arrItem[i-1]->CanWrap()) {
			//		LTRACE("index %d is wrap\n", i-1);
					m_arrItem[i-1]->m_bWrapped = TRUE;
					pItem->m_bWrapped = FALSE;
					x = xStart;
//					if (i != nCount - 1)///////////////////////////
						++nResult;
				}
			}
			else {
				pItem->m_bWrapped = FALSE;
			}
			x += dx;

		}
	}
	*/

	return nResult + 1;
}

// calc only size, by using m_bWrapped
CSize CMenuBar::CalcSize(int nCount)
{
	ASSERT(nCount > 0);
	CPoint cur(CXGAP, CYGAP);
	CSize sizeResult(0, 0);
	int nWrap = 0;
	for (int i = 0; i < nCount; ++i) {
		CMenuItem* pItem = m_arrItem[i];
		if (pItem->IsValid()) {
			sizeResult.cx = max(cur.x + pItem->m_sizeHorz.cx, sizeResult.cx);
			sizeResult.cy = max(cur.y + pItem->m_sizeHorz.cy, sizeResult.cy);

			cur.x += pItem->m_sizeHorz.cx;
			
			if (pItem->m_bWrapped == TRUE) {
				LTRACE("    nIndex:%d is wrapped\n", i);
				cur.x = CXGAP;					// reset x pos
				cur.y += pItem->m_sizeHorz.cy;
				++nWrap;
			}

		}
	}

	//sizeResult.cy += CYGAP;
	//sizeResult.cx += CXGAP;
	// Modify by xhs , and the width of menubar will be shorter
	sizeResult.cy += (CYGAP-4);
	sizeResult.cx += (CXGAP+2);  

	return sizeResult;
}

void CMenuBar::SizeMenuBar(int nCount, int nLength, BOOL bVert)
{
	//LTRACE("CMenuBar::SizeMenuBar\n");
	ASSERT(nCount > 0);

	if (!bVert) {				// nLength is horizontal length

		if (IsFloating()) {		// half size wrapping

			CSize sizeMax, sizeMin, sizeMid;

			// Wrap MenuBar vertically
			WrapMenuBar(nCount, 0);
			sizeMin = CalcSize(nCount);

			// Wrap MenuBar horizontally
			WrapMenuBar(nCount, 32767);
			sizeMax = CalcSize(nCount);

			// we can never know this algorithm :), see CToolBar implementation
			while (sizeMin.cx < sizeMax.cx) {
//				LTRACE("looping sizeMin.cx:%d < sizeMax.cx:%d\n", sizeMin.cx, sizeMax.cx);
				sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2;
				WrapMenuBar(nCount, sizeMid.cx);
				sizeMid = CalcSize(nCount);
				if (sizeMid.cx == sizeMax.cx) {	// if you forget, it loops forever!
					return;
				}
//				LTRACE("    sizeMid : %d %d\n", sizeMid.cx, sizeMid.cy);

				if (nLength >= sizeMax.cx) {
//					LTRACE("    nLength:%d >= sizeMax.cx:%d\n", nLength, sizeMax.cx);
					if (sizeMin == sizeMid) {
						WrapMenuBar(nCount, sizeMax.cx);
//						LTRACE("out SizeMenuBar\n");
						return;
					}
					sizeMin = sizeMid;
				}
				else if (nLength < sizeMax.cx) {
//					LTRACE("    nLength:%d < sizeMax.cx:%d\n", nLength, sizeMax.cx);
					sizeMax = sizeMid;
				}
				else {
//					LTRACE("out SizeMenuBar\n");
					return;
				}
			}
		}
		else {								// each one wrapping
			LTRACE("    just each one wrapping\n");
			WrapMenuBar(nCount, nLength);
		}
	}
	else {						// nLength is vertical length
		CSize sizeMax, sizeMin, sizeMid;

		// Wrap MenuBar vertically
		WrapMenuBar(nCount, 0);
		sizeMin = CalcSize(nCount);

		// Wrap MenuBar horizontally
		WrapMenuBar(nCount, 32767);
		sizeMax = CalcSize(nCount);

		while (sizeMin.cx < sizeMax.cx) {
			sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2;
			WrapMenuBar(nCount, sizeMid.cx);
			sizeMid = CalcSize(nCount);
			if (sizeMid.cx == sizeMax.cx) {
				return;
			}
			if (nLength < sizeMid.cy) {
				if (sizeMin == sizeMid) {
					WrapMenuBar(nCount, sizeMax.cx);
					//LTRACE("out SizeMenuBar\n");
					return;
				}
				sizeMin = sizeMid;
			}
			else if (nLength > sizeMid.cy)
				sizeMax = sizeMid;
			else {
				//LTRACE("out SizeMenuBar\n");
				return;
			}
		}
	}

	//LTRACE("out SizeMenuBar\n");
}


void CMenuBar::DeleteItems()
{
	for(int i = 0; i < m_arrItem.GetSize(); ++i) {
		CMenuItem* pItem = m_arrItem[i];
		delete pItem;
	}

	m_arrItem.RemoveAll();

	m_pMenuIcon = NULL;
	m_pMenuControl = NULL;
}

void CMenuBar::OnDestroy() 
{
	CControlBar::OnDestroy();	
	
	_KillTimer();
	DeleteItems();
}

int CMenuBar::GetNextOrPrevButton(int nIndex, BOOL bPrev)
{
	int nCount = GetItemCount();
	if (bPrev) {				// <-
		--nIndex;
		if (nIndex < 0)
			nIndex = nCount - 1;
	}
	else {						// ->
		++nIndex;
		if (nIndex >= nCount)
			nIndex = 0;
	}

	if (!m_arrItem[nIndex]->CanTrack() || !m_arrItem[nIndex]->IsValid()) {
		return GetNextOrPrevButton(nIndex, bPrev);
	}

	return nIndex;
}

void CMenuBar::AddIcon(HICON hIcon)
{
//	ASSERT(m_bIcon == FALSE);

//	m_bIcon = TRUE;
}

CMenuControl::CMenuControl(CControlBar* pBar)
{
	m_bValid = FALSE;
	m_pBar = pBar;
	m_bDown = FALSE;
	m_nTracking = -1;

	int cxCaption = cyMenuButton - CYGAP + 1;
	m_sizeHorz = CSize(cxCaption*3 + CXGAP/2, cyMenuButton);
}

CMenuControl::~CMenuControl()
{

}

void CMenuControl::Update(CDC* pDC)
{
	// do nothing
}

void CMenuControl::Layout(CPoint point, BOOL bHorz)
{
	LTRACE("CMenuControl::Layout bHorz:%d\n", bHorz);
	m_bHorz = bHorz;

	// just layout easily
	if (bHorz) {
		m_rcItem = CRect(point, m_sizeHorz);
	}
	else {
		m_rcItem = CRect(point, CSize(m_sizeHorz.cy, m_sizeHorz.cx));
	}
}

void CMenuControl::DelayLayoutAndDraw(CDC* pDC, CSize sizeBar)
{
//	if (!IsValid())
//		return;

	// layout
	if (m_bHorz) {
		int cxCaption = cyMenuButton - CYGAP + 1;
		int cyCaption = cxCaption - 1;
		CRect rcCaption;
		rcCaption.right = sizeBar.cx;
		rcCaption.bottom = sizeBar.cy - CYGAP;
		rcCaption.left = rcCaption.right - cxCaption;
		rcCaption.top = rcCaption.bottom - cyCaption;

		m_arrCaption[0] = rcCaption;

		rcCaption -= CPoint(cxCaption+CXGAP/2, 0);
		m_arrCaption[1] = rcCaption;

		rcCaption -= CPoint(cxCaption, 0);
		m_arrCaption[2] = rcCaption;

		m_rcItem = CRect(m_arrCaption[2].left, m_arrCaption[2].top,
			m_arrCaption[0].right, m_arrCaption[0].bottom);
	}
	else {
		int cxCaption = cyMenuButton - CYGAPVERT - 1;
		int cyCaption = cxCaption - 1;
		CRect rcCaption;
		rcCaption.left = CYGAPVERT;
		rcCaption.bottom = sizeBar.cy;
		rcCaption.right = rcCaption.left + cxCaption;
		rcCaption.top = rcCaption.bottom - cyCaption;

		m_arrCaption[0] = rcCaption;	

		rcCaption -= CPoint(0, cyCaption+CXGAP/2);
		m_arrCaption[1] = rcCaption;

		rcCaption -= CPoint(0, cyCaption);
		m_arrCaption[2] = rcCaption;

		m_rcItem = CRect(m_arrCaption[2].left, m_arrCaption[2].top,
			m_arrCaption[0].right, m_arrCaption[0].bottom);
	}

	if (!IsValid())
		return;

	// draw frame controls
	for (int i = 0; i < 3; ++i) {
		DrawControl(pDC, i, FALSE);
	}

//	pDC->DrawFrameControl(m_arrCaption[0], DFC_CAPTION, DFCS_CAPTIONCLOSE);
//	pDC->DrawFrameControl(m_arrCaption[1], DFC_CAPTION, DFCS_CAPTIONRESTORE);
//	pDC->DrawFrameControl(m_arrCaption[2], DFC_CAPTION, DFCS_CAPTIONMIN);		
}


// pasted from CDockContext
void CMenuBarDockContext::StartDrag(CPoint pt)
{
	ASSERT_VALID(m_pBar);
	m_bDragging = TRUE;

	InitLoop();

	if (m_pBar->m_dwStyle & CBRS_SIZE_DYNAMIC)
	{
		// get TRUE bar size (including borders)
		CRect rect;
		m_pBar->GetWindowRect(rect);
		m_ptLast = pt;
		CSize sizeHorz = m_pBar->CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK);
		CSize sizeVert = m_pBar->CalcDynamicLayout(0, LM_VERTDOCK);
		CSize sizeFloat = m_pBar->CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH);

		m_rectDragHorz = CRect(rect.TopLeft(), sizeHorz);
		m_rectDragVert = CRect(rect.TopLeft(), sizeVert);

		// calculate frame dragging rectangle
		m_rectFrameDragHorz = CRect(rect.TopLeft(), sizeFloat);
		m_rectFrameDragVert = CRect(rect.TopLeft(), sizeFloat);

#ifdef _MAC
		CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz,
			WS_THICKFRAME | WS_CAPTION, WS_EX_FORCESIZEBOX);
		CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert,
			WS_THICKFRAME | WS_CAPTION, WS_EX_FORCESIZEBOX);
#else
		CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz);
		CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert);
#endif
		m_rectFrameDragHorz.InflateRect(-cxBorder2, -cyBorder2);
		m_rectFrameDragVert.InflateRect(-cxBorder2, -cyBorder2);
	}
	else if (m_pBar->m_dwStyle & CBRS_SIZE_FIXED)
	{
		// get TRUE bar size (including borders)
		CRect rect;
		m_pBar->GetWindowRect(rect);
		m_ptLast = pt;
		CSize sizeHorz = m_pBar->CalcDynamicLayout(-1, LM_HORZ | LM_HORZDOCK);
		CSize sizeVert = m_pBar->CalcDynamicLayout(-1, LM_VERTDOCK);

		// calculate frame dragging rectangle
		m_rectFrameDragHorz = m_rectDragHorz = CRect(rect.TopLeft(), sizeHorz);
		m_rectFrameDragVert = m_rectDragVert = CRect(rect.TopLeft(), sizeVert);

		CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz);
		CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert);
		m_rectFrameDragHorz.InflateRect(-cxBorder2, -cyBorder2);
		m_rectFrameDragVert.InflateRect(-cxBorder2, -cyBorder2);
	}
	else
	{
		// get TRUE bar size (including borders)
		CRect rect;
		m_pBar->GetWindowRect(rect);
		m_ptLast = pt;
		BOOL bHorz = HORZF(m_dwStyle);
		DWORD dwMode = !bHorz ? (LM_HORZ | LM_HORZDOCK) : LM_VERTDOCK;
		CSize size = m_pBar->CalcDynamicLayout(-1, dwMode);

		// calculate inverted dragging rect
		if (bHorz)
		{
			m_rectDragHorz = rect;
			m_rectDragVert = CRect(CPoint(pt.x - rect.Height()/2, rect.top), size);
		}
		else // vertical orientation
		{
			m_rectDragVert = rect;
			m_rectDragHorz = CRect(CPoint(rect.left, pt.y - rect.Width()/2), size);
		}

		// calculate frame dragging rectangle
		m_rectFrameDragHorz = m_rectDragHorz;
		m_rectFrameDragVert = m_rectDragVert;

		CMiniFrameWnd::CalcBorders(&m_rectFrameDragHorz);
		CMiniFrameWnd::CalcBorders(&m_rectFrameDragVert);
		m_rectFrameDragHorz.InflateRect(-cxBorder2, -cyBorder2);
		m_rectFrameDragVert.InflateRect(-cxBorder2, -cyBorder2);
	}

	// adjust rectangles so that point is inside
	AdjustRectangle(m_rectDragHorz, pt);
	AdjustRectangle(m_rectDragVert, pt);
	AdjustRectangle(m_rectFrameDragHorz, pt);
	AdjustRectangle(m_rectFrameDragVert, pt);

	// initialize tracking state and enter tracking loop
	m_dwOverDockStyle = CanDock();
	Move(pt);   // call it here to handle special keys
	_Track();
}

BOOL CMenuBarDockContext::_Track()
{
	// don't handle if capture already set
	if (::GetCapture() != NULL)
		return FALSE;

	// set capture to the window which received this message
	m_pBar->SetCapture();
	ASSERT(m_pBar == CWnd::GetCapture());

#ifndef _MAC
	// get messages until capture lost or cancelled/accepted
	while (CWnd::GetCapture() == m_pBar)
	{
		MSG msg;
		if (!::GetMessage(&msg, NULL, 0, 0))
		{
			AfxPostQuitMessage(msg.wParam);
			break;
		}

		switch (msg.message)
		{
		case WM_LBUTTONUP:
			if (m_bDragging)
				_EndDrag();
			else
				EndResize();
			return TRUE;
		case WM_MOUSEMOVE:
			if (m_bDragging)
				Move(msg.pt);
			else
				Stretch(msg.pt);
			break;
		case WM_KEYUP:
			if (m_bDragging)
				OnKey((int)msg.wParam, FALSE);
			break;
		case WM_KEYDOWN:
			if (m_bDragging)
				OnKey((int)msg.wParam, TRUE);
			if (msg.wParam == VK_ESCAPE)
			{
				CancelLoop();
				return FALSE;
			}
			break;
		case WM_RBUTTONDOWN:
			CancelLoop();
			return FALSE;

		// just dispatch rest of the messages
		default:
			DispatchMessage(&msg);
			break;
		}
	}
#else
	Point   ptCur = {0};

	// get messages until capture lost or cancelled/accepted
	while (CWnd::GetCapture() == m_pBar)
	{
		EventRecord     er;

		if (OSEventAvail(everyEvent, &er))
		{
			GetNextEvent(everyEvent, &er);
			switch (er.what)
			{
			case mouseUp:
				if (m_bDragging)
					EndDrag();
				else
					EndResize();
				return TRUE;

			case keyDown:
			case keyUp:
			case autoKey:
			case app2Evt:
			{
				MSG     msg;

				if (WrapEvent(&er, &msg, PM_REMOVE))
				{
					if (m_bDragging)
						OnKey((int)msg.wParam, msg.message == WM_KEYDOWN);
					if (msg.message == WM_KEYUP && msg.wParam == VK_ESCAPE)
					{
						CancelLoop();
						return FALSE;
					}
				}
				break;
			}

			default:
				break;
			}
		}
		else
		{
			if (!EqualPt(er.where, ptCur))
			{
				POINT pt = {er.where.h, er.where.v};
				if (m_bDragging)
					Move(pt);
				else
					Stretch(pt);
			}
		}
	}
#endif
	CancelLoop();

	return FALSE;
}

void CMenuBarDockContext::_EndDrag()
{
	CancelLoop();

	if (m_dwOverDockStyle != 0)
	{
		CDockBar* pDockBar = GetDockBar(m_dwOverDockStyle);
		ASSERT(pDockBar != NULL);

		CRect rect = (m_dwOverDockStyle & CBRS_ORIENT_VERT) ?
			m_rectDragVert : m_rectDragHorz;

		UINT uID = _AfxGetDlgCtrlID(pDockBar->m_hWnd);
		if (uID >= AFX_IDW_DOCKBAR_TOP &&
			uID <= AFX_IDW_DOCKBAR_BOTTOM)
		{
			m_uMRUDockID = uID;
			m_rectMRUDockPos = rect;
			pDockBar->ScreenToClient(&m_rectMRUDockPos);
		}

		// dock it at the specified position, RecalcLayout will snap

		// insisting own line trick!
		CRect rcDockBar;
		pDockBar->GetWindowRect(rcDockBar);

		if (m_dwOverDockStyle & CBRS_ORIENT_VERT) {
			rect.top = rcDockBar.top - 1;
		}
		else {
			rect.left = rcDockBar.left - 10;
		}

		m_pDockSite->DockControlBar(m_pBar, pDockBar, &rect);
		m_pDockSite->RecalcLayout();

	}
	else if ((m_dwStyle & CBRS_SIZE_DYNAMIC) || (HORZF(m_dwStyle) && !m_bFlip) ||
			(VERTF(m_dwStyle) && m_bFlip))
	{
		m_dwMRUFloatStyle = CBRS_ALIGN_TOP | (m_dwDockStyle & CBRS_FLOAT_MULTI);
		m_ptMRUFloatPos = m_rectFrameDragHorz.TopLeft();
		m_pDockSite->FloatControlBar(m_pBar, m_ptMRUFloatPos, m_dwMRUFloatStyle);
	}
	else // vertical float
	{
		m_dwMRUFloatStyle = CBRS_ALIGN_LEFT | (m_dwDockStyle & CBRS_FLOAT_MULTI);
		m_ptMRUFloatPos = m_rectFrameDragVert.TopLeft();
		m_pDockSite->FloatControlBar(m_pBar, m_ptMRUFloatPos, m_dwMRUFloatStyle);
	}
}


void CMenuBar::OnPaintMDIClient()
{
	LTRACE("CMenuBar::OnPaintMDIClient\n");

	if (m_hWndMDIClient) {
		BOOL bMax = FALSE;
		// get active MDI child window
		HWND hWndChild = (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0,
			(LPARAM)&bMax);
		//ASSERT(::IsWindow(hWndChild)); crash! tell me why!

		if (bMax != m_bMDIMaximized) {
			LTRACE("    max state changed\n");
			m_bMDIMaximized = bMax;
			
			if (bMax) {
				LTRACE("        maximized\n");
				m_pMenuControl->Validate(TRUE);
				m_pMenuIcon->Validate(TRUE);
			}
			else {
				LTRACE("        not maximized\n");
				m_pMenuControl->Validate(FALSE);
				m_pMenuIcon->Validate(FALSE);
			}

			RefreshBar();			
		}
	}	
}

void CMenuBar::OnInitMenuPopup()
{
	CMenu menu;
	menu.Attach((HMENU)m_hWindowMenu);

	// scan for first window command
	int n = menu.GetMenuItemCount();
	BOOL bAddSeperator = TRUE;
	for (int iPos=0; iPos<n; iPos++) {
		if (menu.GetMenuItemID(iPos) >= AFX_IDM_FIRST_MDICHILD) {
			bAddSeperator = FALSE;
			break;
		}
	}

	// iPos is either first window item, or end if none found.

	// delete everything after.
	while (iPos < (int)menu.GetMenuItemCount())
		menu.RemoveMenu(iPos, MF_BYPOSITION);

	// get active window so I can check its menu item
	ASSERT(m_hWndMDIClient);
	HWND hwndActive = (HWND)::SendMessage(m_hWndMDIClient,
		WM_MDIGETACTIVE, 0, NULL);

	// append window names in the form "# title"
	int iWin=1;
	for (HWND hwnd=::GetWindow(m_hWndMDIClient, GW_CHILD);
		  hwnd;
		  hwnd = ::GetWindow(hwnd, GW_HWNDNEXT)) {

		if (bAddSeperator) {
			menu.InsertMenu(iPos++, MF_BYPOSITION|MF_SEPARATOR);
			bAddSeperator = FALSE;
		}

		// build item name and add it to the menu
		CString sWinName, sMenuItem;
		CWnd::FromHandle(hwnd)->GetWindowText(sWinName);
		sMenuItem.Format(_T("&%d  %s"), iWin++, (LPCTSTR)sWinName);
		menu.InsertMenu(iPos, MF_BYPOSITION,
			::GetDlgCtrlID(hwnd), sMenuItem);
		if (hwnd==hwndActive)
			menu.CheckMenuItem(iPos, MF_BYPOSITION|MF_CHECKED);
		iPos++;
	}
	menu.Detach();
}

void CMenuBar::OnSetMenu(HMENU hNewMenu, HMENU hWindowMenu)
{
	// We can get active MDI child window on this message!
	BOOL bMax;
	HWND hWndChild = GetActiveChildWnd(bMax);

	if (!m_hWndActiveChild || m_hWndActiveChild != hWndChild) {
		// active child window changed
		LTRACE("    active child window changed\n");
		m_hWndActiveChild = hWndChild;
		// tell MenuIcon child window has been changed
		m_pMenuIcon->OnActivateChildWnd(hWndChild);
	}

	LTRACE("CMenuBar::OnSetMenu\n");

	if (!m_hMenu || m_hMenu != hNewMenu) {	// menu changed
		LTRACE("    menu changed\n");
		LoadMenu(hNewMenu, hWindowMenu);	// set toolbar menu
		GetOwner()->SetMenu(NULL);			// clear frame menu
	}
}

HWND CMenuBar::GetActiveChildWnd(BOOL& bMaximized)
{
	if (!m_hWndMDIClient)
		return NULL;

	BOOL bMax = FALSE;
	HWND hWnd = (HWND)::SendMessage(m_hWndMDIClient,
		WM_MDIGETACTIVE, 0, (LPARAM)&bMax);
	bMaximized = bMax;

	return hWnd;
}


CMenuItem::CMenuItem()
{
	m_itemState = none;
	m_rcItem	= CRect(0, 0, 0, 0);
	m_sizeHorz	= CSize(0, 0);
	m_bHorz		= TRUE;
	m_bWrapped	= FALSE;
}

CMenuItem::~CMenuItem()
{

}

CPoint CMenuItem::ComputeMenuTrackPoint(CWnd* pWnd, HMENU hSubMenu, TPMPARAMS& tpm, CFont* pFont)
{
	ASSERT_VALID(pWnd);
	ASSERT_VALID(pFont);
	ASSERT(::IsMenu(hSubMenu));

	tpm.cbSize = sizeof(tpm);
	CRect& rcExclude = (CRect&)tpm.rcExclude;
	CWnd::GetDesktopWindow()->GetWindowRect(&rcExclude);

	CPoint pt;
	CRect  rcItem = m_rcItem;
	pWnd->ClientToScreen(&rcItem);

//	if (hSubMenu == NULL)		// it's possible no sub menu
//		return CPoint();

	if (m_bHorz) {			// horizontal
		int nCount = ::GetMenuItemCount(hSubMenu);
		ASSERT(nCount != -1);
		int cyPopup = nCount * rcItem.Height();	// I want it be not owner drawn but ordinary menu..

		if (rcItem.bottom + cyPopup > rcExclude.bottom) {
			pt = CPoint(rcItem.left, rcItem.top);	// over Screen
			rcExclude.top = rcItem.top;
		}
		else {
			pt = CPoint(rcItem.left, rcItem.bottom);
		}

		pt += CPoint(-1, 1);						// precisely same as DevStudio
	}
	else {										// vertical
		// we never get the width of popup up menu, but I will try
		int nCount = ::GetMenuItemCount(hSubMenu);
		ASSERT(nCount != -1);
	
		int cxPopup = 0;
		CWindowDC dc(NULL);

		CFont* pOldFont = dc.SelectObject(pFont);
		for (int i = 0; i < nCount; ++i) {
			char szName[256];		
			MENUITEMINFO info;
			::ZeroMemory(&info, sizeof(MENUITEMINFO));
			info.cbSize		= sizeof(MENUITEMINFO);
			info.fMask		= MIIM_ID | MIIM_TYPE;
			info.dwTypeData = szName;
			info.cch		= sizeof(szName);
			::GetMenuItemInfo(hSubMenu, i, TRUE, &info);
			
			CString strItem(szName);

			CRect rcText(0, 0, 0, 0);
			dc.DrawText(strItem, &rcText,
				DT_SINGLELINE | DT_VCENTER | DT_CALCRECT | DT_NOPREFIX);
			int cxOffset = ::GetSystemMetrics(SM_CXMENUCHECK) * 2;
			cxPopup = max(rcText.Width() + 4*2 + CXGAP*2 + cxOffset, cxPopup);
		}
		dc.SelectObject(pOldFont);

		if (rcItem.right + cxPopup > rcExclude.right) {	// over right-side
			pt = CPoint(rcItem.left, rcItem.top);
			rcExclude.left = rcItem.left;
		}
		else {
			pt = CPoint(rcItem.right, rcItem.top);
			rcExclude.right = rcItem.right;
		}

		pt += CPoint(1, -1);							// precisely same as DevStudio
	}

	return pt;
}

CMenuIcon::CMenuIcon(CWnd* pWnd)
{
	m_hDocIcon = NULL;//hIcon;
	m_sizeHorz = CSize(::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
	m_bValid = FALSE;
	m_hSysMenu = NULL;//::GetSystemMenu(pWnd->GetSafeHwnd(), FALSE);
}

CMenuIcon::~CMenuIcon()
{

}

void CMenuIcon::Update(CDC* pDC)
{
	ASSERT(m_hDocIcon);
	ASSERT(m_bValid);

	::DrawIconEx(pDC->m_hDC, m_rcItem.left, m_rcItem.top, m_hDocIcon,
		m_sizeHorz.cx, m_sizeHorz.cy, 0, NULL, DI_NORMAL);
}

void CMenuIcon::TrackPopup(CWnd* pWnd)
{
	ASSERT(m_hDocIcon);
	ASSERT(m_bValid);
	ASSERT(::IsMenu(m_hSysMenu));

	NONCLIENTMETRICS info;
	info.cbSize = sizeof(info);
	::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
	CFont fontSys;
	if (!fontSys.CreateFontIndirect(&info.lfMenuFont))
		return;

	TPMPARAMS tpm;
	CPoint pt = ComputeMenuTrackPoint(pWnd, m_hSysMenu, tpm, &fontSys);

	::TrackPopupMenuEx(m_hSysMenu,
		TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
		pt.x, pt.y, pWnd->GetOwner()->GetSafeHwnd(), &tpm);
}

void CMenuIcon::Layout(CPoint point, BOOL bHorz)
{
	ASSERT(m_bValid);

	m_bHorz = bHorz;
	m_rcItem = CRect(point, m_sizeHorz);
}

void CMenuIcon::OnActivateChildWnd(HWND hWndChild)
{
	//TRACE("CMenuIcon::OnActivateChildWnd\n");
	ASSERT(::IsWindow(hWndChild));
	m_hSysMenu = ::GetSystemMenu(hWndChild, FALSE);
	ASSERT(::IsMenu(m_hSysMenu));
	m_hDocIcon = (HICON)GetClassLong(hWndChild, GCL_HICONSM);
}


BOOL CMenuControl::OnMouseMsg(UINT msg, UINT nFlags, CPoint pt)
{
	if (!IsValid())
		return FALSE;

	if (msg == WM_LBUTTONDOWN) {
		m_nTracking = HitTest(pt);
		if (m_nTracking >= 0) {
			CClientDC dc(m_pBar);
			DrawControl(&dc, m_nTracking, TRUE);
			m_bDown = TRUE;
			m_pBar->SetCapture();				  // grab mouse input
			return TRUE;
		}
	}
	else if ((msg == WM_MOUSEMOVE) && m_nTracking >= 0) {
		// mouse moved, and I am tracking: possibly draw button up/down
		BOOL bOldDown = m_bDown;
		m_bDown = m_arrCaption[m_nTracking].PtInRect(pt);
		if (bOldDown != m_bDown) {
			// up/down state changed: need to redraw button
			CClientDC dc(m_pBar);
			DrawControl(&dc, m_nTracking, m_bDown);
		}
		return TRUE; // handled

	}
	else if (msg == WM_LBUTTONUP && m_nTracking >= 0) {
		// user released the mouse and I am tracking: do button command
		ReleaseCapture(); // let go the mouse
		if (m_bDown) {
			// if button was down when released: draw button up, and do system cmd
			CClientDC dc(m_pBar);
			DrawControl(&dc, m_nTracking, FALSE);
			CFrameWnd* pFrame = m_pBar->GetTopLevelFrame()->GetActiveFrame();
			ASSERT_VALID(pFrame);
			static syscmd[3] =
			{ /*SC_MOUSEMENU,*/ SC_CLOSE, SC_RESTORE, SC_MINIMIZE };
			pFrame->SendMessage(WM_SYSCOMMAND, syscmd[m_nTracking]);
		}
		m_nTracking = -1; // stop tracking
		return TRUE;	  // handled (eat)
	}

	return FALSE;
}

int CMenuControl::HitTest(CPoint point)
{
	for (int i = 0; i < 3; ++i) {
		if (m_arrCaption[i].PtInRect(point))
			return i;
	}

	return -1;
}


void CMenuControl::DrawControl(CDC* pDC, int nIndex, BOOL bPressed)
{
	// draw frame controls
	CRect& rc = m_arrCaption[nIndex];
	static UINT dfcs[3] = { DFCS_CAPTIONCLOSE, DFCS_CAPTIONRESTORE, DFCS_CAPTIONMIN };
	UINT uState = dfcs[nIndex];
	if (bPressed)
		uState |= DFCS_PUSHED;

	pDC->DrawFrameControl(rc, DFC_CAPTION, uState);
}

void CMenuControl::ForceDrawControl(CDC * pDC)
{
	if (!IsValid())
		return;

	for (int i = 0; i < 3; ++i) {
		DrawControl(pDC, i, FALSE);
	}
}


namespace {

	const int CXTEXTMARGIN	  = 5;
	int CYTEXTMARGIN		  = 0;
	const int CYTEXTMARGINMIN = 2;
	
	CFont fontHorz, fontVert;

//	const int CXGAP = 3;

}// anonymouse namespace

CMenuButton::CMenuButton(HMENU hMenu, int nIndex)
{
	ASSERT(hMenu);
	InitButtonStringAndSubMenuHandle(hMenu, nIndex);
	InitHorizontalButtonSize();
	InitAccessKeyAndVerticalLinePoint();
}

CMenuButton::~CMenuButton()
{

}

BOOL CMenuButton::InitCommonResource()
{
	// clean up
	fontHorz.DeleteObject();
	fontVert.DeleteObject();

	// create fonts
	NONCLIENTMETRICS info;
	info.cbSize = sizeof(info);
	::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
	if (!fontHorz.CreateFontIndirect(&info.lfMenuFont))
		return FALSE;

	info.lfMenuFont.lfEscapement = -900;
	info.lfMenuFont.lfOrientation = -900;
	if (!fontVert.CreateFontIndirect(&info.lfMenuFont))
		return FALSE;


	// get font height
	LOGFONT logfont;
	fontHorz.GetLogFont(&logfont);
	int cyFont = abs(logfont.lfHeight);

	// calc Y text margin
	int cyText = cyFont + CYTEXTMARGINMIN*2;
	int cyMenu = ::GetSystemMetrics(SM_CYMENU);
	if (cyMenu > cyText) {
		CYTEXTMARGIN = (cyMenu - cyFont)/2;
	}
	else {
		CYTEXTMARGIN = CYTEXTMARGINMIN;
	}

	return TRUE;
}

void CMenuButton::InitButtonStringAndSubMenuHandle(HMENU hMenu, int nIndex)
{
	// get menu button Text
	char szText[256];		
	MENUITEMINFO info; ::ZeroMemory(&info, sizeof(MENUITEMINFO));
	info.cbSize		= sizeof(MENUITEMINFO);
	info.fMask		= MIIM_ID | MIIM_TYPE;
	info.dwTypeData = szText;
	info.cch		= sizeof(szText);
	::GetMenuItemInfo(hMenu, nIndex, TRUE, &info);
	m_strBtn = CString(szText);

	m_hSubMenu = ::GetSubMenu(hMenu, nIndex);
}

void CMenuButton::InitHorizontalButtonSize()
{
	// get menu button Text size
	ASSERT(m_strBtn.IsEmpty() == FALSE);

	CWindowDC dc(NULL);
	CRect rcText(0, 0, 0, 0);
	CFont* pOldFont = dc.SelectObject(&fontHorz);
	dc.DrawText(m_strBtn, &rcText, DT_SINGLELINE | DT_CALCRECT);
	dc.SelectObject(pOldFont);
	m_sizeHorz.cx = rcText.Width() + CXTEXTMARGIN*2;

	LOGFONT logfont;
	fontHorz.GetLogFont(&logfont);
	int cyFont = abs(logfont.lfHeight);

	//this is decide the height of the menubar button
	m_sizeHorz.cy = cyFont + CYTEXTMARGIN*2+2;
}

void CMenuButton::InitAccessKeyAndVerticalLinePoint()
{
	int nIndex = m_strBtn.Find('&');
	m_cAccessKey = m_strBtn[nIndex + 1];	// -1 + 1 = 0; it's ok

	if (nIndex == -1) {
		m_ptLineFrom = m_ptLineTo = CPoint(0, 0);
	}
	else if (nIndex == 0) {
		CRect rcTo;
		CWindowDC dc(NULL);
		CFont* pOldFont = dc.SelectObject(&fontHorz);
		dc.DrawText(m_strBtn.Left(nIndex+2), &rcTo, DT_SINGLELINE | DT_CALCRECT);
		dc.SelectObject(pOldFont);

		m_ptLineFrom = CPoint(CYTEXTMARGIN, CXTEXTMARGIN);
		m_ptLineTo	 = CPoint(CYTEXTMARGIN, CXTEXTMARGIN + rcTo.Width());
	}
	else {
		CRect rcFrom, rcTo;
		CWindowDC dc(NULL);
		CFont* pOldFont = dc.SelectObject(&fontHorz);
		dc.DrawText(m_strBtn.Left(nIndex), &rcFrom, DT_SINGLELINE | DT_CALCRECT);
		dc.DrawText(m_strBtn.Left(nIndex+2), &rcTo, DT_SINGLELINE | DT_CALCRECT);
		dc.SelectObject(pOldFont);

		m_ptLineFrom = CPoint(CYTEXTMARGIN, CXTEXTMARGIN + rcFrom.Width());
		m_ptLineTo = CPoint(CYTEXTMARGIN, CXTEXTMARGIN + rcTo.Width());
	}
}

void CMenuButton::Layout(CPoint point, BOOL bHorz)
{
	if (bHorz == TRUE) {
		//this can change the rect of menubutton modify by xhs
		CPoint p(point.x-3,point.y-2);
		m_rcItem = CRect(p, m_sizeHorz);
	}
	else {
		m_rcItem = CRect(point, CSize(m_sizeHorz.cy, m_sizeHorz.cx));
	}

	m_bHorz = bHorz;
}

void CMenuButton::Update(CDC* pDC)
{
	// clean background
	COLORREF clr = ::GetSysColor(COLOR_BTNFACE);
	pDC->FillSolidRect(m_rcItem, clr);

	switch (m_itemState) {
	case hot:
		DrawHot(pDC);
		break;
	case select:
		DrawSelect(pDC);
		break;
	case none:
		DrawNone(pDC);
		break;
	default:
		ASSERT(TRUE);
	}
}

void CMenuButton::TrackPopup(CWnd* pWnd)
{
	TPMPARAMS tpm;
	CPoint pt = ComputeMenuTrackPoint(pWnd, m_hSubMenu, tpm, &fontHorz);

	::TrackPopupMenuEx(m_hSubMenu,
		TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
		pt.x, pt.y, pWnd->GetOwner()->GetSafeHwnd(), &tpm);
}

void CMenuButton::DrawHorzText(CDC* pDC, CPoint ptOffset)
{
	CRect rcBtn = m_rcItem;
	pDC->SetBkMode(TRANSPARENT);
	CFont* pOldFont = pDC->SelectObject(&fontHorz);
	// I know precise text size
	rcBtn.DeflateRect(CXTEXTMARGIN, CYTEXTMARGIN);
	pDC->DrawText(m_strBtn, rcBtn + ptOffset,
		DT_SINGLELINE);// | DT_CENTER | DT_VCENTER);	no need
	
	pDC->SelectObject(pOldFont);
}

void CMenuButton::DrawVertText(CDC* pDC, CPoint ptOffset)
{
	CRect rcBtn = m_rcItem;
	int nLength = m_strBtn.GetLength();
	int nIndex = m_strBtn.Find('&');
	CString strBtn = m_strBtn.Left(nIndex) + m_strBtn.Right(nLength - (nIndex+1));

	pDC->SetBkMode(TRANSPARENT);
	CFont* pOldFont = pDC->SelectObject(&fontVert);
	// I must know precise text size
	CRect rcString = CRect(
		CPoint(rcBtn.right - CYTEXTMARGIN, rcBtn.top + CXTEXTMARGIN), m_sizeHorz);
	pDC->DrawText(strBtn, rcString + ptOffset,
		DT_SINGLELINE | DT_NOCLIP | DT_NOPREFIX);	// don't forget DT_NOCLIP
	
	pDC->SelectObject(pOldFont);

	// DrawText is poor, so we have to draw vertical line by ourselves
	pDC->MoveTo(rcBtn.TopLeft() + m_ptLineFrom + ptOffset);
	pDC->LineTo(rcBtn.TopLeft() + m_ptLineTo + ptOffset);
}

void CMenuButton::DrawHot(CDC* pDC)
{
	if (m_bHorz) {
		// draw down button
		pDC->DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
		DrawHorzText(pDC);
	}
	else {
		pDC->DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
		DrawVertText(pDC);
	}
}

void CMenuButton::DrawSelect(CDC* pDC)
{
	if (m_bHorz) {
		// draw pressed button
		pDC->DrawEdge(m_rcItem, BDR_SUNKENOUTER, BF_RECT);
		DrawHorzText(pDC, CPoint(1, 1));
	}
	else {
		pDC->DrawEdge(m_rcItem, BDR_SUNKENOUTER, BF_RECT);
		DrawVertText(pDC, CPoint(1, 1));
	}
}

void CMenuButton::DrawNone(CDC* pDC)
{
	if (m_bHorz) {
		DrawHorzText(pDC);
	}
	else {
		DrawVertText(pDC);
	}
}

int CMenuBar::OnActivateFrame(int nCmdShow)
{
	CFrameWnd* pFrame = GetParentFrame();
	ASSERT_VALID(pFrame);
	if (pFrame->GetMenu() != NULL) {
		LTRACE(" has menu\n");
		pFrame->SetMenu(NULL);
	}
	m_nCmdShow = nCmdShow;
	return SW_HIDE;
}