Click here to Skip to main content
15,885,908 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 : COXPopupBarCtrl
// ==========================================================================

// 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.

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

// OXPopupBarCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "OXPopupBarCtrl.h"

#include <windowsx.h>
#include <afxpriv.h>

#include "UTB64Bit.h"


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


#define POPUPBAR_CLASSNAME	_T("PopupBarControl")

// Messages sent to popup bar window
// 
// m_nOkMessage			-	hide popup bar control and return TRUE from Pick function
// m_nCancelMessage		-	hide popup bar control and return FALSE from Pick function
// m_nCustomButtonPicked-	hide popup bar control and call virtual OnCustomButton 
//							function. Result of OnCustomButton function is used as a 
//							result of Pick function
// m_nTearoffMessage	-	convert popup bar to floating window with caption
//
UINT COXPopupBarCtrl::m_nOkMessage=::RegisterWindowMessage(_T("_POPUPBAR_OK_"));
UINT COXPopupBarCtrl::m_nCancelMessage=
::RegisterWindowMessage(_T("_POPUPBAR_CANCEL_"));
UINT COXPopupBarCtrl::m_nCustomButtonPicked=
::RegisterWindowMessage(_T("_POPUPBAR_CUSTOMBUTTONPICKED_"));
UINT COXPopupBarCtrl::m_nTearoffMessage=
::RegisterWindowMessage(_T("_POPUPBAR_TEAROFF_"));

/////////////////////////////////////////////////////////////////////////////
// COXPopupBarCtrl

COXPopupBarCtrl::COXPopupBarCtrl(UINT nButtons, UINT nRows, DWORD dwDefault,
								 CSize sizeButton, CString sDefaultButtonText,
								 CString sCustomButtonText) :
m_nButtons(nButtons),
m_nRows(nRows),
m_dwDefault(dwDefault),
m_sizeButton(sizeButton),
m_sDefaultButtonText(sDefaultButtonText),
m_sCustomButtonText(sCustomButtonText),
// Define margines used to draw buttons. 
// In your derived class you can redefine them.
m_rectEdgeMargin(4,2,4,2),
m_rectButtonMargin(0,0,0,0),
m_rectDefaultButtonMargin(0,0,0,2),
m_rectCustomButtonMargin(0,2,0,0),
m_rectTearoffBarMargin(2,-1,2,2),
// by default none button selected
m_dwSelected(ID_POPUPBAR_DATA_NONE),
m_pParentWnd(NULL),
m_bMouseButtonPressed(FALSE),
m_bClickedInTearoffBar(FALSE),
m_bFloating(FALSE),
m_nCheckMouseTimer(0),
m_bCheckMousePos(TRUE),
m_ptLastMousePos(-1,-1),
m_pTrackingDC(NULL),
m_ptLastTrack(0,0),
m_rectTrackBar(0,0,0,0),
m_rectLastTrackBar(0,0,0,0),
m_sizeLastTrackBar(0,0),
m_bDitherLast(FALSE)
{
	m_sTearoffText.LoadString(IDS_OX_POPUPBARTEAROFF);// "Drag to make this menu float"
	m_sCaption.LoadString(IDS_OX_POPUPBARCAPTION);// "Floating bar"
}


COXPopupBarCtrl::COXPopupBarCtrl(UINT nButtons, UINT nRows, DWORD dwDefault,
								 CSize sizeButton, int nIDdefault, int nIDcustom) :
m_nButtons(nButtons),
m_nRows(nRows),
m_dwDefault(dwDefault),
m_sizeButton(sizeButton),
// Define margines used to draw buttons. 
// In your derived class you can redefine them.
m_rectEdgeMargin(4,2,4,2),
m_rectButtonMargin(0,0,0,0),
m_rectDefaultButtonMargin(0,0,0,2),
m_rectCustomButtonMargin(0,2,0,0),
m_rectTearoffBarMargin(2,-1,2,2),
// by default none button selected
m_dwSelected(ID_POPUPBAR_DATA_NONE),
m_pParentWnd(NULL),
m_bMouseButtonPressed(FALSE),
m_bClickedInTearoffBar(FALSE),
m_bFloating(FALSE),
m_nCheckMouseTimer(0),
m_bCheckMousePos(TRUE),
m_ptLastMousePos(-1,-1),
m_pTrackingDC(NULL),
m_ptLastTrack(0,0),
m_rectTrackBar(0,0,0,0),
m_rectLastTrackBar(0,0,0,0),
m_sizeLastTrackBar(0,0),
m_bDitherLast(FALSE)
{
	m_sDefaultButtonText.LoadString(nIDdefault);
	m_sCustomButtonText.LoadString(nIDcustom);
}


COXPopupBarCtrl::~COXPopupBarCtrl()
{
	// clear all data
	m_arrData.RemoveAll();
	m_arrToolTipText.RemoveAll();
	m_arrButtonRect.RemoveAll();

	if((HFONT)m_font!=NULL)
	{
		m_font.DeleteObject();
	}

	if(::IsWindow(m_hWnd))
	{
		DestroyWindow();
	}
}


BEGIN_MESSAGE_MAP(COXPopupBarCtrl, CWnd)
	//{{AFX_MSG_MAP(COXPopupBarCtrl)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_SYSKEYUP()
	ON_WM_SYSKEYDOWN()
	ON_WM_KEYUP()
	ON_WM_KEYDOWN()
	ON_WM_PAINT()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_QUERYNEWPALETTE()
	ON_WM_PALETTECHANGED()
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_WM_ACTIVATE()
	ON_WM_TIMER()
	ON_WM_ERASEBKGND()
	ON_MESSAGE(WM_FLOATSTATUS, OnFloatStatus)
	ON_WM_NCCREATE()
	ON_WM_NCACTIVATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()



BOOL COXPopupBarCtrl::Create(CWnd* pParentWnd, 
							 UINT nPopupBarStyle/*=POPUPBAR_NOTEAROFF*/, 
							 HBRUSH hbrBackground/*=NULL*/)
{
	ASSERT_VALID(pParentWnd);
	// parent window have to exist
	ASSERT(::IsWindow(pParentWnd->GetSafeHwnd()));

	m_pParentWnd=pParentWnd;

	m_nPopupBarStyle=nPopupBarStyle;

	// creation of window
	// 

	if(hbrBackground==NULL)
	{
		hbrBackground=(HBRUSH)(COLOR_BTNFACE+1);
	}

	// define our own window class 
	WNDCLASS wndClass;
	wndClass.style=CS_SAVEBITS; 
	wndClass.lpfnWndProc=AfxWndProc; 
	wndClass.cbClsExtra=0; 
	wndClass.cbWndExtra=0; 
	wndClass.hInstance=AfxGetInstanceHandle(); 
	wndClass.hIcon=0; 
	wndClass.hCursor=::LoadCursor(NULL,IDC_ARROW); 
	wndClass.hbrBackground=hbrBackground; 
	wndClass.lpszMenuName=NULL; 
	wndClass.lpszClassName=POPUPBAR_CLASSNAME;

	if(!AfxRegisterClass(&wndClass))
	{
		return FALSE;
	}

	CRect rect(0,0,0,0);
	DWORD dwStyle=WS_POPUP;
	DWORD dwExStyle=WS_EX_DLGMODALFRAME;
	if(IsFloating())
	{
		dwStyle|=WS_CAPTION|WS_VISIBLE|WS_SYSMENU|MFS_SYNCACTIVE; 
		dwExStyle|=WS_EX_TOOLWINDOW;
	}
	if(!CWnd::CreateEx(dwExStyle,wndClass.lpszClassName,m_sCaption,dwStyle,
		rect,m_pParentWnd,NULL,NULL))
	{
		TRACE(_T("COXPopupBarCtrl::Create: failed to create popup bar\n"));
		return FALSE;
	}

	// setup timer to check constantly mouse position
	m_nCheckMouseTimer=SetTimer(IDT_OXPB_CHECKMOUSE,ID_OXPB_CHECKMOUSE_DELAY,NULL);
	if(m_nCheckMouseTimer==0)
	{
		DestroyWindow();
		TRACE(_T("COXPopupBarCtrl::Create: failed to set timer to check mouse position\n"));
		return FALSE;
	}

	// create tooltip control
	if(!m_ctlToolTip.Create(this)) 
	{
		DestroyWindow();
		TRACE(_T("COXPopupBarCtrl::Create: failed to create tooltip control\n"));
		return FALSE;
	}

	// update system menu
	if(dwStyle & WS_SYSMENU)
	{
		CMenu* pSysMenu=GetSystemMenu(FALSE);

		// Remove standard Restore, Maximize and Minimize and Size items out of 
		// the system menu.
		if(pSysMenu!=NULL)
		{
			pSysMenu->RemoveMenu(SC_RESTORE,MF_BYCOMMAND);
			pSysMenu->RemoveMenu(SC_MINIMIZE,MF_BYCOMMAND);
			pSysMenu->RemoveMenu(SC_MAXIMIZE,MF_BYCOMMAND);
			pSysMenu->RemoveMenu(SC_SIZE,MF_BYCOMMAND);
		}
	}

	return TRUE;
}


BOOL COXPopupBarCtrl::Pick(UINT nAlignment, CRect* pParentRect, CSize sizeOffset)
{
	ASSERT_VALID(m_pParentWnd);

	if(IsFloating())
	{
		ASSERT(::IsWindow(GetSafeHwnd()));

		DestroyWindow();
		m_bFloating=FALSE;

		// Get the background brush
		WNDCLASS wc;
		HBRUSH hBrush=NULL;
		if(GetClassInfo(AfxGetInstanceHandle(),POPUPBAR_CLASSNAME,&wc))
			hBrush=wc.hbrBackground;

		if(!Create(m_pParentWnd,m_nPopupBarStyle,hBrush))
			return FALSE;
	}

	ASSERT(::IsWindow(m_pParentWnd->GetSafeHwnd()));
	ASSERT(::IsWindow(m_hWnd));

	ASSERT(m_nButtons<=ID_POPUPBAR_MAX_BUTTONS && m_nButtons>0);
	ASSERT(m_nRows<=ID_POPUPBAR_MAX_BUTTONS && m_nRows>0 && m_nRows<=m_nButtons);

	// nobody has cared to fill array of common buttons
	for(int nIndex= PtrToInt(m_arrData.GetSize()); nIndex<(int)m_nButtons; nIndex++)
	{
		m_arrData.Add(0);
	}
	ASSERT(m_arrData.GetSize()>=(int)m_nButtons);


	// OK, we did all preparations
	//

	// now we've got to calculate size of control's window
	if(!CalcWindowSize())
	{
		return FALSE;
	}


	// a-ha, we've got size of window but we have to display it
	// in a nice spot (at least in the place user wants us to put in)
	if(!AdjustWindowPosition(nAlignment,pParentRect,sizeOffset))
	{
		return FALSE;
	}

	// current index by default is not set
	m_nCurrentIndex=ID_POPUPBAR_INDEX_NONE;
	// set selected index
	m_nSelectedIndex=ID_POPUPBAR_INDEX_NONE;
	if(m_dwSelected!=ID_POPUPBAR_DATA_NONE)
	{
		m_nSelectedIndex=GetIndexFromData(m_dwSelected);
	}


	// now we can populate the tooltip control
	if(!PopulateToolTip())
	{
		return FALSE;
	}

	// make sure to show/hide tooltip depending on the style
	m_ctlToolTip.Activate(IsToolTip());

	// it's show time!
	SetWindowPos(NULL,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height(),
		SWP_NOZORDER|SWP_NOACTIVATE);
	ShowWindow(SW_SHOWNA);
	HWND hFocusWnd=::GetFocus();
	if(hFocusWnd)
	{
		::SetFocus(NULL);
	}

	// Capture all mouse events for the life of this window
	HWND hOldCaptureOwner=::SetCapture(m_hWnd);

	m_bMouseButtonPressed=FALSE;

	BOOL bRouteLastMessage=FALSE;
	// init message loop
	// m_nState = 0 - control is active
	// m_nState = 1 - user selected some color
	// m_nState = 2 - user left control without selecting any color
	// m_nState = 3 - custom button was pressed
	// m_nState = 4 - menu has been torn off - need some restructing
	m_nState=0;
	while(m_nState==0)
	{
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));
		m_ctlToolTip.RelayEvent(&msg);

		if(::GetCapture() != m_hWnd)
		{
			break;
		}

		if(msg.message==WM_SYSKEYUP)
		{
			OnSysKeyUp(
				PtrToUint(msg.wParam),
				LOWORD(msg.lParam),
				HIWORD(msg.lParam));
			continue;
		}

		if(msg.message==WM_SYSKEYDOWN)
		{
			OnSysKeyDown(
				PtrToUint(msg.wParam),
				LOWORD(msg.lParam),
				HIWORD(msg.lParam));
			continue;
		}

		if(msg.message==m_nOkMessage)
		{
			m_nState=1;
		}
		else if(msg.message==m_nCancelMessage)
		{
			m_nState=2;
		}
		else if(msg.message==m_nCustomButtonPicked)
		{
			m_nState=3;
			break;
		}
		else if(msg.message==m_nTearoffMessage)
		{
			m_nState=4;
			break;
		}
		else
		{
			DispatchMessage(&msg);
		}

		if(msg.message==WM_LBUTTONDOWN)
		{
			m_lastMsg.message=msg.message;
			m_lastMsg.wParam=msg.wParam;
			m_lastMsg.lParam=msg.lParam;
			bRouteLastMessage=TRUE;
		}
		else if(m_nState==0)
		{
			bRouteLastMessage=FALSE;
		}

	}

	if(m_nState!=4)
		ShowWindow(SW_HIDE);

	BOOL bResult=m_nState==1 ? TRUE : FALSE;

	if(m_nState==3)
	{
		bResult=OnCustomButton();
	}

	// give back what took
	if(hFocusWnd)
	{
		::SetFocus(hFocusWnd);
	}
	if(::GetCapture()==m_hWnd)
	{
		::ReleaseCapture();
		if(hOldCaptureOwner!=NULL)
		{
			::SetCapture(hOldCaptureOwner);
		}
	}

	m_bMouseButtonPressed=FALSE;

	if(bRouteLastMessage)
	{
		POINTS points=MAKEPOINTS(m_lastMsg.lParam);	
		CPoint ptScreen(points.x,points.y);
		ClientToScreen(&ptScreen);
		CWnd* pWnd=WindowFromPoint(ptScreen);
		ASSERT(pWnd);

		int nHitTest=(int)pWnd->SendMessage(WM_NCHITTEST,0,MAKELONG(ptScreen.x,ptScreen.y));	
		if (nHitTest == HTCLIENT) 
		{
			pWnd->ScreenToClient(&ptScreen);		
			m_lastMsg.message=WM_LBUTTONDOWN;		
		} 
		else 
		{	
			m_lastMsg.message=WM_NCLBUTTONDOWN;		
		}
		m_lastMsg.wParam=nHitTest;
		m_lastMsg.lParam=MAKELONG(ptScreen.x,ptScreen.y);

		pWnd->PostMessage(m_lastMsg.message,m_lastMsg.wParam,m_lastMsg.lParam);

	}

	if(m_nState==4)
	{
		ConvertToTearOff();
	}

	return bResult;
}


////////////////////////////////////
// Attributes

BOOL COXPopupBarCtrl::SetTextFont(CFont* pFont)
{
	ASSERT_VALID(pFont);
	if((HFONT)m_font!=NULL)
	{
		m_font.DeleteObject();
	}

	LOGFONT lf;
	if(pFont->GetLogFont(&lf))
	{
		return m_font.CreateFontIndirect(&lf);
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// COXPopupBarCtrl message handlers

// handle it to pass a mouse message to a tool tip control for processing 
BOOL COXPopupBarCtrl::PreTranslateMessage(MSG* pMsg)
{
	if(::IsWindow(m_ctlToolTip.GetSafeHwnd()))
	{
		m_ctlToolTip.Activate(TRUE); 
		m_ctlToolTip.RelayEvent(pMsg);
	}
	return CWnd::PreTranslateMessage(pMsg);
}


void COXPopupBarCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	OnMouseButtonDown(point);

	CWnd::OnLButtonDown(nFlags, point);
}


void COXPopupBarCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	OnMouseButtonUp(point);

	CWnd::OnLButtonUp(nFlags, point);
}


void COXPopupBarCtrl::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	OnMouseButtonDown(point);

	CWnd::OnRButtonDown(nFlags, point);
}


void COXPopupBarCtrl::OnRButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	OnMouseButtonUp(point);

	CWnd::OnRButtonUp(nFlags, point);
}


void COXPopupBarCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default

	if(IsTearOff() && m_bClickedInTearoffBar)
	{
		ASSERT(!IsFloating());
		if(!m_rectTearoffBar.PtInRect(point))
		{
			m_bClickedInTearoffBar=FALSE;
			PostMessage(m_nTearoffMessage);
			return;
		}
	}

	if(IsFloating())
	{
		m_bCheckMousePos=TRUE;
	}

	if(m_ptLastMousePos!=point)
	{
		m_ptLastMousePos=point;
		OnChangeCurrentIndex(GetIndexFromPoint(point));
	}

	CWnd::OnMouseMove(nFlags, point);
}


void COXPopupBarCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	OnSysKeyDown(nChar,nRepCnt,nFlags);
}


void COXPopupBarCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	UINT nCols=GetNumColumns();

	if(IsFloating() && (nChar==VK_DOWN || nChar==VK_UP || nChar==VK_RIGHT || 
		nChar==VK_TAB || nChar==VK_RETURN || nChar==VK_SPACE))
	{
		m_bCheckMousePos=FALSE;
	}

	if(nChar==VK_DOWN) 
	{
		if(m_nCurrentIndex==ID_POPUPBAR_INDEX_NONE || 
			m_nCurrentIndex==GetLastButtonIndex() || 
			((m_nCurrentIndex+nCols)>GetLastButtonIndex() && 
			GetLastButtonIndex()==m_nButtons))
		{
			OnChangeCurrentIndex(GetFirstButtonIndex());
		}
		else if(m_nCurrentIndex==0)
		{
			OnChangeCurrentIndex(m_nCurrentIndex+1);
		}
		else if(m_nCurrentIndex+nCols>=GetLastButtonIndex() && 
			GetLastButtonIndex()!=m_nButtons)
		{
			OnChangeCurrentIndex(GetLastButtonIndex());
		}
		else
		{
			OnChangeCurrentIndex(m_nCurrentIndex+nCols);
		}
	}

	if(nChar==VK_UP) 
	{
		if(m_nCurrentIndex==ID_POPUPBAR_INDEX_NONE || 
			m_nCurrentIndex==GetFirstButtonIndex() || 
			(m_nCurrentIndex<(GetFirstButtonIndex()+nCols) && 
			GetFirstButtonIndex()!=0))
		{
			OnChangeCurrentIndex(GetLastButtonIndex());
		}
		else if(m_nCurrentIndex==m_nButtons+1)
		{
			OnChangeCurrentIndex(m_nCurrentIndex-1);
		}
		else if(m_nCurrentIndex<=GetFirstButtonIndex()+nCols && 
			GetFirstButtonIndex()==0)
		{
			OnChangeCurrentIndex(GetFirstButtonIndex());
		}
		else
		{
			OnChangeCurrentIndex(m_nCurrentIndex-nCols);
		}
	}

	if(nChar==VK_RIGHT) 
	{
		if(m_nCurrentIndex==ID_POPUPBAR_INDEX_NONE || 
			m_nCurrentIndex==GetLastButtonIndex())
		{
			OnChangeCurrentIndex(GetFirstButtonIndex());
		}
		else
		{
			OnChangeCurrentIndex(m_nCurrentIndex+1);
		}
	}

	if(nChar==VK_LEFT) 
	{
		if(m_nCurrentIndex==ID_POPUPBAR_INDEX_NONE || 
			m_nCurrentIndex==GetFirstButtonIndex())
		{
			OnChangeCurrentIndex(GetLastButtonIndex());
		}
		else
		{
			OnChangeCurrentIndex(m_nCurrentIndex-1);
		}
	}

	if(nChar==VK_TAB) 
	{
		if(GetKeyState(VK_SHIFT) & 0x8000)
		{

			if(m_nCurrentIndex==ID_POPUPBAR_INDEX_NONE || 
				GetFirstButtonIndex()==m_nCurrentIndex || 
				(m_nCurrentIndex>0 && m_nCurrentIndex<=m_nButtons && 
				GetFirstButtonIndex()!=0))
			{
				if(GetLastButtonIndex()==m_nButtons+1)
				{
					OnChangeCurrentIndex(GetLastButtonIndex());
				}
				else
				{
					OnChangeCurrentIndex(1);
				}
			}
			else if(m_nCurrentIndex==m_nButtons+1)
			{
				OnChangeCurrentIndex(1);
			}
			else
			{
				OnChangeCurrentIndex(GetFirstButtonIndex());
			}
		}
		else
		{
			if(m_nCurrentIndex==ID_POPUPBAR_INDEX_NONE || 
				GetLastButtonIndex()==m_nCurrentIndex || 
				(m_nCurrentIndex>0 && m_nCurrentIndex<=m_nButtons && 
				GetLastButtonIndex()==m_nButtons))
			{
				OnChangeCurrentIndex(GetFirstButtonIndex());
			}
			else if(m_nCurrentIndex==0)
			{
				OnChangeCurrentIndex(m_nCurrentIndex+1);
			}
			else
			{
				OnChangeCurrentIndex(GetLastButtonIndex());
			}
		}
	}

	if(nChar==VK_RETURN)
	{
		if(m_nCurrentIndex!=ID_POPUPBAR_INDEX_NONE)
		{
			if(m_nCurrentIndex==m_nButtons+1)
			{
				// CustomButton is about to be activated
				//
				SendNotification(m_nCustomButtonPicked);
			}
			else
			{
				OnChangeSelectedIndex(m_nCurrentIndex);
				if(m_nSelectedIndex==0)
				{
					// DefaultButton is about to be activated
					//
					ASSERT(IsDefaultButton());
					m_dwSelected=m_dwDefault;
				}
				else
				{
					ASSERT((int)m_nButtons<=m_arrData.GetSize());
					m_dwSelected=m_arrData[m_nSelectedIndex-1];
				}
				SendNotification(m_nOkMessage);
			}
		}
		else
		{
			if(!IsFloating())
				PostMessage(m_nCancelMessage);
		}
	}

	if(nChar==VK_SPACE)
	{
		if(!m_bMouseButtonPressed && m_nCurrentIndex!=ID_POPUPBAR_INDEX_NONE)
		{
			m_bMouseButtonPressed=TRUE;
			RedrawElement(m_nCurrentIndex);
		}
	}

	CWnd::OnSysKeyDown(nChar, nRepCnt, nFlags);
}


void COXPopupBarCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	OnSysKeyUp(nChar,nRepCnt,nFlags);
}


void COXPopupBarCtrl::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	if(nChar==VK_ESCAPE) 
	{
		SendNotification(m_nCancelMessage);
	}

	if(nChar==VK_SPACE)
	{
		m_bMouseButtonPressed=FALSE;

		if(m_nCurrentIndex!=ID_POPUPBAR_INDEX_NONE)
		{
			if(m_nCurrentIndex==m_nButtons+1)
			{
				// CustomButton is about to be activated
				//
				SendNotification(m_nCustomButtonPicked);
			}
			else
			{
				OnChangeSelectedIndex(m_nCurrentIndex);
				if(m_nSelectedIndex==0)
				{
					// DefaultButton is about to be activated
					//
					ASSERT(IsDefaultButton());
					m_dwSelected=m_dwDefault;
				}
				else
				{
					ASSERT((int)m_nButtons<=m_arrData.GetSize());
					m_dwSelected=m_arrData[m_nSelectedIndex-1];
				}
				SendNotification(m_nOkMessage);
			}
		}
		else
		{
			if(!IsFloating())
				PostMessage(m_nCancelMessage);
		}
	}

	CWnd::OnSysKeyUp(nChar, nRepCnt, nFlags);
}


void COXPopupBarCtrl::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	// TODO: Add your message handler code here

	// realize custom palette if any set
	CPalette* pOldPalette=NULL;
	if((HPALETTE)m_CustomPalette!=NULL)
	{
		pOldPalette=dc.SelectPalette(&m_CustomPalette,FALSE);
		dc.RealizePalette();
	}

	// Draw the background
	DrawBackground(&dc);

	// Draw common buttons
	for(int nIndex=0; nIndex<(int)m_nButtons; nIndex++)
	{
		DrawButton(&dc, nIndex);
	}

	// Draw Tear off bar
	if(IsTearOff())
	{
		DrawTearoffBar(&dc);
	}

	// Draw default button
	if(IsDefaultButton())
	{
		DrawDefaultButton(&dc);
	}

	// Draw custom button
	if(IsCustomButton())
	{
		DrawCustomButton(&dc);
	}

	if(pOldPalette!=NULL)
	{
		dc.SelectPalette(pOldPalette,FALSE);
	}

	// Do not call CWnd::OnPaint() for painting messages
}


BOOL COXPopupBarCtrl::OnQueryNewPalette() 
{
	Invalidate();    
	return CWnd::OnQueryNewPalette();
}


void COXPopupBarCtrl::OnPaletteChanged(CWnd* pFocusWnd)
{
	CWnd::OnPaletteChanged(pFocusWnd);

	if(pFocusWnd!=this)
	{
		Invalidate();
	}
}


void COXPopupBarCtrl::OnClose() 
{
	if(IsFloating())
		ShowWindow(SW_HIDE);
	else
		CWnd::OnClose();
}


void COXPopupBarCtrl::OnDestroy()
{
	if(m_nCheckMouseTimer!=0)
		KillTimer(m_nCheckMouseTimer);

	CWnd::OnDestroy();
}	


void COXPopupBarCtrl::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
	UNREFERENCED_PARAMETER(pWndOther);
	if(nState!=WA_INACTIVE && !bMinimized)
	{
		SetFocus();
	}
}


// v9.3 - update 03 - 64-bit - using OXTPARAM here - see UTB64Bit.h
void COXPopupBarCtrl::OnTimer(OXTPARAM nIDEvent)
{
	if(nIDEvent==(UINT)m_nCheckMouseTimer)
	{
		if(m_bCheckMousePos && IsFloating())
		{
			CPoint point;
			::GetCursorPos(&point);
			ScreenToClient(&point);
			int nIndex=GetIndexFromPoint(point);
			if(nIndex==ID_POPUPBAR_INDEX_NONE)
				OnChangeCurrentIndex(nIndex);
		}
		return;
	}

	CWnd::OnTimer(nIDEvent);
}


BOOL COXPopupBarCtrl::OnEraseBkgnd(CDC* pDC)
{
	UNREFERENCED_PARAMETER(pDC);
	return TRUE;
}

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

void COXPopupBarCtrl::OnMouseButtonDown(CPoint point) 
{
	CRect rect;
	GetClientRect(rect);
	ClientToScreen(rect);

	CPoint ptScreen=point;
	ClientToScreen(&ptScreen);
	if(rect.PtInRect(ptScreen))
	{
		m_bMouseButtonPressed=TRUE;
		RedrawElement(GetIndexFromPoint(point));
	}
	else
	{
		// mouse is out of control area
		if(!IsFloating())
			PostMessage(m_nCancelMessage);
	}

	// Check if the tear off bar was clicked
	ScreenToClient(&ptScreen);
	if(IsTearOff() && m_rectTearoffBar.PtInRect(ptScreen))
	{
		m_bClickedInTearoffBar=TRUE;
	}
}


void COXPopupBarCtrl::OnMouseButtonUp(CPoint point) 
{
	UNREFERENCED_PARAMETER(point);

	m_bClickedInTearoffBar=FALSE;

	if(m_bMouseButtonPressed && m_nCurrentIndex!=ID_POPUPBAR_INDEX_NONE)
	{
		if(m_nCurrentIndex==m_nButtons+1)
		{
			// mouse is over CustomButton
			//
			SendNotification(m_nCustomButtonPicked);
		}
		else
		{
			OnChangeSelectedIndex(m_nCurrentIndex);
			if(m_nSelectedIndex==0)
			{
				// mouse is over DefaultButton
				//
				ASSERT(IsDefaultButton());
				m_dwSelected=m_dwDefault;
			}
			else
			{
				ASSERT((int)m_nButtons<=m_arrData.GetSize());
				m_dwSelected=m_arrData[m_nSelectedIndex-1];
			}
			SendNotification(m_nOkMessage);
		}
	}
	else
	{
		if(m_bMouseButtonPressed && !IsFloating())
		{
			PostMessage(m_nCancelMessage);
		}
	}
	m_bMouseButtonPressed=FALSE;
}


BOOL COXPopupBarCtrl::OnCustomButton()
{
	ASSERT(IsCustomButton());

	return FALSE;
}


void COXPopupBarCtrl::OnChangeCurrentIndex(UINT newCurrentIndex)
{
	if(newCurrentIndex!=m_nCurrentIndex)
	{
		UINT oldCurrentIndex=m_nCurrentIndex;
		m_nCurrentIndex=newCurrentIndex;
		RedrawElement(oldCurrentIndex);
		RedrawElement(m_nCurrentIndex);
	}
}


void COXPopupBarCtrl::OnChangeSelectedIndex(UINT newSelectedIndex)
{
	if(newSelectedIndex!=m_nSelectedIndex)
	{
		UINT oldSelectedIndex=m_nSelectedIndex;
		m_nSelectedIndex=newSelectedIndex;
		RedrawElement(oldSelectedIndex);
		RedrawElement(m_nSelectedIndex);
	}
}


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

// sets array of data of common buttons
BOOL COXPopupBarCtrl::SetButtonDataTable(DWORD* arrData, UINT nElements)
{
	if(IsFloating())
		SendNotification(m_nCancelMessage);

	if(nElements>0)
	{
		ASSERT(arrData);
	}

	m_arrData.RemoveAll();
	for(int nIndex=0; nIndex<(int)nElements; nIndex++)
	{
		m_arrData.Add(arrData[nIndex]);
	}
	return TRUE;
}


// set array of data of common buttons
BOOL COXPopupBarCtrl::SetButtonDataTable(ButtonDataTable& arrData)
{
	if(IsFloating())
		SendNotification(m_nCancelMessage);

	UINT_PTR nElements=arrData.GetSize();

	m_arrData.RemoveAll();
	for(int nIndex=0; nIndex<(int)nElements; nIndex++)
	{
		m_arrData.Add(arrData[nIndex]);
	}
	return TRUE;
}


// sets array of tooltips associated with common buttons
BOOL COXPopupBarCtrl::SetToolTipTextTable(ButtonToolTipTable* arrToolTipText, 
										  UINT nElements)
{
	if(nElements>0)
	{
		ASSERT(arrToolTipText);
	}

	m_arrToolTipText.RemoveAll();
	CString sToolTipText;
	for(int nIndex=0; nIndex<(int)nElements; nIndex++)
	{
		sToolTipText=arrToolTipText[nIndex].pszToolTipText;
		m_arrToolTipText.SetAt(arrToolTipText[nIndex].dwData,sToolTipText);
	}
	return TRUE;
}


// sets array of tooltips associated with common buttons
BOOL COXPopupBarCtrl::SetToolTipTextIDTable(ButtonToolTipIDTable* arrToolTipText, 
											UINT nElements)
{
	if(nElements>0)
	{
		ASSERT(arrToolTipText);
	}

	m_arrToolTipText.RemoveAll();
	CString sToolTipText;
	for(int nIndex=0; nIndex<(int)nElements; nIndex++)
	{
		sToolTipText.LoadString(arrToolTipText[nIndex].nID);
		m_arrToolTipText.SetAt(arrToolTipText[nIndex].dwData, sToolTipText);
	}
	return TRUE;
}


// sets array of tooltips associated with common buttons
BOOL COXPopupBarCtrl::SetToolTipTextTable(ButtonToolTipMap& arrToolTipText)
{
	UINT_PTR nElements=arrToolTipText.GetCount();

	m_arrToolTipText.RemoveAll();

	POSITION pos=arrToolTipText.GetStartPosition();
	DWORD dwData;
	CString sToolTipText;
	BOOL bResult=TRUE;
	for(int nIndex=0; nIndex<(int)nElements; nIndex++)
	{
		if(pos==NULL)
		{
			bResult=FALSE;
			m_arrToolTipText.RemoveAll();
			break;
		}
		arrToolTipText.GetNextAssoc(pos,dwData,sToolTipText);
		m_arrToolTipText.SetAt(dwData,sToolTipText);
	}
	return bResult;
}


BOOL COXPopupBarCtrl::PopulateToolTip()
{
	ASSERT(::IsWindow(m_ctlToolTip.GetSafeHwnd()));

	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);

	ASSERT(m_arrButtonRect.GetSize()==(int)m_nButtons);
	ASSERT((int)m_nButtons<=m_arrData.GetSize());

	// delete all previously created tools
	int nTools=m_ctlToolTip.GetToolCount();
	int nIndex=0;
	for(nIndex=0; nIndex<nTools; nIndex++)
	{
		m_ctlToolTip.DelTool(this,nIndex+1);
	}

	CString sToolTipText;
	// Add a tool for each Color Button
	for(nIndex=0; nIndex<(int)m_nButtons; nIndex++)
	{
		if(m_arrToolTipText.Lookup(m_arrData[nIndex],sToolTipText))
		{
			m_ctlToolTip.AddTool(this,sToolTipText,m_arrButtonRect[nIndex],nIndex+1);
		}
	}

	if(IsDefaultButton())
	{
		m_ctlToolTip.AddTool(this,m_sDefaultButtonText,m_rectDefaultButton,nIndex+1);
	}
	if(IsCustomButton())
	{
		m_ctlToolTip.AddTool(this,m_sCustomButtonText,m_rectCustomButton,nIndex+2);
	}
	if(IsTearOff())
	{
		m_ctlToolTip.AddTool(this,m_sTearoffText,m_rectTearoffBar,nIndex+3);
	}

	return TRUE;
}


BOOL COXPopupBarCtrl::ConvertToTearOff()
{
	ASSERT(::IsWindow(m_hWnd));
	ASSERT(IsTearOff());

	// Remember the location of the window
	CRect rect;
	GetWindowRect(rect);

	// Ensure that the mouse is still over this window, otherwise the ButtonUp
	// message will not get to this window, and when the user clicks up the 
	// mouse will still be dragging this window around. Trust me - it looks 
	// pretty silly.
	DWORD dwMessagePos=::GetMessagePos();
	CPoint ptMouse(GET_X_LPARAM(dwMessagePos),GET_Y_LPARAM(dwMessagePos));
	if (!rect.PtInRect(ptMouse))
	{
		ShowWindow(SW_HIDE);
		return FALSE;
	}

	// track the window
	//

	// initialize tracking state
	m_rectLastTrackBar.SetRectEmpty();
	m_sizeLastTrackBar=CSize(0,0);
	m_bDitherLast=FALSE;

	m_rectTrackBar=rect;

	// lock window update while tracking
	ASSERT(m_pTrackingDC==NULL);
	CWnd* pWnd=CWnd::GetDesktopWindow();
#ifndef _MAC
	if(pWnd->LockWindowUpdate()) 
		m_pTrackingDC=pWnd->GetDCEx(NULL,DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
	else
#endif
		m_pTrackingDC=pWnd->GetDCEx(NULL,DCX_WINDOW|DCX_CACHE);
	ASSERT(m_pTrackingDC!=NULL); 

	m_ptLastTrack=ptMouse;
	MoveTrackBar(ptMouse);
	if(!TrackBar())
	{
		ShowWindow(SW_HIDE);
		return FALSE;
	}

	rect=m_rectTrackBar;
	//
	///////////////////////////////////////

	// Destroy this window so we can create a new one 
	DestroyWindow();
	m_ctlToolTip.DestroyWindow();

	m_bFloating=TRUE;

	// Get the background brush
	WNDCLASS wc;
	HBRUSH hBrush = NULL;
	if(GetClassInfo(AfxGetInstanceHandle(),POPUPBAR_CLASSNAME,&wc))
	{
		hBrush=wc.hbrBackground;
	}


	if(!Create(m_pParentWnd,m_nPopupBarStyle,hBrush))
	{
		return FALSE;
	}

	// Place everything in the right position
	CalcWindowSize();

	if(!PopulateToolTip())
	{
		return FALSE;
	}

	m_rect.OffsetRect(-m_rect.TopLeft());
	m_rect.OffsetRect(rect.TopLeft());

	// it's show time!
	SetWindowPos(NULL,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height(),
		SWP_NOZORDER|SWP_NOACTIVATE);

	RedrawWindow();

	return TRUE;
}


void COXPopupBarCtrl::MoveTrackBar(CPoint pt)
{
	CPoint ptOffset=pt-m_ptLastTrack;
	// offset drag rect to new position
	m_rectTrackBar.OffsetRect(ptOffset);
	m_ptLastTrack = pt;
	// update feedback
	DrawTrackingRect(FALSE);
}


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

	// set capture
	SetCapture();
	ASSERT(this==CWnd::GetCapture());

	BOOL bCancelled=FALSE;
	// get messages until capture lost or cancelled/accepted
	while(CWnd::GetCapture()==this && !bCancelled)
	{
		MSG msg;
#ifndef _MAC
		if(!::GetMessage(&msg,NULL,0,0))
#else
		// don't allow yielding while tracking since we don't have LockWindowUpdate
		if(!PeekMessage(&msg,NULL,0,0,PM_REMOVE|PM_NOYIELD))
			continue;
		if(msg.message==WM_QUIT)
#endif
		{
			AfxPostQuitMessage(PtrToInt(msg.wParam));
			break;
		}

		switch(msg.message)
		{
		case WM_LBUTTONUP:		// drag finished 
			EndTrackBar();
			return TRUE;

		case WM_MOUSEMOVE:
			MoveTrackBar(msg.pt);
			break;

		case WM_KEYDOWN:
			if(msg.wParam==VK_ESCAPE)
				bCancelled=TRUE;
			break;

		case WM_RBUTTONDOWN:
			bCancelled=TRUE;
			break;

			// just dispatch rest of the messages
		default:
			DispatchMessage(&msg);
			break;
		}
	}

	CancelTrackBar();

	return FALSE;
}


void COXPopupBarCtrl::EndTrackBar()
{
	CancelTrackBar();
}


void COXPopupBarCtrl::CancelTrackBar()
{
	DrawTrackingRect(TRUE);    // gets rid of focus rect
	ReleaseCapture();

	CWnd* pWnd = CWnd::GetDesktopWindow();
#ifndef _MAC
	pWnd->UnlockWindowUpdate();
#endif
	if(m_pTrackingDC!=NULL)
	{
		pWnd->ReleaseDC(m_pTrackingDC);
		m_pTrackingDC=NULL;
	}
}


const int CX_TRACKBORDER=3;
const int CY_TRACKBORDER=3;

void COXPopupBarCtrl::DrawTrackingRect(BOOL bRemoveRect)
{
	ASSERT(m_pTrackingDC!=NULL);

	// default to thin frame
	CSize size(CX_TRACKBORDER, CY_TRACKBORDER);

	// determine new rect and size
	CRect rect;
	CBrush* pWhiteBrush=CBrush::FromHandle((HBRUSH)::GetStockObject(WHITE_BRUSH));
	CBrush* pDitherBrush=CDC::GetHalftoneBrush();
	CBrush* pBrush=pWhiteBrush;

	rect=m_rectTrackBar;
	if(bRemoveRect)
	{
		size.cx=0;
		size.cy=0;
	}

	// draw it and remember last size
	m_pTrackingDC->DrawDragRect(&rect,size,&m_rectLastTrackBar,
		m_sizeLastTrackBar,pBrush,(m_bDitherLast ? pDitherBrush : pWhiteBrush));
	m_rectLastTrackBar=rect;
	m_sizeLastTrackBar = size;
	m_bDitherLast=(pBrush==pDitherBrush);
}


// if the menu is floating then the "Pick" message loop will have been exited.
// We need to let the parent know what is happening. If the menu is not floating
// then we just send messages back to this window so the "Pick" loop knows
// what is going on.
void COXPopupBarCtrl::SendNotification(UINT nMessage)
{
	if(IsFloating())
	{
		BOOL bResult=TRUE;
		if(nMessage==m_nCustomButtonPicked)
			bResult=OnCustomButton();
		else if(nMessage==m_nCancelMessage)
		{
			m_bCheckMousePos=FALSE;
			ShowWindow(SW_HIDE);
			bResult=FALSE;
		}

		if(bResult)
		{
			m_pParentWnd->PostMessage(OXPBN_SELCHANGED,NULL,(LPARAM)GetSafeHwnd());
		}
	}
	else
	{
		PostMessage(nMessage);
	}
}

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

// calculate size of window and location of default and 
// custom buttons within the control
BOOL COXPopupBarCtrl::CalcWindowSize()
{
	ASSERT(::IsWindow(m_hWnd));
	ASSERT(m_nRows<=m_nButtons);

	m_rect.SetRectEmpty();
	m_rectTearoffBar.SetRectEmpty();
	m_rectDefaultButton.SetRectEmpty();
	m_rectCustomButton.SetRectEmpty();
	m_arrButtonRect.RemoveAll();

	// number of columns
	int nCols=GetNumColumns();
	// sum the size of window edges, all common buttons and margins
	m_rect.right=2*::GetSystemMetrics(SM_CXDLGFRAME)+
		m_rectEdgeMargin.left+m_rectEdgeMargin.right+
		nCols*(m_sizeButton.cx+m_rectButtonMargin.left+
		m_rectButtonMargin.right);
	// sum the size of window edges, all common buttons and margins
	m_rect.bottom=2*::GetSystemMetrics(SM_CYDLGFRAME)+
		m_rectEdgeMargin.top+m_rectEdgeMargin.bottom+
		m_nRows*(m_sizeButton.cy+m_rectButtonMargin.top+
		m_rectButtonMargin.bottom);

	if (IsFloating())
		m_rect.bottom +=::GetSystemMetrics(SM_CYSMCAPTION);

	if((HFONT)m_font==NULL)
	{
		// Create the font
		//////////////////////////////////////////////////////////////////
		// v9.3 update 02 - the NONCLIENTMETRICS struct is an int larger 
		// on Vista vs XP (iPaddedBorderWidth added). Code compiled for 
		// WINVER 0x0600 will fail the call to SystemParametersInfo on XP.
		// Code compiled for XP should still run on Vista.

		int ncmSize = sizeof( NONCLIENTMETRICS );

#		if WINVER >= 0x0600
		// compiled for Vista - check for OS version
		OSVERSIONINFO vi={ sizeof(OSVERSIONINFO) };
		ASSERT(GetVersionEx(&vi));
		if(vi.dwMajorVersion < 6) {
			// running on lesser version - adjust size of NONCLIENTMETRICS struct
			ncmSize -= sizeof( int );
		}
#		endif
		NONCLIENTMETRICS ncm={ ncmSize };
		VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncmSize, &ncm, 0));
		// end NONCLIENTMETRICS mods v9.3 update 02
		/////////////////////////////////////////////////
		VERIFY(m_font.CreateFontIndirect(&ncm.lfMessageFont));
	}


	if(IsTearOff())
	{
		m_rectTearoffBar.left  = m_rectEdgeMargin.left + m_rectTearoffBarMargin.left;
		m_rectTearoffBar.right = m_rectTearoffBar.left + 
			m_rect.right - (2*::GetSystemMetrics(SM_CXDLGFRAME) +
			m_rectEdgeMargin.left + m_rectTearoffBarMargin.left +
			m_rectEdgeMargin.right + m_rectTearoffBarMargin.right);

		m_rectTearoffBar.top    = m_rectEdgeMargin.top + m_rectTearoffBarMargin.top;
		m_rectTearoffBar.bottom = m_rectTearoffBar.top + 6;

		m_rect.bottom += m_rectTearoffBar.Height() +
			m_rectTearoffBarMargin.top + m_rectTearoffBarMargin.bottom;
	}


	CClientDC dc(this);
	if(IsDefaultButton())
	{
		m_rectDefaultButton.left=m_rectEdgeMargin.left+m_rectDefaultButtonMargin.left;
		m_rectDefaultButton.right=m_rectDefaultButton.left+
			m_rect.right-(2*::GetSystemMetrics(SM_CXDLGFRAME)+
			m_rectEdgeMargin.left+m_rectDefaultButtonMargin.left+
			m_rectEdgeMargin.right+m_rectDefaultButtonMargin.right);
		m_rectDefaultButton.top=m_rectEdgeMargin.top+m_rectDefaultButtonMargin.top;

		// Get the size of the custom text
		CFont* pOldFont=(CFont*)dc.SelectObject(&m_font);
		CRect rect(0, 0, 0, 0);
		dc.DrawText(m_sDefaultButtonText,&rect,DT_CALCRECT);
		rect.InflateRect(4,4);
		dc.SelectObject(pOldFont);

		m_rectDefaultButton.bottom=m_rectDefaultButton.top+rect.Height();

		if(IsTearOff())
		{
			int nShiftDown=m_rectTearoffBar.Height()+
				m_rectTearoffBarMargin.top+m_rectTearoffBarMargin.bottom;
			m_rectDefaultButton.OffsetRect(0, nShiftDown);
		}

		m_rect.bottom+=m_rectDefaultButton.Height()+
			m_rectDefaultButtonMargin.top+m_rectDefaultButtonMargin.bottom;
	}

	if(IsCustomButton())
	{
		m_rectCustomButton.left=m_rectEdgeMargin.left+m_rectCustomButtonMargin.left;
		m_rectCustomButton.right=m_rectCustomButton.left+
			m_rect.right-(2*::GetSystemMetrics(SM_CXDLGFRAME)+
			m_rectEdgeMargin.left+m_rectCustomButtonMargin.left+
			m_rectEdgeMargin.right+m_rectCustomButtonMargin.right);
		m_rectCustomButton.top=m_rect.bottom-(2*::GetSystemMetrics(SM_CYDLGFRAME)+
			m_rectEdgeMargin.top)+m_rectCustomButtonMargin.top;

		if(IsFloating())
			m_rectCustomButton.top-=::GetSystemMetrics(SM_CYSMCAPTION);

		// Get the size of the custom text
		CFont* pOldFont=(CFont*)dc.SelectObject(&m_font);
		CRect rect(0, 0, 0, 0);
		dc.DrawText(m_sCustomButtonText,&rect,DT_CALCRECT);
		rect.InflateRect(4,4);
		dc.SelectObject(pOldFont);

		m_rectCustomButton.bottom=m_rectCustomButton.top+rect.Height();

		m_rect.bottom+=m_rectCustomButton.Height()+
			m_rectCustomButtonMargin.top+m_rectCustomButtonMargin.bottom;
	}



	CSize sizeIniShift;
	sizeIniShift.cx=m_rectEdgeMargin.left+m_rectButtonMargin.left;
	sizeIniShift.cy=m_rectEdgeMargin.top+m_rectButtonMargin.top;
	if(IsDefaultButton())
	{
		sizeIniShift.cy+=m_rectDefaultButtonMargin.top+
			m_rectDefaultButtonMargin.bottom+m_rectDefaultButton.Height();
	}
	if(IsTearOff())
	{
		sizeIniShift.cy+=m_rectTearoffBarMargin.top+
			m_rectTearoffBarMargin.bottom+m_rectTearoffBar.Height();
	}

	CRect rect;
	for(int i=0; i<(int)m_nRows; i++)
	{
		for(int j=0; j<nCols; j++)
		{
			if(i*nCols+j<(int)m_nButtons)
			{
				rect.left=sizeIniShift.cx+(m_rectButtonMargin.left+
					m_sizeButton.cx+m_rectButtonMargin.right)*j;
				rect.right=rect.left+m_sizeButton.cx;
				rect.top=sizeIniShift.cy+(m_rectButtonMargin.top+
					m_sizeButton.cy+m_rectButtonMargin.bottom)*i;
				rect.bottom=rect.top+m_sizeButton.cy;

				m_arrButtonRect.Add(rect);
			}
		}
	}

	return TRUE;
}


// user for sure want to control displaying of the control 
BOOL COXPopupBarCtrl::AdjustWindowPosition(UINT nAlignment, CRect* pParentRect, 
										   CSize sizeMargin)
{
	ASSERT(::IsWindow(m_hWnd));
	ASSERT(nAlignment==ID_POPUPBAR_ALIGNLEFT || 
		nAlignment==ID_POPUPBAR_ALIGNRIGHT || 
		nAlignment==ID_POPUPBAR_ALIGNTOP || 
		nAlignment==ID_POPUPBAR_ALIGNBOTTOM ||
		nAlignment==ID_POPUPBAR_ALIGNLEFTBOTTOM || 
		nAlignment==ID_POPUPBAR_ALIGNRIGHTBOTTOM || 
		nAlignment==ID_POPUPBAR_ALIGNTOPRIGHT || 
		nAlignment==ID_POPUPBAR_ALIGNBOTTOMRIGHT);

	BOOL bTopMostParent=(AfxGetMainWnd()!=NULL &&
		(AfxGetMainWnd()->GetExStyle() & WS_EX_TOPMOST)==WS_EX_TOPMOST);
	CRect rect;
	// check if item rectangle fits into the screen
#if(defined(GetMonitorInfo) && defined(MonitorFromPoint))
	// if we use Win 98/NT5 we have to take into account multiple monitors
	OSVERSIONINFO osvi={ sizeof(OSVERSIONINFO) };
	VERIFY(::GetVersionEx(&osvi)!=0);
	if(osvi.dwMajorVersion>4 || 
		(osvi.dwMajorVersion==4 && osvi.dwMinorVersion>0))
	{
		CPoint pt;
		::GetCursorPos(&pt);
		HMONITOR hMonitor=::MonitorFromPoint(pt,MONITOR_DEFAULTTONEAREST);
		ASSERT(hMonitor!=NULL);
		MONITORINFO monitorInfo={ sizeof(MONITORINFO) };
		VERIFY(::GetMonitorInfo(hMonitor,&monitorInfo));
		if(bTopMostParent)
		{
			rect=monitorInfo.rcMonitor;
		}
		else
		{
			rect=monitorInfo.rcWork;
		}
	}
	else
	{
#endif
		if(bTopMostParent)
		{
			CWnd::GetDesktopWindow()->GetWindowRect(rect);
		}
		else
		{
			::SystemParametersInfo(SPI_GETWORKAREA,NULL,&rect,NULL);
		}
#if(defined(GetMonitorInfo) && defined(MonitorFromPoint))
	}
#endif

	int nScreenWidth=rect.Width();
	int nScreenHeight=rect.Height();

	if(m_rect.Width()>nScreenWidth || m_rect.Height()>nScreenHeight)
	{
		return FALSE;
	}

	CSize sizeOffset;

	CRect rectParent;
	if(pParentRect==NULL)
	{
		m_pParentWnd->GetWindowRect(rectParent);
	}
	else
	{
		rectParent=*pParentRect;
	}

	switch(nAlignment)
	{
	case ID_POPUPBAR_ALIGNLEFT:
		{
			sizeOffset.cx=rectParent.left-sizeMargin.cx-m_rect.Width();
			sizeOffset.cy=rectParent.top+sizeMargin.cy;
#if(WINVER < 0x0500)
			if(sizeOffset.cx<0)
			{
				sizeOffset.cx=rectParent.right+sizeMargin.cx;
				if(sizeOffset.cx+m_rect.Width()>nScreenWidth)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNLEFTBOTTOM:
		{
			sizeOffset.cx=rectParent.left-sizeMargin.cx-m_rect.Width();
			sizeOffset.cy=rectParent.bottom-sizeMargin.cy-m_rect.Height();
#if(WINVER < 0x0500)
			if(sizeOffset.cx<0)
			{
				sizeOffset.cx=rectParent.right+sizeMargin.cx;
				if(sizeOffset.cx+m_rect.Width()>nScreenWidth)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNRIGHT:
		{
			sizeOffset.cx=rectParent.right+sizeMargin.cx;
			sizeOffset.cy=rectParent.top+sizeMargin.cy;
#if(WINVER < 0x0500)
			if(sizeOffset.cx+m_rect.Width()>nScreenWidth)
			{
				sizeOffset.cx=rectParent.left-sizeMargin.cx-m_rect.Width();
				if(sizeOffset.cx<0)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNRIGHTBOTTOM:
		{
			sizeOffset.cx=rectParent.right+sizeMargin.cx;
			sizeOffset.cy=rectParent.bottom-sizeMargin.cy-m_rect.Height();
#if(WINVER < 0x0500)
			if(sizeOffset.cx+m_rect.Width()>nScreenWidth)
			{
				sizeOffset.cx=rectParent.left-sizeMargin.cx-m_rect.Width();
				if(sizeOffset.cx<0)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNTOP:
		{
			sizeOffset.cx=rectParent.left+sizeMargin.cx;
			sizeOffset.cy=rectParent.top-sizeMargin.cy-m_rect.Height();
#if(WINVER < 0x0500)
			if(sizeOffset.cy<0)
			{
				sizeOffset.cy=rectParent.bottom+sizeMargin.cy;
				if(sizeOffset.cy+m_rect.Height()>nScreenHeight)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNTOPRIGHT:
		{
			sizeOffset.cx=rectParent.right-sizeMargin.cx-m_rect.Width();
			sizeOffset.cy=rectParent.top-sizeMargin.cy-m_rect.Height();
#if(WINVER < 0x0500)
			if(sizeOffset.cy<0)
			{
				sizeOffset.cy=rectParent.bottom+sizeMargin.cy;
				if(sizeOffset.cy+m_rect.Height()>nScreenHeight)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNBOTTOM:
		{
			sizeOffset.cx=rectParent.left+sizeMargin.cx;
			sizeOffset.cy=rectParent.bottom+sizeMargin.cy;
#if(WINVER < 0x0500)
			if(sizeOffset.cy+m_rect.Height()>nScreenHeight)
			{
				sizeOffset.cy=rectParent.top-sizeMargin.cy-m_rect.Height();
				if(sizeOffset.cy<0)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	case ID_POPUPBAR_ALIGNBOTTOMRIGHT:
		{
			sizeOffset.cx=rectParent.right-sizeMargin.cx-m_rect.Width();
			sizeOffset.cy=rectParent.bottom+sizeMargin.cy;
#if(WINVER < 0x0500)
			if(sizeOffset.cy+m_rect.Height()>nScreenHeight)
			{
				sizeOffset.cy=rectParent.top-sizeMargin.cy-m_rect.Height();
				if(sizeOffset.cy<0)
				{
					return FALSE;
				}
			}
#endif
			m_rect.OffsetRect(sizeOffset);
			break;
		}
	}

	sizeOffset.cx=0;
	sizeOffset.cy=0;

#if(WINVER < 0x0500)

	if(m_rect.left<0)
	{
		sizeOffset.cx=-m_rect.left;
	}
	else if(m_rect.right>nScreenWidth)
	{
		sizeOffset.cx=nScreenWidth-m_rect.right;
	}
	if(m_rect.top<0)
	{
		sizeOffset.cy=-m_rect.top;
	}
	else if(m_rect.bottom>nScreenHeight)
	{
		sizeOffset.cy=nScreenHeight-m_rect.bottom;
	}
	m_rect.OffsetRect(sizeOffset);
#endif
	return TRUE;
}


// gets index of button base on color
UINT COXPopupBarCtrl::GetIndexFromData(DWORD dwData)
{
	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);
	UINT_PTR nElements=m_arrData.GetSize();
	ASSERT(m_nButtons<=nElements);

	if(dwData==ID_POPUPBAR_DATA_NONE)
	{
		return ID_POPUPBAR_INDEX_NONE;
	}
	if(IsDefaultButton() && m_dwDefault==dwData)
	{
		return 0;
	}
	for(int nIndex=0; nIndex<(int)m_nButtons; nIndex++)
	{
		if(m_arrData[nIndex]==dwData)
		{
			return nIndex+1;
		}
	}
	if(IsCustomButton())
	{
		return m_nButtons+1;
	}

	return ID_POPUPBAR_INDEX_NONE;
}


// gets index of button base on point
UINT COXPopupBarCtrl::GetIndexFromPoint(CPoint point)
{
	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);
	UINT_PTR nElements=m_arrButtonRect.GetSize();
	ASSERT(m_nButtons==nElements);

	if(IsDefaultButton() && m_rectDefaultButton.PtInRect(point))
	{
		return 0;
	}
	for(int nIndex=0; nIndex<(int)m_nButtons; nIndex++)
	{
		if(m_arrButtonRect[nIndex].PtInRect(point))
		{
			return nIndex+1;
		}
	}
	if(IsCustomButton() && m_rectCustomButton.PtInRect(point))
	{
		return m_nButtons+1;
	}

	return ID_POPUPBAR_INDEX_NONE;
}


// returns number of columns of common buttons in the control
UINT COXPopupBarCtrl::GetNumColumns() const
{
	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);
	ASSERT(m_nRows>0 && m_nRows<=m_nButtons);

	return m_nButtons/m_nRows+(m_nButtons%m_nRows>0 ? 1 : 0);
}


// returns index of the first button on the control
UINT COXPopupBarCtrl::GetFirstButtonIndex()
{
	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);
	ASSERT(m_nRows>0 && m_nRows<=m_nButtons);

	return IsDefaultButton() ? 0 : 1;
}

// returns index of the last button on the control
UINT COXPopupBarCtrl::GetLastButtonIndex()
{
	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);
	ASSERT(m_nRows>0 && m_nRows<=m_nButtons);

	return IsCustomButton() ? m_nButtons+1 : m_nButtons;
}


/////////////////////////////////////////////////////////////////
void COXPopupBarCtrl::RedrawElement(UINT nIndex)
{
	if(nIndex==0)
	{
		// default button
		ASSERT(IsDefaultButton());
		InvalidateRect(m_rectDefaultButton);
	} 
	else if(nIndex==m_nButtons+1)
	{
		// custom button
		ASSERT(IsCustomButton());
		InvalidateRect(m_rectCustomButton);
	} 
	else if(nIndex>=1 && nIndex<=m_nButtons)
	{
		// common buttons
		ASSERT((int)m_nButtons==m_arrButtonRect.GetSize());
		InvalidateRect(m_arrButtonRect[nIndex-1]);
	}
}


// draw color button
void COXPopupBarCtrl::DrawButton(CDC* pDC, UINT nIndex)
{
	ASSERT(m_nButtons>0 && m_nButtons<=ID_POPUPBAR_MAX_BUTTONS);
	ASSERT((int)m_nButtons==m_arrButtonRect.GetSize());
	ASSERT((int)m_nButtons<=m_arrData.GetSize());
	ASSERT(nIndex<=m_nButtons);

	CRect rect=m_arrButtonRect[nIndex];

	DrawFrameRect(pDC,rect,nIndex+1);

}


// Draw Tear off bar
void COXPopupBarCtrl::DrawTearoffBar(CDC* pDC)
{
	ASSERT(IsTearOff());

	DWORD clrBar=::GetSysColor(COLOR_HIGHLIGHT);
	CRect rect=m_rectTearoffBar;

	CBrush brushBar(clrBar);
	pDC->FillRect(rect, &brushBar);
}


// draw default button
void COXPopupBarCtrl::DrawDefaultButton(CDC* pDC)
{
	ASSERT(IsDefaultButton());

	DWORD clrShadow=::GetSysColor(COLOR_BTNSHADOW);
	CRect rect=m_rectDefaultButton;

	DrawFrameRect(pDC,rect,0);

	CBrush brushFrame(clrShadow);
	rect.DeflateRect(2,2);
	pDC->FrameRect(&rect,&brushFrame);
	rect.DeflateRect(1,1);

	CFont* pOldFont=(CFont*)pDC->SelectObject(&m_font);
	int oldBkMode=pDC->SetBkMode(TRANSPARENT);					
	pDC->DrawText(m_sDefaultButtonText,&rect,DT_CENTER|DT_VCENTER|DT_END_ELLIPSIS);
	pDC->SetBkMode(oldBkMode);					
	pDC->SelectObject(pOldFont);
}


// draw custom button
void COXPopupBarCtrl::DrawCustomButton(CDC* pDC)
{
	ASSERT(IsCustomButton());

	CRect rect=m_rectCustomButton;

	DrawFrameRect(pDC,rect,m_nButtons+1);

	rect.DeflateRect(3,3);
	CFont* pOldFont=(CFont*)pDC->SelectObject(&m_font);
	int oldBkMode=pDC->SetBkMode(TRANSPARENT);					
	pDC->DrawText(m_sCustomButtonText,&rect,DT_CENTER|DT_VCENTER|DT_END_ELLIPSIS);
	pDC->SetBkMode(oldBkMode);					
	pDC->SelectObject(pOldFont);
}


// draw frame rect around button
void COXPopupBarCtrl::DrawFrameRect(CDC* pDC, CRect& rect, UINT nIndex)
{
	DWORD clrHilight=::GetSysColor(COLOR_BTNHILIGHT);
	DWORD clrShadow=::GetSysColor(COLOR_BTNSHADOW);
	DWORD clrLight=::GetSysColor(COLOR_3DLIGHT);

	if(m_nSelectedIndex==nIndex || 
		(m_nCurrentIndex==nIndex && m_bMouseButtonPressed))
	{
		// draw sunken rect
		pDC->Draw3dRect(rect,clrShadow,clrHilight);
	}
	else if(m_nCurrentIndex==nIndex)
	{
		// draw raised rect
		pDC->Draw3dRect(rect,clrHilight,clrShadow);
	}

	rect.DeflateRect(1,1);
	if(m_nSelectedIndex==nIndex && m_nCurrentIndex!=nIndex)
	{
		// fill rect with lighter color
		CBrush brush(clrLight);
		pDC->FillRect(&rect,&brush);
	}
}


void COXPopupBarCtrl::DrawBackground(CDC* pDC)
{
	ASSERT(pDC!=NULL);

	WNDCLASS wc;
	HBRUSH hBrush = NULL;
	if(GetClassInfo(AfxGetInstanceHandle(),POPUPBAR_CLASSNAME,&wc))
		hBrush=wc.hbrBackground;
	CRect rect;
	GetClientRect(rect);
	::FillRect(pDC->GetSafeHdc(),rect,hBrush);
}


LRESULT COXPopupBarCtrl::OnFloatStatus(WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);

	if(!IsFloating())
	{
		return Default();
	}

	// these asserts make sure no conflicting actions are requested
	ASSERT(!((wParam & FS_SHOW) && (wParam & FS_HIDE)));
	ASSERT(!((wParam & FS_ENABLE) && (wParam & FS_DISABLE)));
	ASSERT(!((wParam & FS_ACTIVATE) && (wParam & FS_DEACTIVATE)));

	// FS_SYNCACTIVE is used to detect MFS_SYNCACTIVE windows
	LRESULT lResult=0;
	if((GetStyle() & MFS_SYNCACTIVE) && (wParam & FS_SYNCACTIVE))
	{
		lResult=1;
	}

	if(wParam & (FS_SHOW|FS_HIDE))
	{
		SetWindowPos(NULL,0,0,0,0,
			((wParam & FS_SHOW) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW)|
			SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
	}

	if(wParam&(FS_ENABLE|FS_DISABLE))
	{
		EnableWindow((wParam & FS_ENABLE)!=0);
	}

	if((wParam & (FS_ACTIVATE|FS_DEACTIVATE)) && (GetStyle() & MFS_SYNCACTIVE))
	{
		ModifyStyle(MFS_SYNCACTIVE,0);
		SendMessage(WM_NCACTIVATE,(wParam & FS_ACTIVATE)!=0);
		ModifyStyle(0,MFS_SYNCACTIVE);
	}

	return lResult;
}


BOOL COXPopupBarCtrl::OnNcCreate(LPCREATESTRUCT lpcs)
{
	if(!CWnd::OnNcCreate(lpcs))
	{
		return FALSE;
	}

	if(GetStyle() & MFS_SYNCACTIVE)
	{
		// syncronize activation state with top level parent
		CWnd* pParentWnd=GetTopLevelParent();
		ASSERT(pParentWnd!=NULL);
		CWnd* pActiveWnd=GetForegroundWindow();
		BOOL bActive=(pParentWnd==pActiveWnd) ||
			(pParentWnd->GetLastActivePopup()==pActiveWnd &&
			pActiveWnd->SendMessage(WM_FLOATSTATUS,FS_SYNCACTIVE)!=0);

		// the WM_FLOATSTATUS does the actual work
		SendMessage(WM_FLOATSTATUS, bActive ? FS_ACTIVATE : FS_DEACTIVATE);
	}

	return TRUE;
}


BOOL COXPopupBarCtrl::OnNcActivate(BOOL bActive)
{
	UNREFERENCED_PARAMETER(bActive);

	if((GetStyle() & MFS_SYNCACTIVE)==0)
	{
		return (BOOL)Default();
	}
	else if(m_nFlags & WF_KEEPMINIACTIVE)
	{
		return FALSE;
	}
	return TRUE;
}


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