/* Anakrino - .NET Reflection Browser
 * Copyright (C) 2001-2002  Jay Freeman (saurik)
*/

/*
 *        Redistribution and use in source and binary
 * forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation
 *    and/or other materials provided with the
 *    distribution.
 * 3. The name of the author may not be used to endorse
 *    or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "stdafx.h"
#include "MicrosoftBar.h"

/* Kids, don't try this at home.  Jay is a untrained professional :-P. */

//#32768

enum _MSBarDrawConstants
{
	s_kcxGap = 1,
	s_kcxTextMargin = 2,
	s_kcxButtonMargin = 3,
	s_kcyButtonMargin = 3
};

void CMicrosoftBarCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
	_MenuItemData* pmd = (_MenuItemData*)lpDrawItemStruct->itemData;
	CDCHandle dc = lpDrawItemStruct->hDC;
	//CWindow wnd = lpDrawItemStruct->hwndItem;
	//CDCHandle dc = wnd.GetDC();

	const RECT& rcItem = lpDrawItemStruct->rcItem;

	if(pmd->fType & MFT_SEPARATOR)
	{
		// draw separator
		RECT rc = rcItem;
		rc.left += 30;
		rc.right += 12;
		rc.top += (rc.bottom - rc.top) / 2;	// vertical center
		dc.DrawEdge(&rc, EDGE_ETCHED, BF_TOP);	// draw separator line
	}
	else		// not a separator
	{
		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;
		RECT rcButn = { rcItem.left, rcItem.top, rcItem.left + m_szButton.cx, rcItem.top + m_szButton.cy };			// button rect
		::OffsetRect(&rcButn, 0, ((rcItem.bottom - rcItem.top) - (rcButn.bottom - rcButn.top)) / 2);	// center vertically

		int iButton = pmd->iButton;
		if(iButton >= 0)
		{
			bHasImage = TRUE;

			// calc drawing point
			SIZE sz = { rcButn.right - rcButn.left - m_szBitmap.cx, rcButn.bottom - rcButn.top - 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)
				{
					dc.FillRect(&rcButn, (HBRUSH)LongToPtr((bChecked && !bSelected) ? (COLOR_3DLIGHT + 1) : (COLOR_MENU + 1)));
				}
				else
				{
					COLORREF crTxt = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE));
					COLORREF crBk = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT));
					CBrush hbr(CDCHandle::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)
				{
					RECT rc2 = rcButn;
					dc.DrawEdge(&rc2, bChecked ? BDR_SUNKENOUTER : BDR_RAISEDINNER, BF_RECT);
				}
				// draw the image
				::ImageList_Draw(m_hImageList, iButton, dc, point.x, point.y, ILD_TRANSPARENT);
			}
			else
			{
				DrawBitmapDisabled(dc, iButton, point);
			}
		}
		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 != NULL)
			{
				BOOL bRadio = ((info.fType & MFT_RADIOCHECK) != 0);
				bHasImage = Draw3DCheckmark(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)
		{
			RECT rcBG = rcItem;
			rcBG.top -= 1;
			dc.FillRect(&rcBG, (HBRUSH)LongToPtr(bSelected ? (COLOR_HIGHLIGHT + 1) : (COLOR_MENU + 1)));
			if (bSelected)
				dc.FrameRect(&rcBG, (HBRUSH)LongToPtr(COLOR_3DHILIGHT + 1));
		}

		// calc text rectangle and colors
		RECT 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)
			RECT rcDisabled = rcText;
			::OffsetRect(&rcDisabled, 1, 1);
			DrawMenuText(dc, rcDisabled, pmd->lpstrText, ::GetSysColor(COLOR_3DHILIGHT));
		}
		DrawMenuText(dc, rcText, pmd->lpstrText, colorText); // finally!
	}
}

typedef long (__stdcall *NeutralWindowProc)(struct HWND__ *, unsigned int, unsigned int, long);
NeutralWindowProc GoodWindowProc;

LRESULT CALLBACK EvilWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	//LRESULT result = GoodWindowProc(hwnd, uMsg, wParam, lParam);

	switch (uMsg) {
		case WM_NCPAINT: {
			CWindow wnd = hwnd;
			CDCHandle dc = wnd.GetDCEx((HRGN) wParam, DCX_WINDOW | DCX_INTERSECTRGN);
			if (dc == NULL)
				return 0;
			RECT rect;
			wnd.GetWindowRect(&rect);
			dc.FillRect(&rect, (HBRUSH)LongToPtr(COLOR_3DHILIGHT + 1));
			return 0;
		} break;
		default:
			return GoodWindowProc(hwnd, uMsg, wParam, lParam);
	}

	//return result;
}

BOOL CALLBACK EvilCaptureMenu(HWND hwnd, LPARAM lParam) {
	wchar_t name[8];
	::GetClassName(hwnd, name, 8);
	if (wcscmp(name, L"#32768") != 0)
		return TRUE;
	if (::GetWindowLong(hwnd, GWL_WNDPROC) == (LONG) EvilWindowProc)
		return FALSE;
	GoodWindowProc = (NeutralWindowProc) ::GetWindowLong(hwnd, GWL_WNDPROC);
	::SetWindowLong(hwnd, GWL_WNDPROC, (LONG) EvilWindowProc);
	return FALSE;
}

void CMicrosoftBarCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) {
	::EnumWindows(EvilCaptureMenu, 0);

	_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(NULL);
		HFONT hOldFont;
		if(pmd->fState & MFS_DEFAULT)
		{
			// need bold version of font
			LOGFONT lf;
			m_fontMenu.GetLogFont(lf);
			lf.lfWeight += 200;
			CFont fontBold;
			fontBold.CreateFontIndirect(&lf);
			ATLASSERT(fontBold.m_hFont != NULL);
			hOldFont = dc.SelectFont(fontBold);
		}
		else
		{
			hOldFont = dc.SelectFont(m_fontMenu);
		}

		RECT rcText = { 0, 0, 0, 0 };
		dc.DrawText(pmd->lpstrText, -1, &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT);
		int cx = rcText.right - rcText.left;
		dc.SelectFont(hOldFont);

		LOGFONT lf;
		m_fontMenu.GetLogFont(lf);
		int cy = lf.lfHeight;
		if(cy < 0)
			cy = -cy;
		cy += 8;

		// height of item is the bigger of these two
		lpMeasureItemStruct->itemHeight = std::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)

		// Windows adds 1 to returned value
		cx -= ::GetSystemMetrics(SM_CXMENUCHECK) - 1;
		lpMeasureItemStruct->itemWidth = cx;		// done deal
	}
}