/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
// CommandBarCtrl.cpp: CCommandBarCtrl クラスのインプリメンテーション

#include "stdafx.h"
#include "CommandBarCtrl.h"
#include "Misc/BitmapHandle.h"

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

namespace{
	class CMenuItemInfo : public MENUITEMINFO{
	public:
		CMenuItemInfo(){
			memset(this, 0, sizeof(MENUITEMINFO));
			cbSize = sizeof(MENUITEMINFO);
		}
	};

//	int newCount = 0;
}

CCommandBarCtrl::CCommandBarCtrl()
 : m_hMenu(0),m_hImageList(0),
m_dwExtendedStyle(CBR_EX_TRANSPARENT | CBR_EX_SHAREMENU | CBR_EX_TRACKALWAYS),
m_bAttachedMenu(false), m_bAlphaImages(false), m_bMenuActive(false),
m_bImagesVisible(true), m_bFlatMenus(false),
m_bShowKeyboardCues(false),
m_clrMask(RGB(192, 192, 192)), m_cxExtraSpacing(0)
{
	SetImageSize(16, 15);	// default
}

CCommandBarCtrl::~CCommandBarCtrl()
{
	if(m_hMenu != 0 && (m_dwExtendedStyle & CBR_EX_SHAREMENU) == 0)
		::DestroyMenu(m_hMenu);

	if(m_hImageList != 0)
		::ImageList_Destroy(m_hImageList);

//	TRACE("newCount: %d ~CCommandBarCtrl\n", newCount);
}
#if 0
BOOL CCommandBarCtrl::AttachMenu(HMENU hMenu){
//	ASSERT(::IsWindow(m_hWnd));
	ASSERT(::IsMenu(hMenu));
	if(hMenu != 0 && !IsMenu(hMenu))
		return FALSE;

	// Destroy old menu, if needed, and set new one.
	if(m_hMenu && (m_dwExtendedStyle & CBR_EX_SHAREMENU) == 0)
		::DestroyMenu(m_hMenu);

	m_hMenu = hMenu;

	if(m_bAttachedMenu)
		return TRUE;

//	SetRedraw(FALSE);
	// Clear all
	BOOL bRet;
/*	int nCount = m_wndToolBar.GetToolBarCtrl().GetButtonCount();
	for(int i = 0; i < nCount; i++)
	{
		bRet = m_wndToolBar.GetToolBarCtrl().DeleteButton(0);
		ASSERT(bRet);
	}*/

	// Add buttons for each menu item
	if(m_hMenu){
		int nItems = ::GetMenuItemCount(m_hMenu);
		TCHAR szString[_nMaxMenuItemTextLength];
		for(int i = 0; i < nItems; i++){
			CMenuItemInfo mii;
			mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_SUBMENU;
			mii.fType = MFT_STRING;
			mii.dwTypeData = szString;
			mii.cch = _nMaxMenuItemTextLength;
			bRet = ::GetMenuItemInfo(m_hMenu, i, TRUE, &mii);
			ASSERT(bRet);
			// If we have more than the buffer, we assume we have bitmaps bits

			if(lstrlen(szString) > _nMaxMenuItemTextLength - 1){
				mii.fType = MFT_BITMAP;
				::SetMenuItemInfo(m_hMenu, i, TRUE, &mii);
				szString[0] = 0;
			}

			// NOTE: Command Bar currently supports only drop-down menu items
			ASSERT(mii.hSubMenu != 0);
/*
			TBBUTTON btn;
			btn.iBitmap = 0;
			btn.idCommand = i;
			btn.fsState = (BYTE)(((mii.fState & MFS_DISABLED) == 0) ? TBSTATE_ENABLED : 0);
			btn.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE | TBSTYLE_DROPDOWN;
			btn.dwData = 0;
			btn.iString = 0;

			bRet = m_wndToolBar.GetToolBarCtrl().InsertButton(-1, &btn);
			ASSERT(bRet);

			TBBUTTONINFO bi;
			memset(&bi, 0, sizeof(bi));
			bi.cbSize = sizeof(TBBUTTONINFO);
			bi.dwMask = TBIF_TEXT;
			bi.pszText = szString;

			bRet = m_wndToolBar.GetToolBarCtrl().SetButtonInfo(i, &bi);
			ASSERT(bRet);
*/
		}
	}

//	SetRedraw(TRUE);
//	Invalidate();
//	UpdateWindow();
	return TRUE;
}
#endif

bool CCommandBarCtrl::CreateInternalImageList(int cImages){
	UINT uFlags = (m_bAlphaImages ? ILC_COLOR32 : ILC_COLOR) | ILC_MASK;
	m_hImageList = ::ImageList_Create(m_szBitmap.cx, m_szBitmap.cy, uFlags, cImages, 1);
	ASSERT(m_hImageList != 0);
	return (m_hImageList != 0);
}

// リソース漏れ注意
BOOL CCommandBarCtrl::DitherBlt(
	CDC& mydc,
	int x, int y, 
	int nWidth, int nHeight, 
	HDC hSrcDC, HBITMAP hBitmap, 
	int xSrc, int ySrc,
	HBRUSH hBrushBackground,
	HBRUSH hBrush3DEffect,
	HBRUSH hBrushDisabledImage
){
	ASSERT(mydc.GetSafeHdc() || hBitmap != 0);
	ASSERT(nWidth > 0 && nHeight > 0);
	
	// Create a generic DC for all BitBlts
	CDC dc;
	dc.Attach((hSrcDC != 0) ? hSrcDC : ::CreateCompatibleDC(mydc));
	ASSERT(dc.GetSafeHdc());
	if(!dc.GetSafeHdc())
		return FALSE;
	
	// Create a DC for the monochrome DIB section
	CDC dcBW;
	dcBW.CreateCompatibleDC(&mydc);
	ASSERT(dcBW.m_hDC != 0);
	if(dcBW.m_hDC == 0){
		if(hSrcDC == 0)
			dc.DeleteDC();
		return FALSE;
	}

	// Create the monochrome DIB section with a black and white palette
	struct RGBBWBITMAPINFO
	{
		BITMAPINFOHEADER bmiHeader; 
		RGBQUAD bmiColors[2]; 
	};

	RGBBWBITMAPINFO rgbBWBitmapInfo = 
	{
		{ sizeof(BITMAPINFOHEADER), nWidth, nHeight, 1, 1, BI_RGB, 0, 0, 0, 0, 0 },
		{ { 0x00, 0x00, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF, 0x00 } }
	};

	VOID* pbitsBW;
	CBitmap bmpBW;
	bmpBW.Attach(::CreateDIBSection(dcBW, (LPBITMAPINFO)&rgbBWBitmapInfo, DIB_RGB_COLORS, &pbitsBW, 0, 0));
	ASSERT(bmpBW.m_hObject != 0);
	if(bmpBW.m_hObject == 0){
		if(hSrcDC == 0)
			dc.DeleteDC();
		return FALSE;
	}
	
	// Attach the monochrome DIB section and the bitmap to the DCs
	HBITMAP hbmOldBW = (HBITMAP)dcBW.SelectObject(bmpBW);
	HBITMAP hbmOldDC = 0;
	if(hBitmap != 0)
		hbmOldDC = (HBITMAP)dc.SelectObject(hBitmap);

	// Block: Dark gray removal: we want (128, 128, 128) pixels to become black and not white
	{
		CDC dcTemp1;
		dcTemp1.Attach(::CreateCompatibleDC(mydc));
		CDC dcTemp2;
		dcTemp2.Attach(::CreateCompatibleDC(mydc));

		CBitmap bmpTemp1;
		bmpTemp1.CreateCompatibleBitmap(&dc, nWidth, nHeight);
		CBitmap bmpTemp2;
		bmpTemp2.CreateBitmap(nWidth, nHeight, 1, 1, 0);

		HBITMAP hOldBmp1 = (HBITMAP)dcTemp1.SelectObject(bmpTemp1);
		HBITMAP hOldBmp2 = (HBITMAP)dcTemp2.SelectObject(bmpTemp2);

		// Let's copy our image, it will be altered
		dcTemp1.BitBlt(0, 0, nWidth, nHeight, &dc, xSrc, ySrc, SRCCOPY);

		// All dark gray pixels will become white, the others black
		dcTemp1.SetBkColor(RGB(128, 128, 128));
		dcTemp2.BitBlt(0, 0, nWidth, nHeight, &dcTemp1, 0, 0, SRCCOPY);

		// Do an XOR to set to black these white pixels
		dcTemp1.BitBlt(0, 0, nWidth, nHeight, &dcTemp2, 0, 0, SRCINVERT);

		// BitBlt the bitmap into the monochrome DIB section
		// The DIB section will do a true monochrome conversion
		// The magenta background being closer to white will become white
		dcBW.BitBlt(0, 0, nWidth, nHeight, &dcTemp1, 0, 0, SRCCOPY);

		// Cleanup
		dcTemp1.SelectObject(hOldBmp1);
		dcTemp2.SelectObject(hOldBmp2);
	}
	
	// Paint the destination rectangle using hBrushBackground
	CRect rc(x, y, x + nWidth, y + nHeight);
	mydc.FillRect(rc, CBrush::FromHandle(hBrushBackground));

	// BitBlt the black bits in the monochrome bitmap into hBrush3DEffect color in the destination DC
	// The magic ROP comes from the Charles Petzold's book
	HBRUSH hOldBrush = (HBRUSH)mydc.SelectObject(hBrush3DEffect);
	mydc.BitBlt(x + 1, y + 1, nWidth, nHeight, &dcBW, 0, 0, 0xB8074A);

	// BitBlt the black bits in the monochrome bitmap into hBrushDisabledImage color in the destination DC
	mydc.SelectObject(hBrushDisabledImage);
	mydc.BitBlt(x, y, nWidth, nHeight, &dcBW, 0, 0, 0xB8074A);

	mydc.SelectObject(hOldBrush);
	dcBW.SelectObject(hbmOldBW);
	dc.SelectObject(hbmOldDC);

	if(hSrcDC == 0)
		dc.DeleteDC();

	return TRUE;
}

BOOL CCommandBarCtrl::DrawCheckmark(
	CDC& dc,
	const CRect& rc,
	BOOL bSelected,
	BOOL bDisabled,
	BOOL bRadio,
	HBITMAP hBmpCheck
){
	// get checkmark bitmap, if none, use Windows standard
	CSize size(0, 0);
	CBitmapHandle bmp = hBmpCheck;
	if(hBmpCheck){
		bmp.GetSize(size);
	}else{
		size.cx = ::GetSystemMetrics(SM_CXMENUCHECK); 
		size.cy = ::GetSystemMetrics(SM_CYMENUCHECK); 
		bmp.CreateCompatibleBitmap(dc, size.cx, size.cy);
		ASSERT(bmp.m_hBitmap != 0);
	}
	// center bitmap in caller's rectangle
	CRect rcDest(rc);
	if(rc.Width() > size.cx){
		rcDest.left = rc.left + (rc.Width() - size.cx) / 2;
		rcDest.right = rcDest.left + size.cx;
	}
	if(rc.Height() > size.cy){
		rcDest.top = rc.top + (rc.Height() - size.cy) / 2;
		rcDest.bottom = rcDest.top + size.cy;
	}
	// paint background
	if(!m_bFlatMenus && !bDisabled){
		if(bSelected){
			dc.FillRect(&rcDest, CBrush::FromHandle(::GetSysColorBrush(COLOR_MENU)));
		}else{
			COLORREF clrTextOld = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE));
			COLORREF clrBkOld = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT));
			CBrush* hbr = CDC::GetHalftoneBrush();
			dc.SetBrushOrg(rcDest.left, rcDest.top);
			dc.FillRect(&rcDest, hbr);
			dc.SetTextColor(clrTextOld);
			dc.SetBkColor(clrBkOld);
		}
	}

	// create source image
	CDC dcSource;
	dcSource.CreateCompatibleDC(&dc);

	HBITMAP hBmpOld = (HBITMAP)dcSource.SelectObject(bmp.m_hBitmap);
	// set colors
	const COLORREF clrBlack = RGB(0, 0, 0);
	const COLORREF clrWhite = RGB(255, 255, 255);
	COLORREF clrTextOld = dc.SetTextColor(clrBlack);
	COLORREF clrBkOld = dc.SetBkColor(clrWhite);
	// create mask
	CDC dcMask;
	dcMask.CreateCompatibleDC(&dc);
	CBitmap bmpMask;
	bmpMask.CreateBitmap(size.cx, size.cy, 1, 1, 0);
	HBITMAP hBmpOld1 = (HBITMAP)dcMask.SelectObject(bmpMask);

	// draw the checkmark transparently
	int cx = rcDest.Width();
	int cy = rcDest.Height();
	if(hBmpCheck){
		// build mask based on transparent color	
		dcSource.SetBkColor(m_clrMask);
		dcMask.SetBkColor(clrBlack);
		dcMask.SetTextColor(clrWhite);
		dcMask.BitBlt(0, 0, size.cx, size.cy, &dcSource, 0, 0, SRCCOPY);
		// draw bitmap using the mask
		dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcSource, 0, 0, SRCINVERT);
		dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcMask, 0, 0, SRCAND);
		dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcSource, 0, 0, SRCINVERT);
	}else{
		const DWORD ROP_DSno = 0x00BB0226L;
		const DWORD ROP_DSa = 0x008800C6L;
		const DWORD ROP_DSo = 0x00EE0086L;
		const DWORD ROP_DSna = 0x00220326L;

		// draw mask
		CRect rcSource(0, 0, __min(size.cx, rc.Width()), __min(size.cy, rc.Height()));
		dcMask.DrawFrameControl(&rcSource, DFC_MENU, bRadio ? DFCS_MENUBULLET : DFCS_MENUCHECK);

		// draw shadow if disabled
		if(!m_bFlatMenus && bDisabled){
			// offset by one pixel
			int x = rcDest.left + 1;
			int y = rcDest.top + 1;
			// paint source bitmap
			const int nColor = COLOR_3DHILIGHT;
			dcSource.FillRect(rcSource, CBrush::FromHandle(::GetSysColorBrush(nColor)));
			// draw checkmark - special case black and white colors
			COLORREF clrCheck = ::GetSysColor(nColor);
			if(clrCheck == clrWhite){
				dc.BitBlt(x, y, cx, cy, &dcMask,  0, 0,   ROP_DSno);
				dc.BitBlt(x, y, cx, cy, &dcSource, 0, 0, ROP_DSa);
			}else{
				if(clrCheck != clrBlack){
					ASSERT(dcSource.GetTextColor() == clrBlack);
					ASSERT(dcSource.GetBkColor() == clrWhite);
					dcSource.BitBlt(0, 0, size.cx, size.cy, &dcMask, 0, 0, ROP_DSna);
				}
				dc.BitBlt(x, y, cx, cy, &dcMask,  0,  0,  ROP_DSa);
				dc.BitBlt(x, y, cx, cy, &dcSource, 0, 0, ROP_DSo);
			}
		}

		// paint source bitmap
		const int nColor = bDisabled ? COLOR_BTNSHADOW : COLOR_MENUTEXT;
		dcSource.FillRect(rcSource, CBrush::FromHandle(::GetSysColorBrush(nColor)));
		// draw checkmark - special case black and white colors
		COLORREF clrCheck = ::GetSysColor(nColor);
		if(clrCheck == clrWhite){
			dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcMask,  0, 0,   ROP_DSno);
			dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcSource, 0, 0, ROP_DSa);
		}else{
			if(clrCheck != clrBlack){
				ASSERT(dcSource.GetTextColor() == clrBlack);
				ASSERT(dcSource.GetBkColor() == clrWhite);
				dcSource.BitBlt(0, 0, size.cx, size.cy, &dcMask, 0, 0, ROP_DSna);
			}
			dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcMask,  0,  0,  ROP_DSa);
			dc.BitBlt(rcDest.left, rcDest.top, cx, cy, &dcSource, 0, 0, ROP_DSo);
		}
	}
	// restore all
	dc.SetTextColor(clrTextOld);			
	dc.SetBkColor(clrBkOld);
	dcSource.SelectObject(hBmpOld);
	dcMask.SelectObject(hBmpOld1);
	if(hBmpCheck == 0)
		bmp.DeleteObject();

	// draw pushed-in hilight
	if(!m_bFlatMenus && !bDisabled){
		if(rc.right - rc.left > size.cx)
			::InflateRect(&rcDest, 1,1);	// inflate checkmark by one pixel all around
		dc.DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT);
	}
	return TRUE;
}

void CCommandBarCtrl::DrawBitmapDisabled(
	CDC& dc, int nImage, POINT point,
	HBRUSH hBrushBackground,
	HBRUSH hBrush3DEffect,
	HBRUSH hBrushDisabledImage
){
#if (_WIN32_WINNT >= 0x0501)
	if(m_bAlphaImages){
		IMAGELISTDRAWPARAMS ildp;
		memset(&ildp, 0, sizeof(ildp));
		ildp.cbSize = sizeof(IMAGELISTDRAWPARAMS);
		ildp.himl = m_hImageList;
		ildp.i = nImage;
		ildp.hdcDst = dc;
		ildp.x = point.x;
		ildp.y = point.y;
		ildp.cx = 0;
		ildp.cy = 0;
		ildp.xBitmap = 0;
		ildp.yBitmap = 0;
		ildp.fStyle = ILD_TRANSPARENT;
		ildp.fState = ILS_SATURATE;
		ildp.Frame = (DWORD)-100;
		::ImageList_DrawIndirect(&ildp);
	}else
#endif //(_WIN32_WINNT >= 0x0501)
	{
		// create memory DC
		CDC dcMem;
		dcMem.CreateCompatibleDC(&dc);
		// create mono or color bitmap
		CBitmap bmp;
		bmp.CreateCompatibleBitmap(&dc, m_szBitmap.cx, m_szBitmap.cy);
		ASSERT(bmp.GetSafeHandle());
		// draw image into memory DC--fill BG white first

		HBITMAP hBmpOld = (HBITMAP)dcMem.SelectObject(bmp);
		dcMem.PatBlt(0, 0, m_szBitmap.cx, m_szBitmap.cy, WHITENESS);
		// If white is the text color, we can't use the normal painting since
		// it would blend with the WHITENESS, but the mask is OK
		UINT uDrawStyle = (::GetSysColor(COLOR_BTNTEXT) == RGB(255, 255, 255)) ? ILD_MASK : ILD_NORMAL;
		::ImageList_Draw(m_hImageList, nImage, dcMem, 0, 0, uDrawStyle);

		DitherBlt(dc, point.x, point.y, m_szBitmap.cx, m_szBitmap.cy, dcMem, 0, 0, 0, hBrushBackground, hBrush3DEffect, hBrushDisabledImage);
		dcMem.SelectObject(hBmpOld); // restore
	}
}

void CCommandBarCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct){
	if(m_bFlatMenus){
//		DrawItemFlat(lpDrawItemStruct);
	}else{
		DrawItem3D(lpDrawItemStruct);
	}
}

void CCommandBarCtrl::DrawItem3D(LPDRAWITEMSTRUCT lpDrawItemStruct){
	_MenuItemData* pmd = (_MenuItemData*)lpDrawItemStruct->itemData;
	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);
	CRect rcItem = lpDrawItemStruct->rcItem;

	if(pmd->fType & MFT_SEPARATOR){
		// draw separator
		CRect rc = rcItem;
		rc.top += rc.Height() / 2;// vertical center
		dc.DrawEdge(rc, EDGE_ETCHED, BF_TOP);// draw separator line
	}else{
		BOOL bDisabled = lpDrawItemStruct->itemState & ODS_GRAYED;
		BOOL bSelected = lpDrawItemStruct->itemState & ODS_SELECTED;
		BOOL bChecked = lpDrawItemStruct->itemState & ODS_CHECKED;
		BOOL bHasImage = FALSE;

		if(LOWORD(lpDrawItemStruct->itemID) == (WORD)-1)
			bSelected = FALSE;

		CRect rcButn(rcItem.left, rcItem.top, rcItem.left + m_szButton.cx, rcItem.top + m_szButton.cy);// button rect
		rcButn.OffsetRect(0, (rcItem.Height() - rcButn.Height()) / 2);// center vertically

		int iButton = pmd->iButton;
		if(iButton >= 0){
			bHasImage = TRUE;

			// calc drawing point
			CSize sz(rcButn.Width() - m_szBitmap.cx, rcButn.Height() - m_szBitmap.cy);
			sz.cx /= 2;
			sz.cy /= 2;
			POINT point = { rcButn.left + sz.cx, rcButn.top + sz.cy };

			// draw disabled or normal
			if(!bDisabled){
				// normal - fill background depending on state
				if(!bChecked || bSelected){
					CBrush* br = CBrush::FromHandle(::GetSysColorBrush((bChecked && !bSelected) ? COLOR_3DLIGHT : COLOR_MENU));
					dc.FillRect(rcButn, br);
				}else{
					COLORREF crTxt = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE));
					COLORREF crBk = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT));
					
					CBrush* hbr = CDC::GetHalftoneBrush();
					dc.SetBrushOrg(rcButn.left, rcButn.top);
					dc.FillRect(rcButn, hbr);
					dc.SetTextColor(crTxt);
					dc.SetBkColor(crBk);
				}

				// draw pushed-in or popped-out edge
				if(bSelected || bChecked){
					dc.DrawEdge(rcButn, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER, BF_RECT);
				}
				// draw the image
				::ImageList_Draw(m_hImageList, iButton, dc, point.x, point.y, ILD_TRANSPARENT);
			}else{
				HBRUSH hBrushBackground = ::GetSysColorBrush(COLOR_MENU);
				DrawBitmapDisabled(dc, iButton, point, hBrushBackground);
			}
		}else{
			// no image - look for custom checked/unchecked bitmaps
			CMenuItemInfo info;
			info.fMask = MIIM_CHECKMARKS | MIIM_TYPE;
			::GetMenuItemInfo((HMENU)lpDrawItemStruct->hwndItem, lpDrawItemStruct->itemID, MF_BYCOMMAND, &info);
			if(bChecked || info.hbmpUnchecked != 0){
				BOOL bRadio = ((info.fType & MFT_RADIOCHECK) != 0);
				bHasImage = DrawCheckmark(dc, rcButn, bSelected, bDisabled, bRadio, bChecked ? info.hbmpChecked : info.hbmpUnchecked);
			}
		}

		// draw item text
		int cxButn = m_szButton.cx;
		COLORREF colorBG = ::GetSysColor(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU);
		if(bSelected || lpDrawItemStruct->itemAction == ODA_SELECT){
			CRect rcBG = rcItem;
			if(bHasImage)
				rcBG.left += cxButn + s_kcxGap;

			HBRUSH br = ::GetSysColorBrush(bSelected ? COLOR_HIGHLIGHT : COLOR_MENU);
			dc.FillRect(rcBG, CBrush::FromHandle(br));
		}

		// calc text rectangle and colors
		CRect rcText = rcItem;
		rcText.left += cxButn + s_kcxGap + s_kcxTextMargin;
		rcText.right -= cxButn;
		dc.SetBkMode(TRANSPARENT);
		COLORREF colorText = ::GetSysColor(bDisabled
			? (bSelected ? COLOR_GRAYTEXT : COLOR_3DSHADOW)
			: (bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT));

		// font already selected by Windows
		if(bDisabled && (!bSelected || colorText == colorBG)){
			// disabled - draw shadow text shifted down and right 1 pixel (unles selected)
			CRect rcDisabled = rcText;
			rcDisabled.OffsetRect(1, 1);
			DrawMenuText(dc, rcDisabled, pmd->lpstrText, ::GetSysColor(COLOR_3DHILIGHT));
		}
		DrawMenuText(dc, rcText, pmd->lpstrText, colorText); // finally!
	}
	dc.Detach();
}

void CCommandBarCtrl::DrawMenuText(CDC& dc, RECT& rc, LPCTSTR lpstrText, COLORREF color){
	int nTab = -1;
	for(int i = 0; i < lstrlen(lpstrText); i++){
		if(lpstrText[i] == '\t'){
			nTab = i;
			break;
		}
	}
	dc.SetTextColor(color);
	dc.DrawText(lpstrText, nTab, &rc, DT_SINGLELINE | DT_LEFT | DT_VCENTER | (m_bShowKeyboardCues ? 0 : DT_HIDEPREFIX));
	if(nTab != -1)
		dc.DrawText(&lpstrText[nTab + 1], -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_VCENTER | (m_bShowKeyboardCues ? 0 : DT_HIDEPREFIX));
}

void CCommandBarCtrl::GetSystemSettings(){
	// refresh our font
	NONCLIENTMETRICS info;
	info.cbSize = sizeof(info);
	BOOL bRet = ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
	ASSERT(bRet);
	if(bRet){
		LOGFONT logfont;
		memset(&logfont, 0, sizeof(LOGFONT));
		if(m_fontMenu.GetSafeHandle())
			m_fontMenu.GetLogFont(&logfont);

		if(logfont.lfHeight != info.lfMenuFont.lfHeight ||
		   logfont.lfWidth != info.lfMenuFont.lfWidth ||
		   logfont.lfEscapement != info.lfMenuFont.lfEscapement ||
		   logfont.lfOrientation != info.lfMenuFont.lfOrientation ||
		   logfont.lfWeight != info.lfMenuFont.lfWeight ||
		   logfont.lfItalic != info.lfMenuFont.lfItalic ||
		   logfont.lfUnderline != info.lfMenuFont.lfUnderline ||
		   logfont.lfStrikeOut != info.lfMenuFont.lfStrikeOut ||
		   logfont.lfCharSet != info.lfMenuFont.lfCharSet ||
		   logfont.lfOutPrecision != info.lfMenuFont.lfOutPrecision ||
		   logfont.lfClipPrecision != info.lfMenuFont.lfClipPrecision ||
		   logfont.lfQuality != info.lfMenuFont.lfQuality ||
		   logfont.lfPitchAndFamily != info.lfMenuFont.lfPitchAndFamily ||
		   lstrcmp(logfont.lfFaceName, info.lfMenuFont.lfFaceName) != 0)
		{
			HFONT hFontMenu = ::CreateFontIndirect(&info.lfMenuFont);
			ASSERT(hFontMenu != 0);
			if(hFontMenu != 0){
				if(m_fontMenu.GetSafeHandle())
					m_fontMenu.DeleteObject();

				m_fontMenu.Attach(hFontMenu);
				AfxGetMainWnd()->SetFont(&m_fontMenu); //!!!
//				AddStrings(_T("NS\0"));	// for proper item height
//				AutoSize();
			}
		}
	}

	// check if we need extra spacing for menu item text
//	CWindowDC dc(m_hWnd);
	CWindowDC dc(AfxGetMainWnd()); //!!!
	HFONT hFontOld = (HFONT)dc.SelectObject(m_fontMenu);

	CRect rcText(0, 0, 0, 0);
	dc.DrawText(_T("\t"), -1, rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT);
	if((rcText.Width()) < 4){
		rcText.SetRectEmpty();
		dc.DrawText(_T("x"), -1, rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT);
		m_cxExtraSpacing = rcText.right - rcText.left;
	}else{
		m_cxExtraSpacing = 0;
	}
	dc.SelectObject(hFontOld);
/*
	// get Windows version
	OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) };
	::GetVersionEx(&ovi);

	// query keyboard cues mode (Windows 2000 or later)
	if(ovi.dwMajorVersion >= 5)
	{
#ifndef SPI_GETKEYBOARDCUES
		const UINT SPI_GETKEYBOARDCUES = 0x100A;
#endif //!SPI_GETKEYBOARDCUES
		BOOL bRetVal = TRUE;
		bRet = ::SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &bRetVal, 0);
		m_bUseKeyboardCues = (bRet && !bRetVal);
		m_bAllowKeyboardCues = true;
		ShowKeyboardCues(!m_bUseKeyboardCues);
	}

	// query flat menu mode (Windows XP or later)
	if((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5))
	{
#ifndef SPI_GETFLATMENU
		const UINT SPI_GETFLATMENU = 0x1022;
#endif //!SPI_GETFLATMENU
		BOOL bRetVal = FALSE;
		bRet = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &bRetVal, 0);
		m_bFlatMenus = (bRet && bRetVal);
	}
	*/
}

BOOL CCommandBarCtrl::LoadImages(LPCTSTR lpszRes){
	bool bMapped = false;
	UINT nFlags = 0;
	LPCOLORMAP lpColorMap = 0;
	int nMapSize = 0;

//	ASSERT(::IsWindow(m_hWnd));	//!!!
	HINSTANCE hInstance = AfxGetResourceHandle();

	HRSRC hRsrc = ::FindResource(hInstance, lpszRes, (LPTSTR)RT_TOOLBAR);
	if(hRsrc == 0)
		return FALSE;

	HGLOBAL hGlobal = ::LoadResource(hInstance, hRsrc);
	if(hGlobal == 0)
		return FALSE;

	_ToolBarData* pData = (_ToolBarData*)::LockResource(hGlobal);
	if(pData == 0)
		return FALSE;
	ASSERT(pData->wVersion == 1);

	WORD* pItems = pData->items();
	int nItems = pData->wItemCount;

	// Set internal data
	SetImageSize(pData->wWidth, pData->wHeight);

	// Create image list if needed
	if(m_hImageList == 0){
		// Check if the bitmap is 32-bit (alpha channel) bitmap (valid for Windows XP only)
//		m_bAlphaImages = AtlIsAlphaBitmapResource(lpszRes);
		m_bAlphaImages = false;

		if(!CreateInternalImageList(pData->wItemCount))
			return FALSE;
	}

	// Add bitmap to our image list
	CBitmap bmp;
	if(bMapped){
		ASSERT(HIWORD(PtrToUlong(lpszRes)) == 0); // if mapped, must be a numeric ID
		int nIDImage = (int)(short)LOWORD(PtrToUlong(lpszRes));
		bmp.LoadMappedBitmap(nIDImage, (WORD)nFlags, lpColorMap, nMapSize);
	}else{
		if(m_bAlphaImages)
			bmp.m_hObject = (HBITMAP)::LoadImage(AfxGetResourceHandle(), lpszRes, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
		else
			bmp.LoadBitmap(lpszRes);
	}
	ASSERT(bmp.m_hObject != 0);
	if(bmp.m_hObject == 0)
		return FALSE;
	if(::ImageList_AddMasked(m_hImageList, bmp, m_clrMask) == -1)
		return FALSE;

	// Fill the array with command IDs
	for(int i = 0; i < nItems; i++){
		if(pItems[i] != 0)
			m_arrCommand.push_back(pItems[i]);
	}

	ASSERT(::ImageList_GetImageCount(m_hImageList) == m_arrCommand.size());
	if(::ImageList_GetImageCount(m_hImageList) != m_arrCommand.size())
		return FALSE;

	return TRUE;
}

void CCommandBarCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct){
	_MenuItemData* pmd = (_MenuItemData*)lpMeasureItemStruct->itemData;

	if(pmd->fType & MFT_SEPARATOR){
		// separator - use half system height and zero width
		lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU) / 2;
		lpMeasureItemStruct->itemWidth  = 0;
	}else{
		// compute size of text - use DrawText with DT_CALCRECT
		CWindowDC dc(0);
		CFont fontBold;
		HFONT hOldFont;
		if(pmd->fState & MFS_DEFAULT){
			// need bold version of font
			LOGFONT lf;
			m_fontMenu.GetLogFont(&lf);
			lf.lfWeight += 200;
			fontBold.CreateFontIndirect(&lf);
			ASSERT(fontBold.GetSafeHandle());
			
			hOldFont = (HFONT)dc.SelectObject(fontBold);
		}else{
			hOldFont = (HFONT)dc.SelectObject(m_fontMenu);
		}

		CRect rcText(0, 0, 0, 0);
		dc.DrawText(pmd->lpstrText, -1, rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT);
		int cx = rcText.Width();
		dc.SelectObject(hOldFont);

		LOGFONT lf;
		m_fontMenu.GetLogFont(&lf);
		int cy = lf.lfHeight;
		if(cy < 0)
			cy = -cy;
		cy += 4; // Original code is 8.

		// height of item is the bigger of these two
		lpMeasureItemStruct->itemHeight = __max(cy, (int)m_szButton.cy);

		// width is width of text plus a bunch of stuff
		cx += 2 * s_kcxTextMargin; // L/R margin for readability
		cx += s_kcxGap;            // space between button and menu text
		cx += 2 * m_szButton.cx;   // button width (L=button; R=empty margin)
		cx += m_cxExtraSpacing;    // extra between item text and accelerator keys

		// Windows adds 1 to returned value
		cx -= ::GetSystemMetrics(SM_CXMENUCHECK) - 1;
		lpMeasureItemStruct->itemWidth = cx;
		// done deal
	}
}

bool CCommandBarCtrl::SetImageSize(int cx, int cy){
	if(m_hImageList != 0){
		if(::ImageList_GetImageCount(m_hImageList) == 0){
			// empty
			::ImageList_Destroy(m_hImageList);
			m_hImageList = 0;
		}else{
			return false;// can't set, image list exists
		}
	}

	if(cx == 0 || cy == 0)
		return false;

	m_szBitmap.cx = cx;
	m_szBitmap.cy = cy;
	m_szButton.cx = m_szBitmap.cx + 2 * s_kcxButtonMargin;
	m_szButton.cy = m_szBitmap.cy + 2 * s_kcyButtonMargin;

	return true;
}

void CCommandBarCtrl::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct){
	_MenuItemData* pmd = (_MenuItemData*)lpDrawItemStruct->itemData;
	if(lpDrawItemStruct->CtlType == ODT_MENU && pmd->IsCmdBarMenuItem()){
		DrawItem(lpDrawItemStruct);
	}
}

void CCommandBarCtrl::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu){
	// Convert menu items to ownerdraw, add our data
	if(m_bImagesVisible)
	{
		CMenu& menuPopup = *pPopupMenu;
		ASSERT(menuPopup.m_hMenu != 0);

		TCHAR szString[_nMaxMenuItemTextLength];
		BOOL bRet;
		for(int i = 0; i < menuPopup.GetMenuItemCount(); i++){
			CMenuItemInfo mii;
			mii.cch = _nMaxMenuItemTextLength;
			mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID | MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE;
			mii.dwTypeData = szString; // !!!
			bRet = menuPopup.GetMenuItemInfo(i, &mii, TRUE);
			ASSERT(bRet);

			if(!(mii.fType & MFT_OWNERDRAW)
				&& (mii.wID < ID_FILE_MRU_FIRST || ID_FILE_MRU_LAST < mii.wID)
				&& !bSysMenu){ // 苦肉の策
				// Not already an ownerdraw item

				mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE;
				_MenuItemData* pMI = new _MenuItemData;
//				++newCount;
				ASSERT(pMI != 0);
				if(pMI != 0){
					pMI->fType = mii.fType;
					pMI->fState = mii.fState;
					mii.fType |= MFT_OWNERDRAW;
					pMI->iButton = -1;

					int ncmd=(int)m_arrCommand.size();
					for(int j=0; j<ncmd ; j++){
						if(m_arrCommand[j] == mii.wID){
							pMI->iButton = j;
							break;
						}
					}					
					pMI->lpstrText = new TCHAR[lstrlen(szString) + 1];
					ASSERT(pMI->lpstrText != 0);
					if(pMI->lpstrText != 0)
						_tcscpy_s(pMI->lpstrText, lstrlen(szString) + 1, szString);
					
					mii.dwItemData = (ULONG_PTR)pMI;
					bRet = SetMenuItemInfo(menuPopup.m_hMenu, i, TRUE, &mii);
					ASSERT(bRet);
				}
			}
		}

		// Add it to the stack.
		m_stackMenuHandle.push(menuPopup.m_hMenu);
	}
//	TRACE("newCount: %d OnInitMenuPopup\n", newCount);
}

void CCommandBarCtrl::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct){
	_MenuItemData* pmd = (_MenuItemData*)lpMeasureItemStruct->itemData;
	if(lpMeasureItemStruct->CtlType == ODT_MENU && pmd->IsCmdBarMenuItem()){
		MeasureItem(lpMeasureItemStruct);
	}
}

void CCommandBarCtrl::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu){
	// Check if a menu is closing, do a cleanup
	if(nFlags == 0xFFFF && hSysMenu == 0){
//		ASSERT(m_stackMenuWnd.GetSize() == 0);
		// Restore the menu items to the previous state for all menus that were converted
		if(m_bImagesVisible){
			HMENU hMenu;
			while(!m_stackMenuHandle.empty()){
				hMenu = m_stackMenuHandle.top();

				CMenu& menuPopup = *CMenu::FromHandle(hMenu);
				ASSERT(menuPopup.m_hMenu != 0);
				// Restore state and delete menu item data
				BOOL bRet;
				for(int i = 0; i < menuPopup.GetMenuItemCount(); i++){
					CMenuItemInfo mii;
					mii.fMask = MIIM_DATA | MIIM_TYPE;
					bRet = menuPopup.GetMenuItemInfo(i, &mii, TRUE);
					ASSERT(bRet);

					_MenuItemData* pMI = (_MenuItemData*)mii.dwItemData;
					if(pMI != 0 && pMI->IsCmdBarMenuItem()){
						mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE;
						mii.fType = pMI->fType;
						mii.dwTypeData = pMI->lpstrText;
						mii.cch = lstrlen(pMI->lpstrText);
						mii.dwItemData = 0;

						bRet = ::SetMenuItemInfo(menuPopup.m_hMenu, i, TRUE, &mii);
						ASSERT(bRet);

						delete [] pMI->lpstrText;
						pMI->dwMagic = 0x6666;
						delete pMI;
//						--newCount;
					}
				}
				m_stackMenuHandle.pop();
			}
		}
//		TRACE("newCount: %d OnMenuSelect\n", newCount);
	}
}
