Click here to Skip to main content
15,886,026 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.8K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
// ==========================================================================
//				Class Implementation : COXBitmapMenu
// ==========================================================================

// Source file : OXBitmapMenu.cpp

// Version: 9.3

// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.                      

// //////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#pragma warning(disable : 4706)
#include <multimon.h>

#include "OXBitmapMenu.h"
#include "OXBitmapMenuOrganizer.h"
#include "OXSkins.h"
#include "OXCoolToolBar.h"
#include "OXMenuBar.h"

#ifdef OX_CUSTOMIZE_COMMANDS
#include "OXDragDropCommands.h"
#endif	//	OX_CUSTOMIZE_COMMANDS


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

/////////////////////////////////////////////////////////

IMPLEMENT_DYNAMIC(COXBitmapMenuPopupWnd,CWnd)


COXBitmapMenuPopupWnd::COXBitmapMenuPopupWnd() : 
	m_pBitmapMenu(NULL),
	m_nCheckForDragDropEventTimerID(0)
{
}


COXBitmapMenuPopupWnd::~COXBitmapMenuPopupWnd() 
{ 
	ResetPopupMenu();
}


BEGIN_MESSAGE_MAP(COXBitmapMenuPopupWnd,CWnd)
	//{{AFX_MSG_MAP(COXBitmapMenuPopupWnd)
	ON_WM_RBUTTONUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_MBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_PAINT()
	ON_WM_MOUSEACTIVATE()
	ON_WM_SETTINGCHANGE()
	ON_WM_TIMER()
	ON_WM_MOUSEMOVE()
	ON_WM_NCPAINT()
	//}}AFX_MSG_MAP

	// handle drag'n'drop messages
	ON_MESSAGE(SHBDTM_DRAGENTER, OnDragEnter)
	ON_MESSAGE(SHBDTM_DRAGLEAVE, OnDragLeave)
	ON_MESSAGE(SHBDTM_DRAGOVER, OnDragOver)
	ON_MESSAGE(SHBDTM_DROP, OnDrop)

	// handle advanced customization commands
	ON_COMMAND(ID_OXCUSTBM_DELETE,OnCustBMDelete)
	ON_COMMAND(ID_OXCUSTBM_APPEARANCE,OnCustBMAppearance)
	ON_COMMAND(ID_OXCUSTBM_SEPARATOR_BEFORE,OnCustBMSeparatorBefore)
	ON_COMMAND(ID_OXCUSTBM_SEPARATOR_AFTER,OnCustBMSeparatorAfter)
	ON_COMMAND(ID_OXCUSTBM_RECENTLY_USED,OnCustBMRecentlyUsed)

END_MESSAGE_MAP()


void COXBitmapMenuPopupWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
	TRACE(_T("COXBitmapMenuPopupWnd::OnLButtonDown\n"));
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(m_nCheckForDragDropEventTimerID==0 && 
		pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		int nItemIndex=pMenu->HitTest(&point);
		if(nItemIndex>=0 && nItemIndex<(int)pMenu->GetMenuItemCount())
		{
			pMenu->SetCustomizedItem(nItemIndex);
		}
		else
		{
			pMenu->SetCustomizedItem(-1);
		}

		m_nCheckForDragDropEventTimerID=SetTimer(IDT_OXCHECKFORDRAGDROPEVENT,
			ID_OXCHECKFORDRAGDROPEVENT_DELAY,NULL);
		ASSERT(m_nCheckForDragDropEventTimerID!=0);

		return;
	}

	CWnd::OnLButtonDown(nFlags,point); 
}


void COXBitmapMenuPopupWnd::OnMButtonDown(UINT nFlags, CPoint point)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		int nItemIndex=pMenu->HitTest(&point);
		if(nItemIndex>=0 && nItemIndex<(int)pMenu->GetMenuItemCount())
		{
			pMenu->SetCustomizedItem(nItemIndex);
		}
		else
		{
			pMenu->SetCustomizedItem(-1);
		}

		return;
	}

	CWnd::OnMButtonDown(nFlags,point);
}


void COXBitmapMenuPopupWnd::OnRButtonDown(UINT nFlags, CPoint point)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		int nItemIndex=pMenu->HitTest(&point);
		if(nItemIndex>=0 && nItemIndex<(int)pMenu->GetMenuItemCount())
		{
			pMenu->SetCustomizedItem(nItemIndex);
		}
		else
		{
			pMenu->SetCustomizedItem(-1);
		}

		return;
	}

	CWnd::OnRButtonDown(nFlags,point);
}


void COXBitmapMenuPopupWnd::OnRButtonUp(UINT nFlags, CPoint point)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		int nCustomizedItemIndex=pMenu->GetCustomizedItem();
		if(nCustomizedItemIndex!=-1)
		{
			ClientToScreen(&point);
			pMenu->DisplayCustomizeItemContextMenu(nCustomizedItemIndex,point);
			return;
		}
	}

	CWnd::OnRButtonUp(nFlags,point);
}


void COXBitmapMenuPopupWnd::OnMouseMove(UINT nFlags, CPoint point)
{
	if(m_nCheckForDragDropEventTimerID!=0 && ::GetKeyState(VK_LBUTTON)>=0)
	{
		KillTimer(m_nCheckForDragDropEventTimerID);
		m_nCheckForDragDropEventTimerID=0;
	}

	CWnd::OnMouseMove(nFlags,point); 
}


LONG COXBitmapMenuPopupWnd::OnDragEnter(WPARAM wParam, LPARAM lParam)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		// lParam is the pointer to SHBDROPTARGETACTION structure
		LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
		ASSERT(pSHBDTAction!=NULL);

		ASSERT(pSHBDTAction->pWnd);
		ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());

		return pMenu->OnDragOver(wParam,lParam);
	}

	return (LONG)FALSE;
}

LONG COXBitmapMenuPopupWnd::OnDragOver(WPARAM wParam, LPARAM lParam)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		// lParam is the pointer to SHBDROPTARGETACTION structure
		LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
		ASSERT(pSHBDTAction!=NULL);

		ASSERT(pSHBDTAction->pWnd);
		ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());

		return pMenu->OnDragOver(wParam,lParam);
	}

	return (LONG)FALSE;
}

LONG COXBitmapMenuPopupWnd::OnDragLeave(WPARAM wParam, LPARAM lParam)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		// lParam is the pointer to SHBDROPTARGETACTION structure
		LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
		ASSERT(pSHBDTAction!=NULL);

		ASSERT(pSHBDTAction->pWnd);
		ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());

		return pMenu->OnDragLeave(wParam,lParam);
	}

	return (LONG)FALSE;
}

LONG COXBitmapMenuPopupWnd::OnDrop(WPARAM wParam, LPARAM lParam)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		// lParam is the pointer to SHBDROPTARGETACTION structure
		LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
		ASSERT(pSHBDTAction!=NULL);

		ASSERT(pSHBDTAction->pWnd);
		ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());

		return pMenu->OnDrop(wParam,lParam);
	}

	return (LONG)FALSE;
}


void COXBitmapMenuPopupWnd::OnCustBMDelete()
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		pMenu->OnCustBMDelete();
	}
}

void COXBitmapMenuPopupWnd::OnCustBMAppearance()
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		pMenu->OnCustBMAppearance();
	}
}

void COXBitmapMenuPopupWnd::OnCustBMSeparatorBefore()
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		pMenu->OnCustBMSeparatorBefore();
	}
}

void COXBitmapMenuPopupWnd::OnCustBMSeparatorAfter()
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		pMenu->OnCustBMSeparatorAfter();
	}
}

void COXBitmapMenuPopupWnd::OnCustBMRecentlyUsed()
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL && pMenu->IsInCustomizationMode())
	{
		pMenu->OnCustBMRecentlyUsed();
	}
}


void COXBitmapMenuPopupWnd::OnPaint()
{
	CPaintDC dc(this);

	int nSavedDC=dc.SaveDC();
	ASSERT(nSavedDC!=0);

	dc.SetTextColor(::GetSysColor(COLOR_MENUTEXT));
	dc.SelectObject(&m_fontMenu);

	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL)
	{
		ASSERT((int)pMenu->GetMenuItemCount()==m_arrItemRects.GetSize());

		MENUITEMINFO mii={ sizeof(mii) };
		mii.fMask=MIIM_DATA;

		DRAWITEMSTRUCT dis;
		dis.CtlType=ODT_MENU;
		dis.hDC=dc.GetSafeHdc();
		dis.itemAction=ODA_DRAWENTIRE;
		dis.CtlID=0;
		dis.itemState=0;
		dis.hwndItem=(HWND)pMenu->GetSafeHmenu();
		for(int nIndex=0; nIndex<m_arrItemRects.GetSize(); nIndex++)
		{
			dis.itemID=pMenu->GetMenuItemID(nIndex);
			dis.rcItem=m_arrItemRects[nIndex];
			VERIFY(::GetMenuItemInfo(pMenu->GetSafeHmenu(),nIndex,TRUE,&mii));
			dis.itemData=mii.dwItemData;
			pMenu->DrawItem(&dis);
		}
	}

	VERIFY(dc.RestoreDC(nSavedDC));
}


int COXBitmapMenuPopupWnd::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest,
										   UINT message)
{
	UNREFERENCED_PARAMETER(pDesktopWnd);
	UNREFERENCED_PARAMETER(nHitTest);
	UNREFERENCED_PARAMETER(message);

	return MA_NOACTIVATE;
}


void COXBitmapMenuPopupWnd::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
	UpdateMenuMetrics();
	OnMenuChanged();

	CWnd::OnSettingChange(uFlags, lpszSection);
}


void COXBitmapMenuPopupWnd::OnTimer(UINT nIDEvent)
{
	if((int)nIDEvent==m_nCheckForDragDropEventTimerID)
	{
		KillTimer(m_nCheckForDragDropEventTimerID);
		m_nCheckForDragDropEventTimerID=0;

		if(::IsWindow(GetSafeHwnd()))		
		{
			// do drag and drop
			//
			COXBitmapMenu* pMenu=GetBitmapMenu();
			if(pMenu!=NULL && pMenu->IsInCustomizationMode())
			{
				if(::GetKeyState(VK_LBUTTON)<0)
				{
					pMenu->OnBeginDragDrop(pMenu->GetCustomizedItem());
				}
			}
			//
			////////////////////////////////////
		}

		return;
	}

	CWnd::OnTimer(nIDEvent);
}


BOOL COXBitmapMenuPopupWnd::TrackPopupMenu(COXBitmapMenu* pMenu, UINT nFlags, 
										   int x, int y, CWnd* pWnd)
{
	ASSERT(pMenu!=NULL);
	m_pBitmapMenu=pMenu;

	COXBitmapMenu* pBitmapMenu=GetBitmapMenu();
	pBitmapMenu->SetPopupWnd(this);

	if(!::IsWindow(GetSafeHwnd()))
	{
		if(!CreateEx(WS_EX_DLGMODALFRAME,AfxRegisterWndClass(
			CS_SAVEBITS|CS_DBLCLKS,0,(HBRUSH)(COLOR_BTNFACE+1),0),_T(""),
			WS_POPUP,m_rectWindow,pWnd,0,NULL))
		{
			return FALSE;
		}
	}

	// register OLE Drag'n'Drop
	COleDropTarget* pOleDropTarget=pBitmapMenu->GetDropTarget();
	ASSERT(pOleDropTarget!=NULL);
	pOleDropTarget->Revoke();
	if(!pOleDropTarget->Register(this))
	{
		TRACE(_T("COXBitmapMenuPopupWnd::Create: failed to register the control with COleDropTarget. You've probably forgotten to initialize OLE libraries using AfxOleInit function\n"));
	}

	pBitmapMenu->SetCutomizationMode(TRUE,pWnd->GetSafeHwnd());
	
	// update info about the font used to display the menu
	UpdateMenuMetrics();
	// calculate the rectangles for the menu items and for the popup menu itself
	VERIFY(CalcLayout(nFlags,x,y));

	SetWindowPos(&wndTop,m_rectWindow.left,m_rectWindow.top,m_rectWindow.Width(),
		m_rectWindow.Height(),SWP_NOACTIVATE|SWP_SHOWWINDOW);
	ShowWindow(SW_SHOWNA);

	return TRUE;
}


void COXBitmapMenuPopupWnd::ResetPopupMenu()
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu!=NULL)
	{
		if(!pMenu->IsDragDropOwner())
		{
			pMenu->SetCustomizedItem(-1);
			pMenu->SetPopupWnd(NULL);
			m_pBitmapMenu=NULL;
		}
	}

	if(::IsWindow(GetSafeHwnd()))
	{
		ShowWindow(SW_HIDE);
	}
}


BOOL COXBitmapMenuPopupWnd::CalcLayout(UINT nFlags, int x, int y)
{
	COXBitmapMenu* pMenu=GetBitmapMenu();
	if(pMenu==NULL)
	{
		return FALSE;
	}

	m_arrItemRects.RemoveAll();
	m_rectWindow.SetRectEmpty();

	int nWidth=0;
	int nHeight=0;

	int nIndex=0;
	for(nIndex=0; nIndex<(int)pMenu->GetMenuItemCount(); nIndex++)
	{
		MENUITEMINFO mii={ sizeof(mii) };
		mii.fMask=MIIM_DATA;
		VERIFY(::GetMenuItemInfo(pMenu->GetSafeHmenu(),nIndex,TRUE,&mii));

		MEASUREITEMSTRUCT mis;
		mis.CtlType=ODT_MENU;
		mis.itemData=mii.dwItemData;
		mis.itemID=pMenu->GetMenuItemID(nIndex);
		mis.itemHeight=0;
		mis.itemWidth=0;
		pMenu->MeasureItem(&mis);

		CRect rect(0,nHeight,mis.itemWidth,nHeight+mis.itemHeight);
		m_arrItemRects.Add(rect);

		nHeight+=mis.itemHeight;
		if(nWidth<(int)mis.itemWidth)
			nWidth=mis.itemWidth;
	}

	if(nWidth==0 && nHeight==0)
	{
		nWidth=ID_OXBITMAPMENUPOPUPWND_DEFAULT_WIDTH;
		nHeight=ID_OXBITMAPMENUPOPUPWND_DEFAULT_HEIGHT;
	}

	m_rectWindow.top=y;
	switch(nFlags)
	{
	case TPM_CENTERALIGN:
		m_rectWindow.left=x-nWidth/2;
		break;
	case TPM_LEFTALIGN:
		m_rectWindow.left=x;
		break;
	case TPM_RIGHTALIGN:
		m_rectWindow.left=x-nWidth;
		break;
	}
	m_rectWindow.right=m_rectWindow.left+nWidth;
	m_rectWindow.bottom=m_rectWindow.top+nHeight;

	CRect rectCopy=m_rectWindow;
	CalcWindowRect(m_rectWindow);
	CSize szOffset(rectCopy.left-m_rectWindow.left,rectCopy.top-m_rectWindow.top);
	m_rectWindow.OffsetRect(szOffset.cx,szOffset.cy);
	m_rectWindow.InflateRect(0,0,2*szOffset.cx,0);

	nWidth+=2*szOffset.cx;
	// adjust rectangles for items
	for(nIndex=0; nIndex<m_arrItemRects.GetSize(); nIndex++)
	{
		CRect& rect=m_arrItemRects[nIndex];
		if(rect.Width()<nWidth)
			rect.right=rect.left+nWidth;
	}

	return TRUE;
}


void COXBitmapMenuPopupWnd::UpdateMenuMetrics()
{
	if((HFONT)m_fontMenu!=NULL)
		m_fontMenu.DeleteObject();

	// Menu font, height and color
	NONCLIENTMETRICS ncm={ sizeof(ncm) };
	SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(ncm),&ncm,0);
	VERIFY(m_fontMenu.CreateFontIndirect(&ncm.lfMenuFont));
}


void COXBitmapMenuPopupWnd::OnMenuChanged()
{
	ASSERT(GetBitmapMenu()!=NULL);
	ASSERT(::IsWindow(GetSafeHwnd()));

	CRect rect;
	GetWindowRect(rect);
	CPoint ptStart=rect.TopLeft();
	GetBitmapMenu()->CalcExtents();
	VERIFY(CalcLayout(TPM_LEFTALIGN,ptStart.x,ptStart.y));

	if(IsWindowVisible())
	{
		SetWindowPos(&wndTop,m_rectWindow.left,m_rectWindow.top,m_rectWindow.Width(),
			m_rectWindow.Height(),SWP_NOACTIVATE|SWP_SHOWWINDOW);
		RedrawWindow();
	}
	else
	{
		ResetPopupMenu();
	}
}


void COXBitmapMenuPopupWnd::RedrawItem(int nIndex)
{
	ASSERT(GetBitmapMenu()!=NULL);
	ASSERT(::IsWindow(GetSafeHwnd()));

	ASSERT(nIndex>=0 && nIndex<m_arrItemRects.GetSize());
	CRect rect;
	rect=m_arrItemRects[nIndex];
	RedrawWindow(rect);
}


///////////////////////////////////////////////////////
// COXShadowedItemWnd
COXShadowedItemWnd::COXShadowedItemWnd(COXCoolToolBar* pCoolToolbar, int iMenuItem, UINT nPosFlags)
{
	m_pCoolToolbar = pCoolToolbar;
	m_iMenuItem = iMenuItem;
	m_nPosFlags = nPosFlags;
}

COXShadowedItemWnd::~COXShadowedItemWnd()
{
}


BEGIN_MESSAGE_MAP(COXShadowedItemWnd, CWnd)
	//{{AFX_MSG_MAP(COXShadowedItemWnd)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////

WNDPROC COXBitmapMenu::m_origWndProc = NULL;

IMPLEMENT_DYNAMIC(COXBitmapMenu, CMenu)

COXMenuSkin* COXBitmapMenu::m_pMenuSkin = NULL;

COXBitmapMenu::COXBitmapMenu()
	:
	m_nBitmapExtent(0),
	m_nTextHeight(0),
	m_nAcceleratorExtent(0),
	m_nStringExtent(0),
	m_nCustomizedItemIndex(-1),
	m_bCutomizable(FALSE),
	m_bInCutomizationMode(FALSE),
	m_bDragDropOwner(FALSE),
	m_bDragDropOperation(FALSE),
	m_hWndCustomizeOrganizer(NULL),
	m_nInsertMarkIndex(-1),
	m_pPopupWnd(NULL),
	m_rectDropDownItem(0, 0, 0, 0)
{
	// Sublass the menus
	RegisterWindowClass(AfxGetInstanceHandle());
}


COXBitmapMenu::~COXBitmapMenu()
{
	m_KeyAccessMap.RemoveAll();
	COXItemInfo* pItemInfo;
	while(!m_ItemInfoList.IsEmpty())
	{
		pItemInfo=m_ItemInfoList.RemoveHead();
		delete pItemInfo;
		pItemInfo = NULL;
	}
	TRACE(_T("\n"));
	// delete the classic skin
	if (m_pMenuSkin != NULL)
	{
		delete m_pMenuSkin;
		m_pMenuSkin = NULL;
	}

	COleDropTarget* pOleDropTarget=GetDropTarget();
	ASSERT(pOleDropTarget!=NULL);
	pOleDropTarget->Revoke();
}


void COXBitmapMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	ASSERT(m_hMenu!=NULL);
	ASSERT(lpDrawItemStruct != NULL);
	ASSERT(lpDrawItemStruct->CtlType == ODT_MENU);

	UINT nState=lpDrawItemStruct->itemState;

	CRect itemRect(lpDrawItemStruct->rcItem);
	CRect buttonRect(0,0,0,0);
	CRect imageRect(0,0,0,0);
	CRect text1Rect(0,0,0,0);
	CRect text2Rect(0,0,0,0);

	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);

	COXItemInfo* pItemInfo=(COXItemInfo*)lpDrawItemStruct->itemData;
	ASSERT(AfxIsValidAddress(pItemInfo, sizeof(COXItemInfo)));
	COXImageInfo* pImageInfo=pItemInfo->GetImageInfo();
	CString sText=pItemInfo->GetText();

	if(lpDrawItemStruct->itemID!=0 && lpDrawItemStruct->itemID!=0xffff)
	{
		// Compute the space for each menu item part
		DistributeSpace(
			nState,pImageInfo,itemRect,buttonRect,imageRect,text1Rect,text2Rect);
	}

	COXBitmapMenuOrganizer* pOrganizer=
		COXBitmapMenuOrganizer::FindOrganizer(AfxGetMainWnd()->GetSafeHwnd());
	ASSERT(pOrganizer!=NULL);
	BOOL bBefore=TRUE;
	CPoint ptTest=itemRect.CenterPoint();
	int nItemIndex=HitTest(&ptTest,&bBefore);
	if(pOrganizer->IsShowOnlyRecentlyUsedItems() && 
		!pOrganizer->IsRecentlyUsed(this,nItemIndex))
	{
		nState|=OXODS_HIDDEN;
		if(nItemIndex>0 && pOrganizer->IsRecentlyUsed(this,nItemIndex-1))
		{
			nState|=OXODS_HIDDENFIRST;
		}
		if(nItemIndex<(int)GetMenuItemCount()-1 && 
			pOrganizer->IsRecentlyUsed(this,nItemIndex+1))
		{
			nState|=OXODS_HIDDENLAST;
		}
	}

	// Draw every part of the menu item
	if(lpDrawItemStruct->itemID==0 || lpDrawItemStruct->itemID==0xffff)
	{
		DrawSeparator(&dc,itemRect);
	}
	else if(IsPopupItem(lpDrawItemStruct->itemID))
	{
		DrawSubmenuItem(&dc,nState,sText,pImageInfo,
			itemRect,buttonRect,text1Rect,text2Rect);
	}
	else
	{
		DrawBackground(&dc,nState,pImageInfo,itemRect,buttonRect);
		DrawButton(&dc,nState,pImageInfo,buttonRect);
		DrawImage(&dc,nState,pImageInfo,imageRect);
		if((((int)lpDrawItemStruct->itemID)&0x0000ffff)==ID_OX_SHOWALLITEMS)
		{
			ASSERT(!IsInCustomizationMode());
			DrawExpansionItem(&dc,itemRect,nState);
		}
		else
		{
			DrawText(&dc,nState,sText,text1Rect,text2Rect);
		}
	}


	// draw customized item
	int nCustomizedItem=GetCustomizedItem();
	if(nCustomizedItem!=-1 && 
		GetMenuItemID(nCustomizedItem)==lpDrawItemStruct->itemID)
	{
		CPoint pt=itemRect.CenterPoint();
		if(HitTest(&pt)==nCustomizedItem)
		{
			DrawCustomized(&dc,itemRect);
		}
	}


	// draw insert mark
	int nInsertMark=GetInsertMark();
	if(nInsertMark!=-1)
	{
		ASSERT(nInsertMark>=0 && nInsertMark<=(int)GetMenuItemCount());
		BOOL bBefore=(nInsertMark<(int)GetMenuItemCount());
		if(!bBefore)
			nInsertMark--;
		CPoint pt=itemRect.CenterPoint();
		if(HitTest(&pt)==nInsertMark)
		{
			DrawInsertMark(&dc,itemRect,bBefore);
		}
	}

	dc.Detach(); 
}


void COXBitmapMenu::DistributeSpace(UINT nState, 
									COXImageInfo* pImageInfo, 
									CRect itemRect, 
									CRect& buttonRect, 
									CRect& imageRect, 
									CRect& text1Rect, 
									CRect& text2Rect)
{
	GetMenuSkin()->DistributeSpace(nState, pImageInfo, itemRect, buttonRect, imageRect,
		text1Rect, text2Rect, this);
}


HBRUSH COXBitmapMenu::HBrushDitherCreate(COLORREF rgbFace, COLORREF rgbHilight)
{     
	struct  //BITMAPINFO with 16 colors         
	{         
		BITMAPINFOHEADER    bmiHeader; 
        RGBQUAD             bmiColors[16];         
	} bmi;      
	HBRUSH hBrush=NULL;     
	DWORD patGray[8];     
	HDC hDC;     
	HBITMAP hBmp;     
	static COLORREF rgbFaceOld=0xFFFFFFFF;  //Initially impossible     
	static COLORREF rgbHilightOld=0xFFFFFFFF;  //Initially impossible      

   /*
	* We're going to create an 8*8 brush for PatBlt using the      
	* face color and highlight color.
	*/     
	bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);     
	bmi.bmiHeader.biWidth=8;     
	bmi.bmiHeader.biHeight=8;     
	bmi.bmiHeader.biPlanes=1;     
	bmi.bmiHeader.biBitCount=1;     
	bmi.bmiHeader.biCompression=BI_RGB;     
	bmi.bmiHeader.biSizeImage=0;     
	bmi.bmiHeader.biXPelsPerMeter=0;     
	bmi.bmiHeader.biYPelsPerMeter=0;     
	bmi.bmiHeader.biClrUsed=0;     
	bmi.bmiHeader.biClrImportant=0;

	bmi.bmiColors[0].rgbBlue=GetBValue(rgbFace);     
	bmi.bmiColors[0].rgbGreen=GetGValue(rgbFace);     
	bmi.bmiColors[0].rgbRed=GetRValue(rgbFace);     
	bmi.bmiColors[0].rgbReserved=0;      
	bmi.bmiColors[1].rgbBlue=GetBValue(rgbHilight);     
	bmi.bmiColors[1].rgbGreen=GetGValue(rgbHilight);     
	bmi.bmiColors[1].rgbRed=GetRValue(rgbHilight);     
	bmi.bmiColors[1].rgbReserved=0;     
	
	//Create the byte array for CreateDIBitmap.     
	patGray[6]=patGray[4]=patGray[2]=patGray[0]=0x5555AAAAL;     
	patGray[7]=patGray[5]=patGray[3]=patGray[1]=0xAAAA5555L;      

	//Create the bitmap     
	hDC=::GetDC(NULL);
	hBmp=::CreateDIBitmap(hDC,&bmi.bmiHeader,CBM_INIT,patGray,
		(LPBITMAPINFO)&bmi,DIB_RGB_COLORS);
    ::ReleaseDC(NULL,hDC);      
	//Create the brush from the bitmap     
	if(NULL!=hBmp)         
	{ 
        hBrush=CreatePatternBrush(hBmp);
		DeleteObject(hBmp);
    } 
    return hBrush;     
}      


void COXBitmapMenu::DrawBackground(CDC* pDC, UINT nState, COXImageInfo* pImageInfo,
								   CRect itemRect, CRect buttonRect)
{
	GetMenuSkin()->DrawBackground(pDC, nState, pImageInfo, itemRect, buttonRect, this);
}


void COXBitmapMenu::DrawButton(CDC* pDC, UINT nState,  COXImageInfo* pImageInfo, 
							   CRect buttonRect)
{
	GetMenuSkin()->DrawButton(pDC, nState, pImageInfo, buttonRect, this);
}

void COXBitmapMenu::DrawImage(CDC* pDC, UINT nState, COXImageInfo* pImageInfo, 
							  CRect imageRect)
{
	GetMenuSkin()->DrawImage(pDC, nState, pImageInfo, imageRect, this);
}

void COXBitmapMenu::DrawText(CDC* pDC, UINT nState, 
							 CString sText, CRect text1Rect, CRect text2Rect)
{
	GetMenuSkin()->DrawText(pDC, nState, sText, text1Rect, text2Rect, this);
}

void COXBitmapMenu::DrawSeparator(CDC* pDC, CRect itemRect)
{
	GetMenuSkin()->DrawSeparator(pDC, itemRect, this);
}

void COXBitmapMenu::DrawCustomized(CDC* pDC, CRect itemRect)
{
	GetMenuSkin()->DrawCustomized(pDC, itemRect, this);
}

void COXBitmapMenu::DrawInsertMark(CDC* pDC, CRect itemRect, BOOL bBefore)
{
	GetMenuSkin()->DrawInsertMark(pDC, itemRect, bBefore, this);
}

void COXBitmapMenu::DrawSubmenuItem(CDC* pDC, UINT nState, CString sText, 
									COXImageInfo* pImageInfo, 
									CRect itemRect, CRect buttonRect, 
									CRect text1Rect, CRect text2Rect)
{
	GetMenuSkin()->DrawSubmenuItem(pDC, nState, sText, pImageInfo, itemRect, buttonRect,
		text1Rect, text2Rect, this);
}

void COXBitmapMenu::DrawExpansionItem(CDC* pDC, CRect itemRect, UINT nState)
{
	GetMenuSkin()->DrawExpansionItem(pDC, itemRect, nState, this);
}


void COXBitmapMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	OXDIMENSIONCONSTANTS& oxdc = GetMenuSkin()->GetDimentionConstants();

	ASSERT(lpMeasureItemStruct != NULL);
	COXItemInfo* pItemInfo=(COXItemInfo*)lpMeasureItemStruct->itemData;
	if(pItemInfo)
	{
		ASSERT(AfxIsValidAddress(pItemInfo, sizeof(COXItemInfo)));
		COXImageInfo* pImageInfo=pItemInfo->GetImageInfo();

		int nImageWidth=0;
		int nImageHeight=0;
		if(pImageInfo != NULL)
		{
			IMAGEINFO ii;
			::ZeroMemory(&ii, sizeof(ii));

			pImageInfo->GetImageList()->GetImageInfo(pImageInfo->GetIndex(),&ii);
			nImageWidth=ii.rcImage.right - ii.rcImage.left + 2;
			nImageHeight=ii.rcImage.bottom - ii.rcImage.top + 2;
		}
		
		lpMeasureItemStruct->itemWidth=
			oxdc.m_nGapLeftBitmap+m_nBitmapExtent+oxdc.m_nGapBitmapText+
			m_nStringExtent+(m_nAcceleratorExtent ? oxdc.m_nGapTextAcclrtr : 0)+
			m_nAcceleratorExtent+oxdc.m_nGapAcclrtrRight;
		if(lpMeasureItemStruct->itemID==0 || lpMeasureItemStruct->itemID==0xffff)
		{
			// separator
			lpMeasureItemStruct->itemHeight=oxdc.m_nSeparatorHeight;
		}
		else
		{
			// item with text
			lpMeasureItemStruct->itemHeight=__max(nImageHeight+oxdc.m_nGapVertBitmap, 
				m_nTextHeight+oxdc.m_nGapVertText);

			if ((((int)lpMeasureItemStruct->itemID)&0x0000ffff)==ID_OX_SHOWALLITEMS)
				GetMenuSkin()->AdjustExpansionItemHeight(lpMeasureItemStruct->itemHeight, this);
		}
	}
	else if (IsPopupItem(lpMeasureItemStruct->itemID))
		lpMeasureItemStruct->itemHeight = m_nTextHeight + oxdc.m_nGapVertText; 
}

void COXBitmapMenu::CalcExtents()
{
	UINT nItemCount=GetMenuItemCount();

	CString sText;
	CString sBeforeTab;
	CString sAfterTab;
	int nTabCharIndex=0;

	m_nStringExtent=0;
	m_nAcceleratorExtent=0;
	m_nBitmapExtent=0;

	MENUITEMINFO mii={ sizeof(MENUITEMINFO) };

	CFont Font, boldFont;
	NONCLIENTMETRICS ncm={ sizeof(NONCLIENTMETRICS) };

	COXItemInfo* pItemInfo=NULL;
	COXImageInfo* pImageInfo=NULL;
	IMAGEINFO ii={ 0 };

	VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0));
	VERIFY(Font.CreateFontIndirect(&ncm.lfMenuFont));
	ncm.lfMenuFont.lfWeight = FW_BOLD; // make the font bold
	VERIFY(boldFont.CreateFontIndirect(&ncm.lfMenuFont));

	CWnd* pMainWnd=AfxGetThread()->GetMainWnd();
	CDC* pDC=pMainWnd->GetDC();

	CSize TextExt;
	for(UINT nItemIndex=0; nItemIndex < nItemCount; nItemIndex++)
	{
		CFont* pOldFont;
		if (GetDefaultItem(GMDI_USEDISABLED, MF_BYPOSITION) == nItemIndex)
			pOldFont = pDC->SelectObject(&boldFont);
		else	
			pOldFont = pDC->SelectObject(&Font);
		
		mii.fMask=MIIM_TYPE|MIIM_DATA|MIIM_SUBMENU;
		mii.cch=300;
		mii.dwTypeData=sText.GetBuffer(mii.cch);
		// ... zero-terminate string
		mii.dwTypeData[0]=_T('\0');
		::GetMenuItemInfo(GetSafeHmenu(), nItemIndex, TRUE, &mii);
		sText.ReleaseBuffer();

		if(mii.fType & MFT_SEPARATOR)
			continue;

		pItemInfo=(COXItemInfo*)(mii.dwItemData);
		if(pItemInfo)
		{
			pImageInfo=pItemInfo->GetImageInfo();
			if(pImageInfo)
			{
				pImageInfo->GetImageList()->GetImageInfo(pImageInfo->GetIndex(),&ii);
				m_nBitmapExtent=__max(m_nBitmapExtent, __max(GetMenuSkin()->GetDimentionConstants().m_nMinBitmapWidth,ii.rcImage.right-ii.rcImage.left));
			}
			sText=pItemInfo->GetText();
		}

		nTabCharIndex=sText.Find(_T('\t'));
		if(nTabCharIndex != -1)
		{
			sBeforeTab=sText.Left(nTabCharIndex);
			sAfterTab=sText.Mid(nTabCharIndex + 1);
		}
		else
		{
			sBeforeTab=sText;
			if(mii.hSubMenu!=NULL)
				sAfterTab=_T("W");
			else	
				sAfterTab.Empty();
		}

		CRect text1Rect(0,0,0,0);
		CRect text2Rect(0,0,0,0);
		pDC->DrawText(sBeforeTab, text1Rect, DT_CALCRECT | DT_SINGLELINE);
		pDC->DrawText(sAfterTab, text2Rect, DT_CALCRECT | DT_SINGLELINE);

		m_nStringExtent=__max(m_nStringExtent, text1Rect.Width());
		m_nAcceleratorExtent=__max(m_nAcceleratorExtent, text2Rect.Width());
	
		pDC->SelectObject(pOldFont);
	}

	TextExt=pDC->GetTextExtent(_T("A"));
	m_nTextHeight=TextExt.cy;

	pMainWnd->ReleaseDC(pDC);
}

void COXBitmapMenu::AddItemInfo(COXItemInfo* pItemInfo)
{
	m_ItemInfoList.AddTail(pItemInfo);
}

/////////////////////////////////////////////////////////////////////////////
// COXBitmapMenu idle update through CBitmapMenuCmdUI class

class OX_CLASS_DECL CBitmapMenuCmdUI : public CCmdUI        // class private to this file !
{
public: // re-implementations only
	virtual void SetText(LPCTSTR lpszText);
};

void CBitmapMenuCmdUI::SetText(LPCTSTR lpszText)
{
	ASSERT(lpszText != NULL);
	ASSERT(AfxIsValidString(lpszText));
	
	ASSERT(m_pMenu->IsKindOf(RUNTIME_CLASS(COXBitmapMenu)));
	if(m_pMenu != NULL)
	{
		if(m_pSubMenu != NULL)
			return; // don't change popup menus indirectly

		MENUITEMINFO mii={ sizeof(mii) };
		mii.fMask=MIIM_TYPE|MIIM_DATA;
		::GetMenuItemInfo(m_pMenu->GetSafeHmenu(),m_nIndex,TRUE,&mii);

		if(mii.fType == MFT_OWNERDRAW)
		{
			COXItemInfo* pItemInfo=(COXItemInfo*)(mii.dwItemData);
			ASSERT(pItemInfo != NULL);
			pItemInfo->SetText(lpszText);
		}
		else
		{
			CCmdUI::SetText(lpszText);
		}
	}
}

void COXBitmapMenu::OnUpdateCmdUI(CWnd* pWnd, UINT /*nIndex*/, BOOL bSysMenu)
{
	// Code almost entirely copied from CFrameWnd::OnInitPopup
	if(bSysMenu)
	{
		return;     // don't support system menu
	}

	// check the enabled state of various menu items

	CBitmapMenuCmdUI state;
	state.m_pMenu=this;
	ASSERT(state.m_pOther == NULL);
	ASSERT(state.m_pParentMenu == NULL);

	// determine if menu is popup in top-level menu and set m_pOther to
	//  it if so (m_pParentMenu == NULL indicates that it is secondary popup)
	HMENU hParentMenu;
	if(AfxGetThreadState()->m_hTrackingMenu == m_hMenu)
	{
		state.m_pParentMenu=this;    // parent == child for tracking popup
	}
	else if((hParentMenu=::GetMenu(pWnd->m_hWnd)) != NULL)
	{
		CWnd* pParent=pWnd->GetTopLevelParent();
			// child windows don't have menus -- need to go to the top!
		if(pParent != NULL &&
			(hParentMenu=::GetMenu(pParent->m_hWnd)) != NULL)
		{
			int nIndexMax=::GetMenuItemCount(hParentMenu);
			for (int nIndex=0; nIndex < nIndexMax; nIndex++)
			{
				if(::GetSubMenu(hParentMenu, nIndex) == m_hMenu)
				{
					// when popup is found, m_pParentMenu is containing menu
					state.m_pParentMenu=CMenu::FromHandle(hParentMenu);
					break;
				}
			}
		}
	}

	state.m_nIndexMax=GetMenuItemCount();
	for (state.m_nIndex=0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
	{
		state.m_nID=GetMenuItemID(state.m_nIndex);
		if(state.m_nID == 0)
			continue; // menu separator or invalid cmd - ignore it

		ASSERT(state.m_pOther == NULL);
		ASSERT(state.m_pMenu != NULL);
		if(state.m_nID == (UINT)-1)
		{
			// possibly a popup menu, route to first item of that popup
			state.m_pSubMenu=GetSubMenu(state.m_nIndex);
			if(state.m_pSubMenu == NULL ||
				(state.m_nID=state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
				state.m_nID == (UINT)-1)
			{
				continue;       // first item of popup can't be routed to
			}
			state.DoUpdate(pWnd, FALSE);    // popups are never auto disabled
		}
		else
		{
			// normal menu item
			// Auto enable/disable if frame window has 'm_bAutoMenuEnable'
			//    set and command is _not_ a system command.
			state.m_pSubMenu=NULL;
			state.DoUpdate(pWnd,
				((CFrameWnd*)pWnd)->m_bAutoMenuEnable && state.m_nID<0xF000);
		}

		// adjust for menu deletions and additions
		UINT nCount=GetMenuItemCount();
		if(nCount < state.m_nIndexMax)
		{
			state.m_nIndex -= (state.m_nIndexMax - nCount);
			while (state.m_nIndex < nCount &&
				GetMenuItemID(state.m_nIndex) == state.m_nID)
			{
				state.m_nIndex++;
			}
		}
		state.m_nIndexMax=nCount;
	}
}


int COXBitmapMenu::SetCustomizedItem(int nIndex) 
{ 
	int nOldCustomizedItemIndex=m_nCustomizedItemIndex;
	m_nCustomizedItemIndex=nIndex;
	if(m_pPopupWnd!=NULL && ::IsWindow(m_pPopupWnd->GetSafeHwnd()))
	{
		if(nOldCustomizedItemIndex!=m_nCustomizedItemIndex)
		{
			if(nOldCustomizedItemIndex!=-1)
				m_pPopupWnd->RedrawItem(nOldCustomizedItemIndex);
			if(m_nCustomizedItemIndex!=-1)
				m_pPopupWnd->RedrawItem(m_nCustomizedItemIndex);
		}
		SendCustomizeNotification(ID_OXCUSTBM_SET_CUSTOMIZE_ITEM);
	}
	return nOldCustomizedItemIndex;
}


BOOL COXBitmapMenu::DisplayCustomizeItemContextMenu(int nItemIndex, CPoint point)
{
	ASSERT(nItemIndex>=0 && nItemIndex<(int)GetMenuItemCount());

	COXBitmapMenuOrganizer* pOrganizer=
		COXBitmapMenuOrganizer::FindOrganizer(AfxGetMainWnd()->GetSafeHwnd());
	ASSERT(pOrganizer!=NULL);

	CString sText;
	MENUITEMINFO mii={ sizeof(MENUITEMINFO) };
	mii.fMask=MIIM_TYPE|MIIM_DATA|MIIM_ID|MIIM_SUBMENU;
	mii.cch=300;
	mii.dwTypeData=sText.GetBuffer(mii.cch);
	// ... zero-terminate string
	mii.dwTypeData[0]=_T('\0');
	VERIFY(::GetMenuItemInfo(GetSafeHmenu(),nItemIndex,TRUE,&mii));
	sText.ReleaseBuffer();
	COXItemInfo* pItemInfo=(COXItemInfo*)(mii.dwItemData);
	ASSERT(AfxIsValidAddress(pItemInfo,sizeof(COXItemInfo)));
	sText=pItemInfo->GetText();

	// don't add most recent files used 
	if((int)mii.wID>=ID_FILE_MRU_FILE1 && (int)mii.wID<=ID_FILE_MRU_FILE16)
	{
		return FALSE;
	}

	// don't add MDIChild windows menu items 
	if((int)mii.wID>=AFX_IDM_FIRST_MDICHILD && mii.hSubMenu==NULL)
	{
		return FALSE;
	}


	CMenu menu;
	if(!menu.CreatePopupMenu())
		return FALSE;

	// populate menu
	CString sItem;
	VERIFY(sItem.LoadString(IDS_OX_CUSTBM_DELETE));
	VERIFY(menu.AppendMenu(MF_STRING,ID_OXCUSTBM_DELETE,sItem));
	if((mii.fType&MFT_SEPARATOR)==0)
	{
		VERIFY(menu.AppendMenu(MF_SEPARATOR));
		VERIFY(sItem.LoadString(IDS_OX_CUSTBM_APPEARANCE));
		VERIFY(menu.AppendMenu(MF_STRING,ID_OXCUSTBM_APPEARANCE,
			sItem));
		VERIFY(menu.AppendMenu(MF_SEPARATOR));
		if(pOrganizer->IsShowOnlyRecentlyUsedItems()) 
		{
			VERIFY(sItem.LoadString(IDS_OX_CUSTBM_RECENTLY_USED));
			VERIFY(menu.AppendMenu(MF_STRING|
				(pOrganizer->IsRecentlyUsed(this,nItemIndex) ? MF_CHECKED : 
				MF_UNCHECKED),ID_OXCUSTBM_RECENTLY_USED,sItem));
			VERIFY(menu.AppendMenu(MF_SEPARATOR));
		}

		BOOL bSeparatorBefore=FALSE;
		if(nItemIndex>0)
		{
			MENUITEMINFO mii={ sizeof(MENUITEMINFO) };
			mii.fMask=MIIM_TYPE;
			mii.cch=300;
			mii.dwTypeData=sText.GetBuffer(mii.cch);
			// ... zero-terminate string
			mii.dwTypeData[0]=_T('\0');
			VERIFY(::GetMenuItemInfo(GetSafeHmenu(),nItemIndex-1,TRUE,&mii));
			sText.ReleaseBuffer();

			bSeparatorBefore=((mii.fType&MFT_SEPARATOR)==0);
		}
		VERIFY(sItem.LoadString(IDS_OX_CUSTBM_SEPARATOR_BEFORE));
		VERIFY(menu.AppendMenu(MF_STRING|(bSeparatorBefore ? 0 : MF_GRAYED),
			ID_OXCUSTBM_SEPARATOR_BEFORE,sItem));

		BOOL bSeparatorAfter=FALSE;
		if(nItemIndex<(int)(GetMenuItemCount()-1))
		{
			MENUITEMINFO mii={ sizeof(MENUITEMINFO) };
			mii.fMask=MIIM_TYPE;
			mii.cch=300;
			mii.dwTypeData=sText.GetBuffer(mii.cch);
			// ... zero-terminate string
			mii.dwTypeData[0]=_T('\0');
			VERIFY(::GetMenuItemInfo(GetSafeHmenu(),nItemIndex+1,TRUE,&mii));
			sText.ReleaseBuffer();

			bSeparatorAfter=((mii.fType&MFT_SEPARATOR)==0);
		}
		VERIFY(sItem.LoadString(IDS_OX_CUSTBM_SEPARATOR_AFTER));
		VERIFY(menu.AppendMenu(MF_STRING|(bSeparatorAfter ? 0 : MF_GRAYED),
			ID_OXCUSTBM_SEPARATOR_AFTER,sItem));
	}

	CWnd* pWndOwner=NULL;
	if(m_pPopupWnd!=NULL && ::IsWindow(m_pPopupWnd->GetSafeHwnd()))
		pWndOwner=m_pPopupWnd;

	menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON,
		point.x,point.y,pWndOwner);

	return TRUE;
}


void COXBitmapMenu::OnCustBMDelete()
{
	ASSERT(IsInCustomizationMode());
	int nCustomizedItemIndex=GetCustomizedItem();
	ASSERT(nCustomizedItemIndex!=-1);

	if(SendCustomizeNotification(ID_OXCUSTBM_DELETE))
		return;

	VERIFY(DeleteMenu(nCustomizedItemIndex,MF_BYPOSITION));
	m_nCustomizedItemIndex=-1;
	UpdateContents();
}

void COXBitmapMenu::OnCustBMAppearance()
{
	ASSERT(IsInCustomizationMode());
	int nCustomizedItemIndex=GetCustomizedItem();
	ASSERT(nCustomizedItemIndex!=-1);
	UNUSED(nCustomizedItemIndex);

	SendCustomizeNotification(ID_OXCUSTBM_APPEARANCE);	
}

void COXBitmapMenu::OnCustBMSeparatorBefore()
{
	ASSERT(IsInCustomizationMode());
	int nCustomizedItemIndex=GetCustomizedItem();
	ASSERT(nCustomizedItemIndex!=-1);

	if(SendCustomizeNotification(ID_OXCUSTBM_SEPARATOR_BEFORE))
		return;

	VERIFY(InsertMenu(nCustomizedItemIndex,MF_SEPARATOR|MF_BYPOSITION));
	UpdateContents();
	SetCustomizedItem(nCustomizedItemIndex+1);
}

void COXBitmapMenu::OnCustBMSeparatorAfter()
{
	ASSERT(IsInCustomizationMode());
	int nCustomizedItemIndex=GetCustomizedItem();
	ASSERT(nCustomizedItemIndex!=-1);

	if(SendCustomizeNotification(ID_OXCUSTBM_SEPARATOR_AFTER))
		return;

	VERIFY(InsertMenu(nCustomizedItemIndex+1,MF_SEPARATOR|MF_BYPOSITION));
	UpdateContents();
}

void COXBitmapMenu::OnCustBMRecentlyUsed()
{
	ASSERT(IsInCustomizationMode());
	int nCustomizedItemIndex=GetCustomizedItem();
	ASSERT(nCustomizedItemIndex!=-1);

	if(SendCustomizeNotification(ID_OXCUSTBM_RECENTLY_USED))
		return;

	COXBitmapMenuOrganizer* pOrganizer=
		COXBitmapMenuOrganizer::FindOrganizer(AfxGetMainWnd()->GetSafeHwnd());
	ASSERT(pOrganizer!=NULL);
	if(pOrganizer->IsRecentlyUsed(this,nCustomizedItemIndex))
	{
		VERIFY(pOrganizer->
			ExcludeFromRecentlyUsed(this,nCustomizedItemIndex));
	}
	else
	{
		VERIFY(pOrganizer->
			AddToRecentlyUsed(this,nCustomizedItemIndex));
	}
	UpdateContents();
}


LRESULT COXBitmapMenu::SendCustomizeNotification(UINT nCustomizeCmdID)
{
	ASSERT(GetPopupWnd()!=NULL);

	HWND hWnd=m_hWndCustomizeOrganizer;
	if(hWnd==NULL)
	{
		hWnd=AfxGetMainWnd()->GetSafeHwnd();
	}
	if(hWnd==NULL)
		return (LRESULT)0;

	NMBMCUSTOMIZE nmbmCustomize;
	nmbmCustomize.nmhdr.code=OXBMN_CUSTOMIZECMD;
	nmbmCustomize.nmhdr.hwndFrom=GetPopupWnd()->GetSafeHwnd();
	nmbmCustomize.nmhdr.idFrom=::GetDlgCtrlID(hWnd);
	nmbmCustomize.nCustomizeEventID=nCustomizeCmdID;

	return ::SendMessage(hWnd,WM_NOTIFY,(WPARAM)nmbmCustomize.nmhdr.idFrom,
		(LPARAM)&nmbmCustomize);
}


LONG COXBitmapMenu::OnDragEnter(WPARAM wParam, LPARAM lParam)
{
	// toolbar must be in customizable state
	if(!IsCustomizable())
		return (LONG)FALSE;

	// set flag that specifies that drag'n'drop operation is active
	m_bDragDropOperation=TRUE;

	return OnDragOver(wParam,lParam);
}

LONG COXBitmapMenu::OnDragOver(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);

	// toolbar must be in customizable state
	if(!IsCustomizable())
		return (LONG)FALSE;

	// lParam is the pointer to SHBDROPTARGETACTION structure
	LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
	ASSERT(pSHBDTAction!=NULL);

	pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE;
#ifdef OX_CUSTOMIZE_COMMANDS
	// Can we use this object?
	if(pSHBDTAction->pDataObject->
		IsDataAvailable(COXDragDropCommands::m_cfCommandButton))
	{
		// analize the current cursor position
		//
		BOOL bBefore=TRUE;
		int nItemIndex=HitTest(&pSHBDTAction->point,&bBefore);
		int nInsertMarkIndex=(bBefore ? nItemIndex : nItemIndex+1);

		SetInsertMark(nInsertMarkIndex);

		// Check if the control key was pressed          
		if((pSHBDTAction->dwKeyState & MK_CONTROL)==MK_CONTROL)
			pSHBDTAction->result=(LRESULT)DROPEFFECT_COPY;
		else
			pSHBDTAction->result=(LRESULT)DROPEFFECT_MOVE; 
	}
#endif	//	OX_CUSTOMIZE_COMMANDS

	return (LONG)TRUE;
}

LONG COXBitmapMenu::OnDragLeave(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);

	if(!IsCustomizable())
		return (LONG)FALSE;

	// lParam is the pointer to SHBDROPTARGETACTION structure
	LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
	ASSERT(pSHBDTAction!=NULL);

	SetInsertMark(-1);

	// reset flag that specifies that drag'n'drop operation is active
	m_bDragDropOperation=FALSE;

	return (LONG)TRUE;
}

LONG COXBitmapMenu::OnDrop(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(wParam);

	if(!IsCustomizable())
		return (LONG)FALSE;

	// lParam is the pointer to SHBDROPTARGETACTION structure
	LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
	ASSERT(pSHBDTAction!=NULL);

	pSHBDTAction->result=(LRESULT)FALSE;

#ifdef OX_CUSTOMIZE_COMMANDS
	// if dragged item is to be copied or moved
	if((pSHBDTAction->dropEffect&DROPEFFECT_COPY)!=0 || 
		(pSHBDTAction->dropEffect&DROPEFFECT_MOVE)!=0)
	{
		// data must be in the specific format
		ASSERT(pSHBDTAction->pDataObject->
			IsDataAvailable(COXDragDropCommands::m_cfCommandButton));

		int nItemIndex=GetInsertMark();
		if(nItemIndex!=-1 || GetMenuItemCount()==0)
		{
			// Get the drag item info
			//
			HGLOBAL hgData=pSHBDTAction->pDataObject->
				GetGlobalData(COXDragDropCommands::m_cfCommandButton);
			ASSERT(hgData!=NULL);
			// lock it
			BYTE* lpItemData=(BYTE*)::GlobalLock(hgData);

			if(!SendCustomizeNotification(ID_OXCUSTBM_INSERT_ITEM))
			{
				RetrieveDragDropMenuItem(lpItemData,GetSafeHmenu(),nItemIndex);

				if(m_bDragDropOwner && m_nCustomizedItemIndex>=nItemIndex)
				{
					ASSERT(m_nCustomizedItemIndex!=-1);
					m_nCustomizedItemIndex++;
					ASSERT(GetDraggedItem()!=-1);
					SetDraggedItem(GetDraggedItem()+1);
				}
			}


			// unlock it
			::GlobalUnlock(hgData);
			// free it
			::GlobalFree(hgData);

			// remove insert mark
			SetInsertMark(-1);

			// drag'n'drop operation completed successfully
			pSHBDTAction->result=(LRESULT)TRUE;
		}
		else
		{
			pSHBDTAction->result=(LRESULT)FALSE;
		}
	}
#endif	//	OX_CUSTOMIZE_COMMANDS

	m_bDragDropOperation=FALSE;

	// we handled the message
	return (LONG)TRUE;
}


void COXBitmapMenu::RetrieveDragDropMenuItem(BYTE*& lpData, HMENU hMenu, 
											 int nItemIndex)
{
	ASSERT(lpData!=NULL);
	ASSERT(hMenu!=NULL);
	ASSERT(::IsMenu(hMenu));

	COXBitmapMenuOrganizer* pOrganizer=
		COXBitmapMenuOrganizer::FindOrganizer(AfxGetMainWnd()->GetSafeHwnd());
	ASSERT(pOrganizer!=NULL);

	while(TRUE)
	{
		// get button command ID
		int nCommandID=*(int*)lpData;
		lpData+=sizeof(int);
		
		// get button text
		CString sText((LPTSTR)lpData);
		lpData+=sText.GetLength()+sizeof(TCHAR);

		if(sText.IsEmpty() && nCommandID!=0)
		{
			HINSTANCE hInstance=
				AfxFindResourceHandle(MAKEINTRESOURCE(nCommandID),RT_STRING);
			ASSERT(hInstance!=NULL);
			sText.LoadString(nCommandID);
			int nPosition=sText.Find(_T('\n'));
			if(nPosition!=-1)
				sText=sText.Mid(nPosition+1);
		}
		ASSERT(!sText.IsEmpty() || nCommandID==0);

		// get button image index
		int nImageIndex=*(int*)lpData;
		lpData+=sizeof(int);
		UNREFERENCED_PARAMETER(nImageIndex);

		// get button style
		BYTE fsStyle=*(BYTE*)lpData;
		lpData+=sizeof(BYTE);
		UNREFERENCED_PARAMETER(fsStyle);

		// get finish flag
		int nFinish=*(int*)lpData;
		lpData+=sizeof(int);

		// update menu
		if(nCommandID==-1)
		{
			HMENU hSubMenu=::CreatePopupMenu();
			ASSERT(hSubMenu!=NULL);
			pOrganizer->m_arrCreatedPopupMenus.Add(hSubMenu);
			VERIFY(::InsertMenu(hMenu,nItemIndex,
				MF_BYPOSITION|MF_POPUP|MF_STRING,(UINT_PTR)hSubMenu,sText)!=0);
/*
			CMenu* pMenu=CMenu::FromHandle(hMenu);
			ASSERT(pMenu!=NULL);
			COXBitmapMenu* pBitmapMenu=DYNAMIC_DOWNCAST(COXBitmapMenu,pMenu);
			if(pBitmapMenu!=NULL)
			{
				pOrganizer->ConvertBitmapMenu(pBitmapMenu,FALSE);
			}
*/
			if(nFinish!=0)
			{
				RetrieveDragDropMenuItem(lpData,hSubMenu,0);
			}
		}
		else
		{
			VERIFY(::InsertMenu(hMenu,nItemIndex,
				MF_BYPOSITION|(nCommandID==0 ? MF_SEPARATOR : MF_STRING),
				(UINT)nCommandID,sText)!=0);
		}
		nItemIndex++;

		if(nFinish==0 || nFinish==2)
			break;
	}

	CMenu* pMenu=CMenu::FromHandle(hMenu);
	ASSERT(pMenu!=NULL);
	COXBitmapMenu* pBitmapMenu=DYNAMIC_DOWNCAST(COXBitmapMenu,pMenu);
	if(pBitmapMenu==NULL)
	{
		pOrganizer->OnInitMenuPopup(pMenu,0,FALSE);
		pMenu=CMenu::FromHandle(hMenu);
		pBitmapMenu=DYNAMIC_DOWNCAST(COXBitmapMenu,pMenu);
	}
	ASSERT(pBitmapMenu!=NULL);
	pBitmapMenu->UpdateContents();
}


int COXBitmapMenu::SetInsertMark(int nItemIndex) 
{
	ASSERT(nItemIndex>=-1 && nItemIndex<=(int)GetMenuItemCount());
	int nOldInsertMark=m_nInsertMarkIndex;
	m_nInsertMarkIndex=nItemIndex;
	if(nOldInsertMark!=m_nInsertMarkIndex)
	{
		if(m_pPopupWnd!=NULL && ::IsWindow(m_pPopupWnd->GetSafeHwnd()))
		{
			if(nOldInsertMark!=-1)
			{
				int nInsertMark=nOldInsertMark;
				if(nInsertMark==(int)GetMenuItemCount())
				{
					nInsertMark--;
				}
				m_pPopupWnd->RedrawItem(nInsertMark);
				if(nOldInsertMark!=0 && nOldInsertMark!=(int)GetMenuItemCount() &&
					nOldInsertMark!=m_nInsertMarkIndex+1)
				{
					m_pPopupWnd->RedrawItem(nOldInsertMark-1);
				}
			}
			if(m_nInsertMarkIndex!=-1)
			{
				int nInsertMark=m_nInsertMarkIndex;
				if(nInsertMark==(int)GetMenuItemCount())
					nInsertMark--;
				m_pPopupWnd->RedrawItem(nInsertMark);
				if(m_nInsertMarkIndex!=0 && 
					m_nInsertMarkIndex!=(int)GetMenuItemCount() &&
					m_nInsertMarkIndex!=nOldInsertMark+1)
				{
					m_pPopupWnd->RedrawItem(m_nInsertMarkIndex-1);
				}
			}
		}
		SendCustomizeNotification(ID_OXCUSTBM_SET_INSERT_MARK);
	}
	return nOldInsertMark;
}


int COXBitmapMenu::HitTest(LPPOINT ppt, BOOL* pbBefore/*=NULL*/)
{
	int nItemIndex=-1;
	int nHeight=0;
	for(int nIndex=0; nIndex<(int)GetMenuItemCount(); nIndex++)
	{
		MENUITEMINFO mii={ sizeof(mii) };
		mii.fMask=MIIM_DATA;
		VERIFY(::GetMenuItemInfo(GetSafeHmenu(),nIndex,TRUE,&mii));

		MEASUREITEMSTRUCT mis;
		mis.CtlType=ODT_MENU;
		mis.itemData=mii.dwItemData;
		mis.itemID=GetMenuItemID(nIndex);
		mis.itemHeight=(UINT)-1;
		mis.itemWidth=(UINT)-1;
		MeasureItem(&mis);
		
		nHeight+=mis.itemHeight;
		if(ppt->y<=nHeight)
		{
			nItemIndex=nIndex;
			if(pbBefore!=NULL)
				*pbBefore=(ppt->y<=nHeight-(int)mis.itemHeight/2);
			break;
		}
	}

	return nItemIndex;
}


void COXBitmapMenu::UpdateContents()
{
	COXBitmapMenuOrganizer* pOrganizer=
		COXBitmapMenuOrganizer::FindOrganizer(AfxGetMainWnd()->GetSafeHwnd());
	if(pOrganizer!=NULL)
	{
		pOrganizer->ConvertBitmapMenu(this,FALSE);
	}
	if(m_pPopupWnd!=NULL && ::IsWindow(m_pPopupWnd->GetSafeHwnd()))
	{
		m_pPopupWnd->OnMenuChanged();
	}
}


void COXBitmapMenu::OnBeginDragDrop(int nIndex)
{
	ASSERT(nIndex>=0 && nIndex<(int)GetMenuItemCount());
	ASSERT(m_pPopupWnd!=NULL && ::IsWindow(m_pPopupWnd->GetSafeHwnd()));

	UNUSED(nIndex);

	// mark the control as the one that launched drag'n'drop operation
	m_bDragDropOwner=TRUE;

#ifdef OX_CUSTOMIZE_COMMANDS
	CString sText;
	MENUITEMINFO mii={ sizeof(mii) };
	mii.fMask=MIIM_DATA|MIIM_TYPE|MIIM_ID|MIIM_SUBMENU;
	mii.cch=300;
	mii.dwTypeData=sText.GetBuffer(mii.cch);
	VERIFY(::GetMenuItemInfo(GetSafeHmenu(),nIndex,TRUE,&mii));
	sText.ReleaseBuffer();

	// don't allow to drag most recent files used 
	if((int)mii.wID>=ID_FILE_MRU_FILE1 && (int)mii.wID<=ID_FILE_MRU_FILE16)
	{
		// unmark as the control which launched drag'n'drop operation
		m_bDragDropOwner=FALSE;
		return;
	}

	// don't allow to drag MDIChild windows menu items 
	if((int)mii.wID>=AFX_IDM_FIRST_MDICHILD && mii.hSubMenu==NULL)
	{
		// unmark as the control which launched drag'n'drop operation
		m_bDragDropOwner=FALSE;
		return;
	}


	COXItemInfo* pItemInfo=(COXItemInfo*)(mii.dwItemData);
	ASSERT(AfxIsValidAddress(pItemInfo,sizeof(COXItemInfo)));
	ASSERT(!pItemInfo->GetText().IsEmpty() || mii.fType&MFT_SEPARATOR);

	CMenu* pSubmenu=GetSubMenu(nIndex);
	if(pSubmenu!=NULL)
	{
		COXBitmapMenu* pBMSubmenu=DYNAMIC_DOWNCAST(COXBitmapMenu,pSubmenu);
		if(pBMSubmenu!=NULL)
		{
			if(pBMSubmenu->GetPopupWnd()!=NULL)
			{
				pBMSubmenu->GetPopupWnd()->ResetPopupMenu();
			}
		}
	}

	COleDataSource* pDataSource=
		COXDragDropCommands::PrepareDragDropData(pItemInfo->GetText(),
		pItemInfo->GetImageInfo()->GetIndex(),GetMenuItemID(nIndex),0,
		GetSubMenu(nIndex)->GetSafeHmenu());
	ASSERT(pDataSource!=NULL);

	m_bDragDropOperation=TRUE;
	SetDraggedItem(nIndex);
	DROPEFFECT dropEffect=
		COXDragDropCommands::DoDragDrop(pDataSource,GetDropSource(m_pPopupWnd));
	if(DROPEFFECT_MOVE==dropEffect || (DROPEFFECT_NONE==dropEffect &&
		::GetKeyState(VK_LBUTTON)>=0 && !m_bDragDropOperation))
	{
		if(!SendCustomizeNotification(ID_OXCUSTBM_DELETE_ITEM))
		{
			int nDraggedItemIndex=GetDraggedItem();
			// delete item if it was moved
			VERIFY(DeleteMenu(nDraggedItemIndex,MF_BYPOSITION));
			m_nCustomizedItemIndex=-1;
			UpdateContents();
		}
	}
	SetDraggedItem(-1);

	//delete drag source (we are responsible to do that)
	delete pDataSource;
#endif	//	OX_CUSTOMIZE_COMMANDS

	// unmark as the control which launched drag'n'drop operation
	m_bDragDropOwner=FALSE;
}

COXMenuSkin* COXBitmapMenu::GetMenuSkin()
{
	// Check if the app is derived from COXSkinnedApp
	COXSkinnedApp* pSkinnedApp = DYNAMIC_DOWNCAST(COXSkinnedApp, AfxGetApp());
	if (pSkinnedApp != NULL && pSkinnedApp->GetCurrentSkin() != NULL)
		return pSkinnedApp->GetCurrentSkin()->GetMenuSkin();
	else
	{
		// Create a classic skin for this class if not created already
		if (m_pMenuSkin == NULL)
			m_pMenuSkin = new COXMenuSkinClassic();

		return m_pMenuSkin;
	}
}

// Returns TRUE if the given item is a popup menu (has a submenu)
BOOL COXBitmapMenu::IsPopupItem(UINT nItemID)
{
	if (nItemID == (UINT)-1)
		return TRUE;

	MENUITEMINFO mii;
	::memset(&mii, 0, sizeof(MENUITEMINFO));
	mii.cbSize = sizeof(MENUITEMINFO);
	mii.fMask = MIIM_SUBMENU;

	GetMenuItemInfo(nItemID, &mii);
	if (mii.hSubMenu != NULL)
		return TRUE;
	else
		return FALSE;
}

void COXBitmapMenu::RestoreMDI()
{
	CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();
	CMDIChildWnd * active = pMainWnd->MDIGetActive();	
	
	active->MDIRestore();

	pMainWnd->MDINext();

	while(active != pMainWnd->MDIGetActive())
	{
		pMainWnd->MDIGetActive()->MDIRestore();
		pMainWnd->MDINext();
	}
}

void COXBitmapMenu::CloseMDI()
{
	CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();

	CMDIChildWnd * active = pMainWnd->MDIGetActive();

	if (active->GetActiveDocument()->IsModified())
	{
		CString sTitle;
		active->GetActiveFrame()->GetWindowText(sTitle);

		int ID = AfxMessageBox(_T("Save changes to ") + sTitle +  _T(" ?"), MB_YESNOCANCEL);

		switch(ID)
		{
		case IDCANCEL:
			// Do nothing
			break;
		case IDYES:
			active->GetActiveDocument()->DoFileSave();
			// Fall through intended
		case IDNO:
			active->DestroyWindow();
		}
	}
	else
	{
		active->DestroyWindow();
	}
}

void COXBitmapMenu::MinimizeMDI()
{
	CMDIFrameWnd* pMainWnd = (CMDIFrameWnd*)AfxGetMainWnd();

	CMDIChildWnd * active = pMainWnd->MDIGetActive();

	active->MDIRestore();
	active->ShowWindow(SW_MINIMIZE);}


BOOL COXBitmapMenu::HandleMDICommandMessage(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	UNREFERENCED_PARAMETER(pHandlerInfo);

	if (nCode == CN_UPDATE_COMMAND_UI)
    {
		switch(nID)
		{
		case ID_OXBITMAPMENU_CLOSE:
		case ID_OXBITMAPMENU_MINIMIZE:
		case ID_OXBITMAPMENU_RESTORE:
			((CCmdUI *) pExtra)->Enable (TRUE);
			return TRUE;
		}
    }
    else
    if (nCode == CN_COMMAND)
    {		
		bool bHandled = false;
		switch(nID)
		{	
		case ID_OXBITMAPMENU_CLOSE:
			CloseMDI();
			bHandled = true;
			break;
		case ID_OXBITMAPMENU_MINIMIZE:
			MinimizeMDI();
			bHandled = true;
			break;
		case ID_OXBITMAPMENU_RESTORE:
			RestoreMDI();
			bHandled = true;
			break;	
		case SC_MAXIMIZE:
			return TRUE;
		}	
		if(bHandled)
		{
			CMDIFrameWnd* pMainWnd = static_cast<CMDIFrameWnd*>(AfxGetMainWnd());
			if(pMainWnd)
			{
				CMDIChildWnd* pActiveWnd = pMainWnd->MDIGetActive();
				if(pActiveWnd)
				{
					pActiveWnd->GetSystemMenu(TRUE);
				}
			}
			return TRUE;
		}
	}
	
	return FALSE;
}


#ifndef CS_DROPSHADOW
#define CS_DROPSHADOW       0x00020000
#endif

BOOL COXBitmapMenu::RegisterWindowClass(HINSTANCE hInstance)
{
	if (m_origWndProc != NULL)
		return TRUE; // already registed

	WNDCLASS wndclass;

    if (GetClassInfo(hInstance,_T("#32768"),&wndclass))
		m_origWndProc = wndclass.lpfnWndProc;
    wndclass.lpfnWndProc = PopupWndProc;
	wndclass.style &= ~CS_DROPSHADOW;
	wndclass.style |= CS_SAVEBITS;
    wndclass.hInstance = hInstance ;
    RegisterClass(&wndclass);

    if (GetClassInfo(hInstance,_T("#32768"),&wndclass))
        return TRUE;
	else
		return FALSE;
}

LRESULT CALLBACK COXBitmapMenu::PopupWndProc(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
	return GetMenuSkin()->MenuPopupWndProc(m_origWndProc, hwnd, nMsg, wParam, lParam);
}

void COXBitmapMenuPopupWnd::OnNcPaint() 
{
	COXBitmapMenu::GetMenuSkin()->OnNcPaintCustomizePopupWnd(this);
}

BOOL COXBitmapMenu::TrackPopupMenu(COXCoolToolBar* pCoolToolBar, CWnd* pWnd, LPCRECT lpRect)
{
	// Determine the selected item
	int iSelectedItem = -1;
	CToolBarCtrl& ctrl = pCoolToolBar->GetToolBarCtrl();
	int iButtonCount = ctrl.GetButtonCount();
	for (int i = 0; i < iButtonCount; i++)
	{
		if (ctrl.IsButtonPressed(pCoolToolBar->GetItemID(i)))
		{
			iSelectedItem = i;
			break;
		}
	}

	if (iSelectedItem == -1)
		iSelectedItem = ctrl.CommandToIndex(pCoolToolBar->m_iLastDropDownIndex);

	// Determine the rectangle of the selected item
	CRect rectItem;
	pCoolToolBar->GetItemRect(iSelectedItem, &rectItem);

	COXMenuBar* pMenuBar = DYNAMIC_DOWNCAST(COXMenuBar, pCoolToolBar);

	if (pMenuBar != NULL && iSelectedItem == -1)
	{
		// This is the icon item

		rectItem = pMenuBar->m_iconRect;

		CRect rectWindow;
		pMenuBar->GetWindowRect(rectWindow);
		pMenuBar->ScreenToClient(&rectWindow);

		CRect rectClient;
		pMenuBar->GetClientRect(rectClient);

		rectItem.left += rectWindow.left - rectClient.left;
		rectItem.right += rectWindow.left - rectClient.left;
		rectItem.top += rectWindow.top - rectClient.top;
		rectItem.bottom += rectWindow.top - rectClient.top;

		rectItem.InflateRect(3, 3);
	}
	pCoolToolBar->ClientToScreen(&rectItem);

	// Determine the position of the popup menu relative to the selected item
	CPoint ptTopLeft;
	UINT nFlags, nPosFlags;
	DeterminePosition(this, rectItem, pCoolToolBar->m_dwStyle, ptTopLeft, nFlags, nPosFlags);

	// Clear the queue of any pending messages
	MSG msg;
	while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0)
	{
		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}	

	COXShadowedItemWnd* pSIW = new COXShadowedItemWnd(pCoolToolBar, iSelectedItem, nPosFlags);
	CRect rect;
	pCoolToolBar->GetItemRect(iSelectedItem, rect);
	rect.DeflateRect(0, 1);

	// Save the item rectangle in screen coordinates
	m_rectDropDownItem = rect;
	pCoolToolBar->ClientToScreen(m_rectDropDownItem);

	// Create the transparent window which will be responsible for drawing the shadow
	// The transparent window must be a child of the main window
	rect.InflateRect(0, 0, 4, 4);
	pCoolToolBar->ClientToScreen(rect);
	pSIW->CreateEx(WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, NULL, NULL, WS_CHILD | WS_VISIBLE, 
		rect, CWnd::GetDesktopWindow(), 0, NULL);

	BOOL bResult = CMenu::TrackPopupMenu(nFlags, ptTopLeft.x, ptTopLeft.y, pWnd, lpRect);

	pSIW->DestroyWindow();
	delete pSIW;
	pSIW = NULL;

	m_rectDropDownItem.SetRectEmpty();

	return bResult;
}

// Returns the size of the popup menu before it is displayed
CSize COXBitmapMenu::GetPopupMenuSize(CMenu* pMenu)
{
	CSize szMenu(0, 0);

	int iCount = pMenu->GetMenuItemCount();
	for (int i = 0; i < iCount; i++)
	{
		UINT nItemID = pMenu->GetMenuItemID(i);

		// Get the data member
		MENUITEMINFO miinfo;
		::memset(&miinfo, 0, sizeof(MENUITEMINFO));
		miinfo.cbSize = sizeof(MENUITEMINFO);
		miinfo.fMask = MIIM_DATA;
		pMenu->GetMenuItemInfo(nItemID, &miinfo);

		// Call CMenu::MeasureItem() to determine the item's dimensions
		MEASUREITEMSTRUCT mi;
		::memset(&mi, 0, sizeof(MEASUREITEMSTRUCT));
		mi.CtlType = ODT_MENU;
		mi.itemID = nItemID;
		mi.itemData = miinfo.dwItemData;

		pMenu->MeasureItem(&mi);

		if (mi.itemHeight == 0 && DYNAMIC_DOWNCAST(COXBitmapMenu, pMenu) == NULL)
		{
			// This is not a COXBitmapMenu
			if (nItemID == 0)
				mi.itemHeight = 8; // separator
			else
				mi.itemHeight = 17; // normal item
		}

		if ((int) mi.itemWidth > szMenu.cx)
			szMenu.cx = mi.itemWidth;

		szMenu.cy += mi.itemHeight;
	}

	// Compensate for the non-client area
	szMenu.cx += 18;
	szMenu.cy += 6;

	return szMenu;
}

// v9.3 update 01 fixes and changes: Manfred Drasch
// Horizontal menu:
//    * wrong ypos under some circumstances
//    * avoid a drawingproblem (the menubutton was painted over the popupmenu)
// Vertical menu:
//    * wrong xpos under some circumstances
// Both:
//    * Menu now shown over vertical Appbar/Sidebar, too

void COXBitmapMenu::DeterminePosition(CMenu* pMenu, LPCRECT lpItemRect, DWORD dwStyle, CPoint& ptTopLeft, UINT& nFlags, UINT& nPosFlags)
{
    // Get the rectangle of the monitor ( + WorkArea) closest to the menu rectangle
    CRect rctMonitorWorkArea;
    CRect rctMonitor;

    CPoint ptCursor;
    HMONITOR hMonitor = ::MonitorFromRect(lpItemRect, MONITOR_DEFAULTTONEAREST);
    MONITORINFO mi;
    mi.cbSize = sizeof(MONITORINFO);
    if(hMonitor!=NULL && ::GetMonitorInfo(hMonitor, &mi))
    {
        rctMonitorWorkArea = mi.rcWork;
        rctMonitor = mi.rcMonitor;
    }
    else
    {
        rctMonitorWorkArea.SetRect(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
        rctMonitor.SetRect(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
    }

    CSize sizeMenu = GetPopupMenuSize(pMenu);
    ptTopLeft.x = 0;
    ptTopLeft.y = 0;
    nFlags = 0;

    if (dwStyle & CBRS_ORIENT_HORZ)
    {
        // Horizontal menu
        ptTopLeft.x = lpItemRect->left;

        if (ptTopLeft.x < rctMonitor.left)
            ptTopLeft.x = rctMonitor.left;

        if (ptTopLeft.x + sizeMenu.cx > rctMonitor.right)
            ptTopLeft.x = rctMonitor.right - 1;
        else
        {
            if (ptTopLeft.x + sizeMenu.cx + 1 >= rctMonitorWorkArea.right)
            {
                ptTopLeft.x += sizeMenu.cx;
                if (ptTopLeft.x == 0)
                    ptTopLeft.x = -1;
            }
        }

        if (lpItemRect->bottom + sizeMenu.cy > rctMonitorWorkArea.bottom)
        {
            // The popup menu should be above the item
            nFlags |= TPM_BOTTOMALIGN;
            nPosFlags = OX_TPM_TOP;
            ptTopLeft.y = lpItemRect->top + 1;
        }
        else
        {
            // The popup menu should be below the item
            nFlags |= TPM_TOPALIGN;
            nPosFlags = OX_TPM_BOTTOM;
            ptTopLeft.y = lpItemRect->bottom - 1;
        }
    }
    else
    {
        // Vertical menu
        if (lpItemRect->right + sizeMenu.cx > rctMonitor.right)
        {
            // The popup menu should be left of the item
            nFlags |= TPM_RIGHTALIGN;
            nPosFlags = OX_TPM_LEFT;
            ptTopLeft.x = lpItemRect->left;
        }
        else
        {
            // The popup menu should be right of the item
            nFlags |= TPM_LEFTALIGN;
            nPosFlags = OX_TPM_RIGHT;

            if (lpItemRect->right + sizeMenu.cx + 1 >= rctMonitorWorkArea.right)
                ptTopLeft.x = lpItemRect->right + sizeMenu.cx;
            else
                ptTopLeft.x = lpItemRect->right;

            if (ptTopLeft.x == 0)
                ptTopLeft.x = -1;
        }

        ptTopLeft.y = lpItemRect->top + 1;
        if (ptTopLeft.y < rctMonitorWorkArea.top)
            ptTopLeft.y = rctMonitorWorkArea.top;

        if (ptTopLeft.y + sizeMenu.cy > rctMonitorWorkArea.bottom)
            ptTopLeft.y = rctMonitorWorkArea.bottom - sizeMenu.cy;
    }
}

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

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions