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