www.pudn.com > mu_explorer.rar > CJToolBar.cpp


// CJToolBar.cpp : implementation file 
// Copyright © 1998-1999 CodeJock.com, All Rights Reserved. 
// See ReadMe.txt for TERMS OF USE. 
// 
///////////////////////////////////////////////////////////////////////////// 
/**************************************************************************** 
 * 
 * $Date: 10/30/99 1:56p $ 
 * $Revision: 18 $ 
 * $Archive: /CodeJock/CJLibrary/CJToolBar.cpp $ 
 * 
 * $History: CJToolBar.cpp $ 
 *  
 * *****************  Version 18  ***************** 
 * User: Kirk Stowell Date: 10/30/99   Time: 1:56p 
 * Updated in $/CodeJock/CJLibrary 
 * Removed Invalidate() calls from OnWindowPosChanging(...) to eliminate 
 * screen flicker. 
 *  
 * *****************  Version 17  ***************** 
 * User: Kirk Stowell Date: 10/26/99   Time: 10:51p 
 * Updated in $/CodeJock/CJLibrary 
 * Added CCJFontCombo to the list of controls that can be inserted into a 
 * CCJToolBar. 
 *  
 * *****************  Version 16  ***************** 
 * User: Kirk Stowell Date: 10/24/99   Time: 12:01a 
 * Updated in $/CodeJock/CJLibrary 
 * Fixed potential resource and memory leak problems. 
 *  
 * *****************  Version 15  ***************** 
 * User: Kirk Stowell Date: 9/13/99    Time: 5:45a 
 * Updated in $/CodeJockey/CJLibrary 
 * Fixed initialization bug (small). 
 *  
 * *****************  Version 14  ***************** 
 * User: Kirk Stowell Date: 8/31/99    Time: 1:11a 
 * Updated in $/CodeJockey/CJLibrary 
 * Updated copyright and contact information. 
 *  
 * *****************  Version 13  ***************** 
 * User: Kirk Stowell Date: 8/30/99    Time: 12:06a 
 * Updated in $/CodeJockey/CJLibrary 
 * Added support for CCJComboBoxEx control. 
 *  
 * *****************  Version 12  ***************** 
 * User: Kirk Stowell Date: 8/29/99    Time: 9:14p 
 * Updated in $/CodeJockey/CJLibrary 
 * Added Unicode compliance, thanks to Barry Burton for his help with 
 * this. 
 *  
 *  
 * *****************  Version 10  ***************** 
 * User: Kirk Stowell Date: 7/25/99    Time: 10:00p 
 * Updated in $/CodeJockey/CJLibrary 
 *  
 * *****************  Version 6  ***************** 
 * User: Kirk Stowell Date: 7/18/99    Time: 10:20p 
 * Updated in $/CodeJockey/CJLibrary 
 * Re-wrote toolbar class for vc5 compatibility. Added customization based  
 * on the "Customizable toolbar" article by Doug Keith -  
 * http://www.codeguru.com/toolbar/customizable_tb.shtml. 
 *  
 * Cleaned up inline functions, and import/export macro so that class will 
 * be imported when linked to, and exported at compile time. 
 *  
 * *****************  Version 5  ***************** 
 * User: Kirk Stowell Date: 4/03/99    Time: 11:44p 
 * Updated in $/CodeJockey/CJ60Lib 
 * Added comments and cleaned up code. 
 *  
 * *****************  Version 4  ***************** 
 * User: Kirk Stowell Date: 3/13/99    Time: 11:43p 
 * Updated in $/CodeJockey/CJ60Lib 
 * Changed class to handle both Office and DevStudio style to toolbars. To 
 * use Office toolbars, use the CreateEx() method, otherwise your toolbars 
 * will look like DevStudio. 
 *  
 * *****************  Version 3  ***************** 
 * User: Kirk Stowell Date: 12/14/98   Time: 11:42p 
 * Updated in $/CodeJockey/CJ60Lib 
 * Changed class to derive from Paul DiLascia's CToolBar, this class 
 * adds gripper and control insertion. 
 *  
 * *****************  Version 2  ***************** 
 * User: Kirk Stowell Date: 11/02/98   Time: 11:41p 
 * Updated in $/CodeJockey/CJ60Lib 
 * Fixed bug with DrawNoGripper() method - (Christian Skovdal Andersen). 
 *  
 * *****************  Version 1  ***************** 
 * User: Kirk Stowell Date: 10/17/98   Time: 11:41p 
 * Created in $/CodeJockey/CJ60Lib 
 * Initial re-write and release. 
 * 
 ***************************************************************************/ 
///////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "CJToolBar.h" 
#include "CJFlatComboBox.h" 
#include "CJComboBoxEx.h" 
#include "CJFontCombo.h" 
#include "CJMenu.h"        // CCJMenu class declaration 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
#define IDC_CUSTOMIZE_BAR	0xFF 
 
/* 
DIBs use RGBQUAD format: 
0xbb 0xgg 0xrr 0x00 
 
  Reasonably efficient code to convert a COLORREF into an 
  RGBQUAD is byte-order-dependent, so we need different 
  code depending on the byte order we're targeting. 
*/ 
#define _RGB_TO_RGBQUAD(r,g,b)   (RGB(b,g,r)) 
#define _CLR_TO_RGBQUAD(clr)     (RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr))) 
 
struct CJX_COLORMAP 
{ 
	// use DWORD instead of RGBQUAD so we can compare two RGBQUADs easily 
	DWORD rgbqFrom; 
	int iSysColorTo; 
}; 
 
const CJX_COLORMAP _sysColorMap[] = 
{ 
	// mapping from color in DIB to system color 
	{ _RGB_TO_RGBQUAD(0x00, 0x00, 0x00),  COLOR_BTNTEXT },       // black 
	{ _RGB_TO_RGBQUAD(0x80, 0x80, 0x80),  COLOR_BTNSHADOW },     // dark gray 
	{ _RGB_TO_RGBQUAD(0xC0, 0xC0, 0xC0),  COLOR_BTNFACE },       // bright gray 
	{ _RGB_TO_RGBQUAD(0xFF, 0xFF, 0xFF),  COLOR_BTNHIGHLIGHT }   // white 
}; 
 
HBITMAP AFXAPI 
_LoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc, BOOL bMono) 
{ 
	HGLOBAL hglb; 
	if ((hglb = LoadResource(hInst, hRsrc)) == NULL) 
		return NULL; 
	 
	LPBITMAPINFOHEADER lpBitmap = (LPBITMAPINFOHEADER)LockResource(hglb); 
	if (lpBitmap == NULL) 
		return NULL; 
	 
	// make copy of BITMAPINFOHEADER so we can modify the color table 
	const int nColorTableSize = 16; 
	UINT nSize = lpBitmap->biSize + nColorTableSize * sizeof(RGBQUAD); 
	LPBITMAPINFOHEADER lpBitmapInfo = (LPBITMAPINFOHEADER)::malloc(nSize); 
	if (lpBitmapInfo == NULL) 
		return NULL; 
	memcpy(lpBitmapInfo, lpBitmap, nSize); 
	 
	// color table is in RGBQUAD DIB format 
	DWORD* pColorTable = 
		(DWORD*)(((LPBYTE)lpBitmapInfo) + (UINT)lpBitmapInfo->biSize); 
	 
	for (int iColor = 0; iColor < nColorTableSize; iColor++) 
	{ 
		// look for matching RGBQUAD color in original 
		for (int i = 0; i < _countof(_sysColorMap); i++) 
		{ 
			if (pColorTable[iColor] == _sysColorMap[i].rgbqFrom) 
			{ 
				if (bMono) 
				{ 
					// all colors except text become white 
					if (_sysColorMap[i].iSysColorTo != COLOR_BTNTEXT) 
						pColorTable[iColor] = _RGB_TO_RGBQUAD(255, 255, 255); 
				} 
				else 
					pColorTable[iColor] = 
					_CLR_TO_RGBQUAD(::GetSysColor(_sysColorMap[i].iSysColorTo)); 
				break; 
			} 
		} 
	} 
	 
	int nWidth = (int)lpBitmapInfo->biWidth; 
	int nHeight = (int)lpBitmapInfo->biHeight; 
	HDC hDCScreen = ::GetDC(NULL); 
	HBITMAP hbm = ::CreateCompatibleBitmap(hDCScreen, nWidth, nHeight); 
	 
	if (hbm != NULL) 
	{ 
		HDC hDCGlyphs = ::CreateCompatibleDC(hDCScreen); 
		HBITMAP hbmOld = (HBITMAP)::SelectObject(hDCGlyphs, hbm); 
		 
		LPBYTE lpBits; 
		lpBits = (LPBYTE)(lpBitmap + 1); 
		lpBits += (1 << (lpBitmapInfo->biBitCount)) * sizeof(RGBQUAD); 
		 
		StretchDIBits(hDCGlyphs, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight, 
			lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY); 
		SelectObject(hDCGlyphs, hbmOld); 
		::DeleteDC(hDCGlyphs); 
	} 
	::ReleaseDC(NULL, hDCScreen); 
	 
	// free copy of bitmap info struct and resource itself 
	::free(lpBitmapInfo); 
	::FreeResource(hglb); 
	 
	return hbm; 
} 
 
#ifdef AFX_INIT_SEG 
#pragma code_seg(AFX_INIT_SEG) 
#endif 
 
struct AFX_DLLVERSIONINFO 
{ 
	DWORD cbSize; 
	DWORD dwMajorVersion;                   // Major version 
	DWORD dwMinorVersion;                   // Minor version 
	DWORD dwBuildNumber;                    // Build number 
	DWORD dwPlatformID;                     // DLLVER_PLATFORM_* 
}; 
 
typedef HRESULT (CALLBACK* AFX_DLLGETVERSIONPROC)(AFX_DLLVERSIONINFO *); 
 
int _ComCtlVersion = -1; 
 
DWORD AFXAPI _GetComCtlVersion() 
{ 
	// return cached version if already determined... 
	if (_ComCtlVersion != -1) 
		return _ComCtlVersion; 
	 
	// otherwise determine comctl32.dll version via DllGetVersion 
	HINSTANCE hInst = ::GetModuleHandleA("COMCTL32.DLL"); 
	ASSERT(hInst != NULL); 
	AFX_DLLGETVERSIONPROC pfn; 
	pfn = (AFX_DLLGETVERSIONPROC)GetProcAddress(hInst, "DllGetVersion"); 
	DWORD dwVersion = VERSION_WIN4; 
	if (pfn != NULL) 
	{ 
		AFX_DLLVERSIONINFO dvi; 
		memset(&dvi, 0, sizeof(dvi)); 
		dvi.cbSize = sizeof(dvi); 
		HRESULT hr = (*pfn)(&dvi); 
		if (SUCCEEDED(hr)) 
		{ 
			ASSERT(dvi.dwMajorVersion <= 0xFFFF); 
			ASSERT(dvi.dwMinorVersion <= 0xFFFF); 
			dwVersion = MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion); 
		} 
	} 
	_ComCtlVersion = dwVersion; 
	return dwVersion; 
} 
 
struct CJX_CONTROLPOS { 
	int				nIndex; 
	int				nID; 
	CRect			rectOldPos; 
} ; 
 
int _dropDownWidth = -1; 
 
int AFXAPI _GetDropDownWidth() 
{ 
	// return cached version if already determined... 
	if (_dropDownWidth != -1) 
		return _dropDownWidth; 
 
	// otherwise calculate it... 
	HDC hDC = GetDC(NULL); 
	ASSERT(hDC != NULL); 
	HFONT hFont; 
	if ((hFont = ::CreateFont(GetSystemMetrics(SM_CYMENUCHECK), 0, 0, 0, 
		FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett"))) != NULL) 
	{ 
		HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont); 
	 
		VERIFY(GetCharWidth(hDC, '6', '6', &_dropDownWidth)); 
 
		// fix potential resource leak - KStowell - 10-21-99 
		if (hFont != NULL) { 
			::SelectObject(hDC, hOldFont); 
			::DeleteObject(hFont); 
		} 
	} 
 
	::ReleaseDC(NULL, hDC); 
	ASSERT(_dropDownWidth != -1); 
	return _dropDownWidth; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJToolBar 
 
int CCJToolBar::m_nBarNumber = 0; 
 
CCJToolBar::CCJToolBar() 
{ 
	m_bShowDropArrow = false; 
	m_pControls		 = NULL; 
	m_pDropButtons	 = NULL;		// list of drop-down buttons 
	m_nButtons		 = 0; 
	m_pBarInfo		 = NULL; 
	m_hKeyRoot		 = HKEY_CURRENT_USER; 
	 
	// get a pointer to the application. 
	CWinApp* pApp = AfxGetApp(); 
	ASSERT( pApp ); 
 
	++m_nBarNumber; 
	m_strValueName.Format( _T("ToolBar %d"), m_nBarNumber ); 
	m_strSubKey.Format( _T("Software\\%s\\%s\\Settings"), 
		pApp->m_pszRegistryKey, pApp->m_pszProfileName ); 
 
	// initialize state 
	m_pStringMap = NULL; 
	m_hRsrcImageWell = NULL; 
	m_hInstImageWell = NULL; 
	m_hbmImageWell = NULL; 
	m_bDelayedButtonLayout = TRUE; 
 
	// default image sizes 
	m_sizeImage.cx = 16; 
	m_sizeImage.cy = 15; 
 
	// default button sizes 
	m_sizeButton.cx = 23; 
	m_sizeButton.cy = 22; 
 
	// top and bottom borders are 1 larger than default for ease of grabbing 
	m_cyTopBorder = 3; 
	m_cyBottomBorder = 3; 
	m_bExStyle = false; 
} 
 
CCJToolBar::~CCJToolBar() 
{ 
	AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell); 
	delete m_pStringMap; 
	 
	m_nCount = 0; 
	 
	while (m_pDropButtons) 
	{ 
		DROPDOWNBUTTON* pnext = m_pDropButtons->next; 
		delete m_pDropButtons; 
		m_pDropButtons = pnext; 
	} 
	 
	if( m_pControls ) { 
		for( POSITION pos = m_pControls->GetHeadPosition() ; pos ; ) { 
			delete m_pControls->GetNext(pos); 
		} 
		delete m_pControls; 
	} 
} 
 
BOOL CCJToolBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) 
{ 
	return CreateEx(pParentWnd, NULL, dwStyle, 
		CRect(m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder), nID); 
} 
 
BOOL CCJToolBar::CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle, DWORD dwStyle, CRect rcBorders, UINT nID) 
{ 
	ASSERT_VALID(pParentWnd);   // must have a parent 
	ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC))); 
	 
	// KStowell - bool for drawing office style gripper. 
	if( dwCtrlStyle != NULL ) 
		m_bExStyle = true; 
 
	SetBorders(rcBorders); 
 
	// save the style 
	m_dwStyle = (dwStyle & CBRS_ALL)|( dwStyle & CBRS_GRIPPER ); 
	if (nID == AFX_IDW_TOOLBAR) 
		m_dwStyle |= CBRS_HIDE_INPLACE; 
 
	dwStyle &= ~CBRS_ALL; 
	dwStyle |= CCS_NOPARENTALIGN|CCS_NOMOVEY|CCS_NODIVIDER|CCS_NORESIZE; 
	dwStyle |= dwCtrlStyle; 
 
	// initialize common controls 
 
#ifdef _VC_VERSION_5 
 
	INITCOMMONCONTROLSEX icex; 
	icex.dwSize = sizeof(icex); 
	icex.dwICC = ICC_BAR_CLASSES; 
	VERIFY(InitCommonControlsEx(&icex)); 
 
#else 
 
	VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTL_BAR_REG)); 
 
#endif 
 
	_GetComCtlVersion(); 
	ASSERT(_ComCtlVersion != -1); 
	_GetDropDownWidth(); 
	ASSERT(_dropDownWidth != -1); 
 
	// create the HWND 
	CRect rect; rect.SetRectEmpty(); 
	if (!CWnd::Create(TOOLBARCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID)) 
		return FALSE; 
 
	// sync up the sizes 
	SetSizes(m_sizeButton, m_sizeImage); 
 
	// Note: Parent must resize itself for control bar to be resized 
 
	return TRUE; 
} 
 
BOOL CCJToolBar::PreCreateWindow(CREATESTRUCT& cs) 
{ 
	if( !CCJToolBarBase::PreCreateWindow(cs)) 
		return FALSE; 
	 
	cs.style |= TBSTYLE_TRANSPARENT|TBSTYLE_FLAT; 
 
	return TRUE; 
} 
 
////////////////////////////////////////////////////////////////////// 
// 1999 Kirk Stowell - Inserts a control into the toolbar at the given button id. 
// 
CWnd* CCJToolBar::InsertControl( CRuntimeClass* pClass, LPCTSTR lpszWindowName, CRect& rect, UINT nID, DWORD dwStyle ) 
{ 
	CWnd *pCtrl = NULL; 
	 
	if( pClass->IsDerivedFrom( RUNTIME_CLASS( CCJComboBoxEx )))	// CCJComboBoxEx control. 
	{ 
		pCtrl = new CCJComboBoxEx; 
	 
		ASSERT_VALID( pCtrl ); 
		 
		if(((CCJComboBoxEx*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
	} 
	else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CCJFlatComboBox )))	// CCJFlatComboBox control. 
	{ 
		pCtrl = new CCJFlatComboBox; 
	 
		ASSERT_VALID( pCtrl ); 
		 
		if(((CCJFlatComboBox*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
	} 
	else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CCJFontCombo )))	// CCJFontCombo control. 
	{ 
		pCtrl = new CCJFontCombo; 
	 
		ASSERT_VALID( pCtrl ); 
		 
		if(((CCJFontCombo*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
	} 
	else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CComboBox )))	// CComboBox control. 
	{ 
		pCtrl = new CComboBox; 
 
		ASSERT_VALID( pCtrl ); 
 
		if(((CComboBox*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
	} 
	else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CEdit )))		// CEdit control. 
	{ 
		pCtrl = new CEdit; 
 
		ASSERT_VALID( pCtrl ); 
 
		if(((CEdit*)pCtrl)->Create( WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
	} 
	else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CButton )))		// CButton control. 
	{ 
		pCtrl = new CButton; 
 
		ASSERT_VALID( pCtrl ); 
 
		if(((CButton*)pCtrl)->Create( lpszWindowName, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
	} 
	else if( pClass->IsDerivedFrom( RUNTIME_CLASS( CWnd )))			// CWnd object. 
	{ 
		pCtrl = new CWnd; 
		 
		ASSERT_VALID( pCtrl ); 
 
#ifdef _UNICODE 
		TCHAR szClassName[ 256 ]; 
 
		MultiByteToWideChar( CP_ACP, 
							 MB_PRECOMPOSED, 
							 pClass->m_lpszClassName, 
							 -1, 
							 szClassName, 
							 255 ); 
 
		if(((CWnd*)pCtrl)->Create( szClassName, lpszWindowName, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
#else 
		if(((CWnd*)pCtrl)->Create( pClass->m_lpszClassName, lpszWindowName, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, nID ) == FALSE ) 
		{ 
			delete pCtrl; 
			return NULL; 
		} 
#endif 
	} 
	else															// An invalid object was passed in 
	{ 
		ASSERT( FALSE ); 
		return NULL; 
	} 
 
	// if our object list has not been allocated, do it now... 
	if( m_pControls == NULL ) 
	{ 
		m_pControls = new CObList(); 
		ASSERT( m_pControls ); 
	} 
 
	// we have to remember this control, so we can delete it later 
	m_pControls->AddTail( pCtrl ); 
 
	return InsertControl( pCtrl, rect, nID ); 
} 
 
CWnd* CCJToolBar::InsertControl(CWnd* pCtrl, CRect & rect, UINT nID) 
{ 
	ASSERT_VALID( pCtrl ); 
 
	// make sure the id is valid, and set the button  
	// style for a seperator. 
	ASSERT( CommandToIndex( nID ) >= 0 ); 
	SetButtonInfo( CommandToIndex( nID ), nID, TBBS_SEPARATOR, rect.Width()); 
	 
	// insert the control into the toolbar. 
	GetItemRect( CommandToIndex(nID), &rect ); 
	pCtrl->SetWindowPos(0, rect.left, rect.top, 0, 0, 
		SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS ); 
	pCtrl->SetFont( GetFont( )); 
	pCtrl->ShowWindow( SW_SHOW ); 
 
	return pCtrl; 
} 
 
////////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// call to add drop-down menus to toolbar buttons. 
// 
BOOL CCJToolBar::AddDropDownButton(UINT nIDButton, UINT nIDMenu, BOOL bArrow) 
{ 
	ASSERT_VALID(this); 
	 
	DROPDOWNBUTTON* pb = FindDropDownButton(nIDButton); 
	if (!pb) { 
		pb = new DROPDOWNBUTTON; 
		ASSERT(pb); 
		pb->next = m_pDropButtons; 
		m_pDropButtons = pb; 
	} 
	pb->idButton = nIDButton; 
	pb->idMenu   = nIDMenu; 
	 
	int iButton = CommandToIndex(nIDButton); 
	 
	DWORD dwStyle = GetButtonStyle(iButton); 
	dwStyle |= TBSTYLE_DROPDOWN; 
	SetButtonStyle(iButton, dwStyle); 
	 
	if (bArrow) 
		SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); 
	 
	return TRUE; 
} 
 
////////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// Find buttons structure for given ID 
// 
DROPDOWNBUTTON* CCJToolBar::FindDropDownButton(UINT nID) 
{ 
	for (DROPDOWNBUTTON* pb = m_pDropButtons; pb; pb = pb->next) { 
		if (pb->idButton == nID) 
			return pb; 
	} 
	return NULL; 
} 
 
////////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// Message handler for TBN_DROPDOWN. Default is to display the  
// specified menu at the right place. You can override to generate dynamic menus 
// 
// Args: 
//		- NMTOOLBAR struct from TBN_DROPDOWN 
//		- command id of button 
//		- point to display menu at 
// 
#ifndef SEESIIE 
void CCJToolBar::OnToolBarBtnDropDown(NMHDR* pNMHDR, LRESULT* pRes) 
{ 
	UNUSED_ALWAYS( pRes ); 
	 
	const NMTOOLBAR& nmtb = *(NMTOOLBAR*)pNMHDR; 
	 
	// get location of button 
	CRect rc; 
	GetRect(nmtb.iItem, rc); 
	ClientToScreen(&rc); 
	 
	// call virtual function to display dropdown menu 
	OnDropDownButton(nmtb, nmtb.iItem, rc);	 
} 
#endif 
///////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// Virtual fn you can override to hand drop-down button 
// events with more friendly args 
// 
void CCJToolBar::OnDropDownButton(const NMTOOLBAR& nmtb, UINT nID, CRect rc) 
{ 
	UNUSED_ALWAYS( nID ); 
 
	DROPDOWNBUTTON* pb = FindDropDownButton(nmtb.iItem); 
	if (pb && pb->idMenu) { 
		 
		// load and display popup menu 
		CCJMenu menu; 
		VERIFY(menu.LoadMenu(pb->idMenu)); 
		CCJMenu* pPopup = (CCJMenu*)menu.GetSubMenu(0); 
		ASSERT(pPopup); 
		pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL, 
			rc.left, rc.bottom, GetParentFrame(), &rc); 
	} 
} 
 
////////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// Now toolbar has really moved: repaint area beneath old position 
// 
void CCJToolBar::OnWindowPosChanged(WINDOWPOS* lpwndpos)  
{ 
	CCJToolBarBase::OnWindowPosChanged(lpwndpos); 
	if (!(lpwndpos->flags & SWP_NOMOVE)) {	 // if moved: 
		InvalidateOldPos(m_rcOldPos);		 // invalidate area of old position 
		 
		// Now paint my non-client area at the new location. 
		// Without this, you will still have a partial display bug (try it!) 
		SendMessage(WM_NCPAINT); 
	} 
} 
 
////////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// Invalidate toolbar rectangle. Because flat toolbars are transparent, 
// this requires invalidating parent and all siblings that intersect the 
// rectangle. 
// 
void CCJToolBar::InvalidateOldPos(const CRect& rcInvalid) 
{ 
	// make parent paint the area beneath rectangle 
	CWnd* pParent = GetParent();		// parent (dock bar/frame) window 
	ASSERT_VALID(pParent);				// check 
	CRect rc( rcInvalid );       		// copy rectangle 
	pParent->ScreenToClient(&rc);		// convert to parent client coords 
	pParent->InvalidateRect(&rc);		// invalidate 
 
	// now do same for each sibling too 
	for (CWnd* pSib = pParent->GetWindow(GW_CHILD); 
		  pSib; 
		  pSib=pSib->GetNextWindow(GW_HWNDNEXT)) { 
 
		CRect rc;										// window rect of sibling 
		pSib->GetWindowRect(&rc);					// ... 
		if (rc.IntersectRect(rc, rcInvalid)) {	// if intersects invalid rect 
			pSib->ScreenToClient(&rc);				// convert to sibling coords 
			pSib->InvalidateRect(&rc);				// invalidate 
			pSib->SendMessage(WM_NCPAINT);		// nonclient area too! 
		} 
	} 
} 
 
////////////////// 
// 1997 Microsoft Systems Journal - Written by Paul DiLascia. 
// This is the all-important function that gets the true size of a button, 
// instead of using m_sizeButton. And it's virtual, so you can override if 
// my algorithm doesn't work, as will surely be the case in some circumstances. 
// 
CSize CCJToolBar::GetButtonSize(TBBUTTON* pData, int iButton) 
{ 
	ASSERT(_ComCtlVersion > 0); 
 
	// Get the actual size of the button, not what's in m_sizeButton. 
	// Make sure to do SendMessage instead of calling MFC's GetItemRect, 
	// which has all sorts of bad side-effects! (Go ahead, take a look at it.) 
	//  
	CRect rc; 
	SendMessage(TB_GETITEMRECT, iButton, (LPARAM)&rc); 
	CSize sz = rc.Size(); 
 
	//////////////// 
	// Now must do special case for various versions of comctl32.dll, 
	// 
	DWORD dwStyle = pData[iButton].fsStyle; 
	if ((pData[iButton].fsState & TBSTATE_WRAP)) { 
		if (dwStyle & TBSTYLE_SEP) { 
			// this is the last separator in the row (eg vertically docked) 
			// fudge the height, and ignore the width. TB_GETITEMRECT will return 
			// size = (8 x 22) even for a separator in vertical toolbar 
			// 
			if (_ComCtlVersion <= VERSION_IE3) 
				sz.cy -= 3;		// empircally good fudge factor 
			else if (_ComCtlVersion != VERSION_IE4) 
				sz.cy = sz.cx; 
			sz.cx = 0;			// separator takes no width if it's the last one 
 
		} else if (dwStyle & TBSTYLE_DROPDOWN && ( m_bShowDropArrow == false )) { 
			// ignore width of dropdown 
			sz.cx = 0; 
		} 
	} 
	return sz; 
} 
 
// This function saves the state (visible buttons, toolbar position, etc.) 
// of the toolbar, using the registry key provided to the Create(...) function. 
void CCJToolBar::SaveState() 
{ 
	// if there is an associated registry subkey 
	if (m_strSubKey.GetLength()) 
	{ 
		// save the toolbar state to the registry 
		GetToolBarCtrl().SaveState( m_hKeyRoot, m_strSubKey, m_strValueName ); 
	} 
} 
 
// This function restores the state (visible buttons, toolbar position, etc.) 
// of the toolbar, using the registry key provided to the Create(...) function. 
void CCJToolBar::RestoreState() 
{ 
	// if there is an associated registry subkey 
	if (m_strSubKey.GetLength()) 
	{ 
		// restore the toolbar state from the registry 
		GetToolBarCtrl().RestoreState( m_hKeyRoot, m_strSubKey, m_strValueName ); 
	} 
} 
 
// This function is called when the user begins dragging a toolbar 
// button or when the customization dialog is being populated with 
// toolbar information.  Basically, *pResult should be populated with 
// your answer to the question, "is the user allowed to delete this 
// button?". 
void CCJToolBar::OnToolBarQueryDelete(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
 
	// if we're not floating - user can delete anything 
	*pResult = !IsFloating(); 
} 
 
// This function is called when the user begins dragging a toolbar 
// button or when the customization dialog is being populated with 
// toolbar information.  Basically, *pResult should be populated with 
// your answer to the question, "is the user allowed to insert a 
// button to the left of this one?". 
void CCJToolBar::OnToolBarQueryInsert(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
 
	// if we're not floating - user can insert anywhere 
	*pResult = !IsFloating(); 
} 
 
// This function is called whenever the user makes a change to the 
// layout of the toolbar.  Calling the mainframe's RecalcLayout forces 
// the toolbar to repaint itself. 
void CCJToolBar::OnToolBarChange(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
 
	// force the frame window to recalculate the size 
	GetParentFrame()->RecalcLayout(); 
} 
 
// This function is called when the user begins dragging a toolbar button. 
void CCJToolBar::OnToolBarBeginDrag(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
} 
 
// This function is called when the user has completed a dragging operation. 
void CCJToolBar::OnToolBarEndDrag(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
} 
 
// This function is called when the user initially calls up the toolbar 
// customization dialog box. 
void CCJToolBar::OnToolBarBeginAdjust(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
} 
 
// This function is called when the user clicks on the help button on the 
// toolbar customization dialog box. 
void CCJToolBar::OnToolBarCustomHelp(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
} 
 
// This function is called when the user dismisses the toolbar customization 
// dialog box. 
void CCJToolBar::OnToolBarEndAdjust(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
 
	// save the state of the toolbar for reinitialization 
	SaveState(); 
} 
 
// This function is called to populate the toolbar customization dialog box 
// with information regarding all of the possible toolbar buttons. 
void CCJToolBar::OnToolBarGetButtonInfo(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pResult ); 
 
	TBNOTIFY* tbStruct;		// data needed by customize dialog box 
 
	// init the pointer 
	tbStruct = (TBNOTIFY *)pNMHDR; 
 
	// if the index is valid 
	if (0 <= tbStruct->iItem && tbStruct->iItem < m_nButtons) 
	{ 
		// copy the stored button structure 
		tbStruct->tbButton = m_pBarInfo[tbStruct->iItem].tbButton; 
 
		// copy the text for the button label in the dialog 
		_tcscpy( tbStruct->pszText, m_pBarInfo[tbStruct->iItem].btnText ); 
 
		// indicate valid data was sent 
		*pResult = TRUE; 
	} 
 
	// else there is no button for this index 
	else 
	{ 
		*pResult = FALSE; 
	} 
} 
 
// This function is called when the user clicks on the reset button on the 
// toolbar customization dialog box. 
void CCJToolBar::OnToolBarReset(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	UNUSED_ALWAYS( pNMHDR ); 
	UNUSED_ALWAYS( pResult ); 
 
	// restore the toolbar to the way it was before entering customization 
	RestoreState(); 
} 
 
void CCJToolBar::OnDestroy()  
{ 
	// save the current state of the toolbar 
	SaveState(); 
	 
	CCJToolBarBase::OnDestroy(); 
} 
 
void CCJToolBar::OnRButtonDown(UINT nFlags, CPoint point)  
{ 
	if( m_pBarInfo ) 
	{ 
		CPoint pt( point ); 
		ClientToScreen( &pt ); 
		 
		// load and display popup menu 
		CCJMenu popupMenu; 
		VERIFY(popupMenu.CreatePopupMenu( )); 
		popupMenu.InsertMenu(0, MF_BYPOSITION, IDC_CUSTOMIZE_BAR, _T("&Customize...")); 
		int nResult = popupMenu.TrackPopupMenu( 
			TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL|TPM_RETURNCMD, 
			pt.x, pt.y, this ); 
		 
		if( nResult == IDC_CUSTOMIZE_BAR ) 
		{ 
			// open the customization dialog. 
			GetToolBarCtrl().Customize(); 
		} 
	} 
 
	else { 
		CCJToolBarBase::OnRButtonDown(nFlags, point); 
	} 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// The remainder of this class is mostly copied straight from MFC ( vc6 ),  
// except for the modifications which are commented... 
///////////////////////////////////////////////////////////////////////////// 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJToolBar 
 
BOOL CCJToolBar::OnNcCreate(LPCREATESTRUCT lpCreateStruct) 
{ 
	// If the toolbar is inside a ReBar, we need to make the parent frame 
	// the owner so it will get notifications. 
	CWnd* pParent = GetParent(); 
	ASSERT(pParent); 
	TCHAR classname[64]; 
	GetClassName(pParent->m_hWnd, classname, _countof(classname)); 
	if (_tcscmp(classname, REBARCLASSNAME)==0) 
	{ 
		CFrameWnd* pFrame = GetParentFrame(); 
		ASSERT_VALID(pFrame); 
		SetOwner(pFrame); 
		m_bInReBar = true; 
		m_bExStyle = false; 
	} 
	 
	if (!CCJToolBarBase::OnNcCreate(lpCreateStruct)) 
		return FALSE; 
	 
	// if the owner was set before the toolbar was created, set it now 
	if (m_hWndOwner != NULL) 
		DefWindowProc(TB_SETPARENT, (WPARAM)m_hWndOwner, 0); 
 
	DefWindowProc(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); 
	return TRUE; 
} 
 
void CCJToolBar::SetOwner(CWnd* pOwnerWnd) 
{ 
	ASSERT_VALID(this); 
	if (m_hWnd != NULL) 
	{ 
		ASSERT(::IsWindow(m_hWnd)); 
		DefWindowProc(TB_SETPARENT, (WPARAM)pOwnerWnd->GetSafeHwnd(), 0); 
	} 
	CCJToolBarBase::SetOwner(pOwnerWnd); 
} 
 
void CCJToolBar::SetSizes(SIZE sizeButton, SIZE sizeImage) 
{ 
	ASSERT_VALID(this); 
 
	// sizes must be non-zero and positive 
	ASSERT(sizeButton.cx > 0 && sizeButton.cy > 0); 
	ASSERT(sizeImage.cx > 0 && sizeImage.cy > 0); 
 
	// button must be big enough to hold image 
	//   + 7 pixels on x 
	//   + 6 pixels on y 
	ASSERT(sizeButton.cx >= sizeImage.cx + 7); 
	ASSERT(sizeButton.cy >= sizeImage.cy + 6); 
 
	if (::IsWindow(m_hWnd)) 
	{ 
		// set the sizes via TB_SETBITMAPSIZE and TB_SETBUTTONSIZE 
		VERIFY(SendMessage(TB_SETBITMAPSIZE, 0, MAKELONG(sizeImage.cx, sizeImage.cy))); 
		VERIFY(SendMessage(TB_SETBUTTONSIZE, 0, MAKELONG(sizeButton.cx, sizeButton.cy))); 
 
		Invalidate();   // just to be nice if called when toolbar is visible 
	} 
	else 
	{ 
		// just set our internal values for later 
		m_sizeButton = sizeButton; 
		m_sizeImage = sizeImage; 
	} 
} 
 
void CCJToolBar::SetHeight(int cyHeight) 
{ 
	ASSERT_VALID(this); 
 
	int nHeight = cyHeight; 
	if (m_dwStyle & CBRS_BORDER_TOP) 
		cyHeight -= afxData.cyBorder2; 
	if (m_dwStyle & CBRS_BORDER_BOTTOM) 
		cyHeight -= afxData.cyBorder2; 
	m_cyBottomBorder = (cyHeight - m_sizeButton.cy) / 2; 
	// if there is an extra pixel, m_cyTopBorder will get it 
	m_cyTopBorder = cyHeight - m_sizeButton.cy - m_cyBottomBorder; 
	if (m_cyTopBorder < 0) 
	{ 
		TRACE1("Warning: CCJToolBar::SetHeight(%d) is smaller than button.\n", 
			nHeight); 
		m_cyBottomBorder += m_cyTopBorder; 
		m_cyTopBorder = 0;  // will clip at bottom 
	} 
 
	// recalculate the non-client region 
	SetWindowPos(NULL, 0, 0, 0, 0, 
		SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOZORDER); 
	Invalidate();   // just to be nice if called when toolbar is visible 
} 
 
struct CToolBarData 
{ 
	WORD wVersion; 
	WORD wWidth; 
	WORD wHeight; 
	WORD wItemCount; 
	//WORD aItems[wItemCount] 
 
	WORD* items() 
		{ return (WORD*)(this+1); } 
}; 
 
BOOL CCJToolBar::LoadToolBar(LPCTSTR lpszResourceName, TOOLBARINFO* pBarInfo) 
{ 
	ASSERT_VALID(this); 
	ASSERT(lpszResourceName != NULL); 
 
	// determine location of the bitmap in resource fork 
	HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR); 
	HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR); 
	if (hRsrc == NULL) 
		return FALSE; 
 
	HGLOBAL hGlobal = LoadResource(hInst, hRsrc); 
	if (hGlobal == NULL) 
		return FALSE; 
 
	CToolBarData* pData = (CToolBarData*)LockResource(hGlobal); 
	if (pData == NULL) 
		return FALSE; 
	ASSERT(pData->wVersion == 1); 
 
	UINT* pItems = new UINT[pData->wItemCount]; 
	for (int i = 0; i < pData->wItemCount; i++) 
		pItems[i] = pData->items()[i]; 
	BOOL bResult = SetButtons(pItems, pData->wItemCount); 
	delete[] pItems; 
 
	if (bResult) 
	{ 
		// set new sizes of the buttons 
		CSize sizeImage(pData->wWidth, pData->wHeight); 
		CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7); 
		SetSizes(sizeButton, sizeImage); 
 
		// load bitmap now that sizes are known by the toolbar control 
		bResult = LoadBitmap(lpszResourceName); 
	} 
 
	UnlockResource(hGlobal); 
	FreeResource(hGlobal); 
 
	// is the toolbar is customizable? 
	if(( pBarInfo ) && ( bResult )) { 
		m_pBarInfo = pBarInfo; 
		m_nButtons = GetToolBarCtrl().GetButtonCount(); 
 
		// modify the style to include adjustable 
		ModifyStyle(0, CCS_ADJUSTABLE); 
		RestoreState(); 
	} 
 
 
	return bResult; 
} 
 
BOOL CCJToolBar::LoadBitmap(LPCTSTR lpszResourceName) 
{ 
	ASSERT_VALID(this); 
	ASSERT(lpszResourceName != NULL); 
 
	// determine location of the bitmap in resource fork 
	HINSTANCE hInstImageWell = AfxFindResourceHandle(lpszResourceName, RT_BITMAP); 
	HRSRC hRsrcImageWell = ::FindResource(hInstImageWell, lpszResourceName, RT_BITMAP); 
	if (hRsrcImageWell == NULL) 
		return FALSE; 
 
	// load the bitmap 
	HBITMAP hbmImageWell; 
	hbmImageWell = _LoadSysColorBitmap(hInstImageWell, hRsrcImageWell); 
 
	// tell common control toolbar about the new bitmap 
	if (!AddReplaceBitmap(hbmImageWell)) 
		return FALSE; 
 
	// remember the resource handles so the bitmap can be recolored if necessary 
	m_hInstImageWell = hInstImageWell; 
	m_hRsrcImageWell = hRsrcImageWell; 
	return TRUE; 
} 
 
BOOL CCJToolBar::SetBitmap(HBITMAP hbmImageWell) 
{ 
	ASSERT_VALID(this); 
	ASSERT(hbmImageWell != NULL); 
 
	// the caller must manage changing system colors 
	m_hInstImageWell = NULL; 
	m_hRsrcImageWell = NULL; 
 
	// tell common control toolbar about the new bitmap 
	return AddReplaceBitmap(hbmImageWell); 
} 
 
BOOL CCJToolBar::AddReplaceBitmap(HBITMAP hbmImageWell) 
{ 
	// need complete bitmap size to determine number of images 
	BITMAP bitmap; 
	VERIFY(::GetObject(hbmImageWell, sizeof(BITMAP), &bitmap)); 
 
	// add the bitmap to the common control toolbar 
	BOOL bResult; 
	if (m_hbmImageWell == NULL) 
	{ 
		TBADDBITMAP addBitmap; 
		addBitmap.hInst = NULL; // makes TBADDBITMAP::nID behave a HBITMAP 
		addBitmap.nID = (UINT)hbmImageWell; 
		bResult =  DefWindowProc(TB_ADDBITMAP, 
			bitmap.bmWidth / m_sizeImage.cx, (LPARAM)&addBitmap) == 0; 
	} 
	else 
	{ 
		TBREPLACEBITMAP replaceBitmap; 
		replaceBitmap.hInstOld = NULL; 
		replaceBitmap.nIDOld = (UINT)m_hbmImageWell; 
		replaceBitmap.hInstNew = NULL; 
		replaceBitmap.nIDNew = (UINT)hbmImageWell; 
		replaceBitmap.nButtons = bitmap.bmWidth / m_sizeImage.cx; 
		bResult = (BOOL)DefWindowProc(TB_REPLACEBITMAP, 0, 
			(LPARAM)&replaceBitmap); 
	} 
	// remove old bitmap, if present 
	if (bResult) 
	{ 
		AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell); 
		m_hbmImageWell = hbmImageWell; 
	} 
 
	return bResult; 
} 
 
BOOL CCJToolBar::SetButtons(const UINT* lpIDArray, int nIDCount) 
{ 
	ASSERT_VALID(this); 
	ASSERT(nIDCount >= 1);  // must be at least one of them 
	ASSERT(lpIDArray == NULL || 
		AfxIsValidAddress(lpIDArray, sizeof(UINT) * nIDCount, FALSE)); 
 
	// delete all existing buttons 
	int nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
	while (nCount--) 
		VERIFY(DefWindowProc(TB_DELETEBUTTON, 0, 0)); 
 
	TBBUTTON button; memset(&button, 0, sizeof(TBBUTTON)); 
	button.iString = -1; 
	if (lpIDArray != NULL) 
	{ 
		// add new buttons to the common control 
		int iImage = 0; 
		for (int i = 0; i < nIDCount; i++) 
		{ 
			button.fsState = TBSTATE_ENABLED; 
			if ((button.idCommand = *lpIDArray++) == 0) 
			{ 
				// separator 
				button.fsStyle = TBSTYLE_SEP; 
				// width of separator includes 8 pixel overlap 
				ASSERT(_ComCtlVersion != -1); 
				if ((GetStyle() & TBSTYLE_FLAT) || _ComCtlVersion == VERSION_IE4) 
					button.iBitmap = 6; 
				else 
					button.iBitmap = 8; 
			} 
			else 
			{ 
				// a command button with image 
				button.fsStyle = TBSTYLE_BUTTON; 
				button.iBitmap = iImage++; 
			} 
			if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button)) 
				return FALSE; 
		} 
	} 
	else 
	{ 
		// add 'blank' buttons 
		button.fsState = TBSTATE_ENABLED; 
		for (int i = 0; i < nIDCount; i++) 
		{ 
			ASSERT(button.fsStyle == TBSTYLE_BUTTON); 
			if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button)) 
				return FALSE; 
		} 
	} 
	m_nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
	m_bDelayedButtonLayout = TRUE; 
 
	return TRUE; 
} 
 
#ifdef AFX_CORE3_SEG 
#pragma code_seg(AFX_CORE3_SEG) 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJToolBar attribute access 
 
void CCJToolBar::GetButton(int nIndex, TBBUTTON* pButton) const 
{ 
	CCJToolBar* pBar = (CCJToolBar*)this; 
	VERIFY(pBar->DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)pButton)); 
	// TBSTATE_ENABLED == TBBS_DISABLED so invert it 
	pButton->fsState ^= TBSTATE_ENABLED; 
} 
 
void CCJToolBar::SetButton(int nIndex, TBBUTTON* pButton) 
{ 
	// get original button state 
	TBBUTTON button; 
	VERIFY(DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)&button)); 
 
	// prepare for old/new button comparsion 
	button.bReserved[0] = 0; 
	button.bReserved[1] = 0; 
	// TBSTATE_ENABLED == TBBS_DISABLED so invert it 
	pButton->fsState ^= TBSTATE_ENABLED; 
	pButton->bReserved[0] = 0; 
	pButton->bReserved[1] = 0; 
 
	// nothing to do if they are the same 
	if (memcmp(pButton, &button, sizeof(TBBUTTON)) != 0) 
	{ 
		// don't redraw everything while setting the button 
		DWORD dwStyle = GetStyle(); 
		ModifyStyle(WS_VISIBLE, 0); 
		VERIFY(DefWindowProc(TB_DELETEBUTTON, nIndex, 0)); 
		VERIFY(DefWindowProc(TB_INSERTBUTTON, nIndex, (LPARAM)pButton)); 
		ModifyStyle(0, dwStyle & WS_VISIBLE); 
 
		// invalidate appropriate parts 
		if (((pButton->fsStyle ^ button.fsStyle) & TBSTYLE_SEP) || 
			((pButton->fsStyle & TBSTYLE_SEP) && pButton->iBitmap != button.iBitmap)) 
		{ 
			// changing a separator 
			Invalidate(); 
		} 
		else 
		{ 
			// invalidate just the button 
			CRect rect; 
			if (DefWindowProc(TB_GETITEMRECT, nIndex, (LPARAM)&rect)) 
				InvalidateRect(rect); 
		} 
	} 
} 
 
int CCJToolBar::CommandToIndex(UINT nIDFind) const 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	CCJToolBar* pBar = (CCJToolBar*)this; 
	return (int)pBar->DefWindowProc(TB_COMMANDTOINDEX, nIDFind, 0); 
} 
 
UINT CCJToolBar::GetItemID(int nIndex) const 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	TBBUTTON button; 
	GetButton(nIndex, &button); 
	return button.idCommand; 
} 
 
void CCJToolBar::GetItemRect(int nIndex, LPRECT lpRect) const 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	// handle any delayed layout 
	if (m_bDelayedButtonLayout) 
		((CCJToolBar*)this)->Layout(); 
 
	// now it is safe to get the item rectangle 
	CCJToolBar* pBar = (CCJToolBar*)this; 
	if (!pBar->DefWindowProc(TB_GETITEMRECT, nIndex, (LPARAM)lpRect)) 
		SetRectEmpty(lpRect); 
} 
 
void CCJToolBar::Layout() 
{ 
	ASSERT(m_bDelayedButtonLayout); 
 
	m_bDelayedButtonLayout = FALSE; 
 
	BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0; 
	if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC)) 
		((CCJToolBar*)this)->CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH | LM_COMMIT); 
	else if (bHorz) 
		((CCJToolBar*)this)->CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK | LM_COMMIT); 
	else 
		((CCJToolBar*)this)->CalcDynamicLayout(0, LM_VERTDOCK | LM_COMMIT); 
} 
 
UINT CCJToolBar::GetButtonStyle(int nIndex) const 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	TBBUTTON button; 
	GetButton(nIndex, &button); 
	return MAKELONG(button.fsStyle, button.fsState); 
} 
 
void CCJToolBar::SetButtonStyle(int nIndex, UINT nStyle) 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	TBBUTTON button; 
	GetButton(nIndex, &button); 
	if (button.fsStyle != (BYTE)LOWORD(nStyle) || button.fsState != (BYTE)HIWORD(nStyle)) 
	{ 
		button.fsStyle = (BYTE)LOWORD(nStyle); 
		button.fsState = (BYTE)HIWORD(nStyle); 
		SetButton(nIndex, &button); 
		m_bDelayedButtonLayout = TRUE; 
	} 
} 
 
#define CX_OVERLAP  0 
 
CSize CCJToolBar::CalcSize(TBBUTTON* pData, int nCount) 
{ 
	ASSERT(pData != NULL && nCount > 0); 
 
	CPoint cur(0,0); 
	CSize sizeResult(0,0); 
	int cyTallestOnRow = 0; 
 
	for (int i = 0; i < nCount; i++) 
	{ 
		if (pData[i].fsState & TBSTATE_HIDDEN) 
			continue; 
 
		// **PD** 
		// Load actual size of button into a local variable 
		// called m_sizeButton. C++ will use this instead of 
		// CToolBar::m_sizeButton. 
		// 
		CSize m_sizeButton = GetButtonSize(pData, i); 
 
		// **PD** 
		// I also changed the logic below to be more correct. 
		cyTallestOnRow = max(cyTallestOnRow, m_sizeButton.cy); 
		sizeResult.cx = max(cur.x + m_sizeButton.cx, sizeResult.cx); 
		sizeResult.cy = max(cur.y + m_sizeButton.cy, sizeResult.cy); 
 
		cur.x += m_sizeButton.cx - CX_OVERLAP; 
 
		if (pData[i].fsState & TBSTATE_WRAP) 
		{ 
			cur.x = 0; 
			cur.y += cyTallestOnRow; 
			cyTallestOnRow = 0; 
			if (pData[i].fsStyle & TBSTYLE_SEP) 
				cur.y += m_sizeButton.cy; 
		} 
	} 
	return sizeResult; 
} 
 
int CCJToolBar::WrapToolBar(TBBUTTON* pData, int nCount, int nWidth) 
{ 
	ASSERT(pData != NULL && nCount > 0); 
 
	int nResult = 0; 
	int x = 0; 
	for (int i = 0; i < nCount; i++) 
	{ 
		pData[i].fsState &= ~TBSTATE_WRAP; 
 
		if (pData[i].fsState & TBSTATE_HIDDEN) 
			continue; 
 
		int dx, dxNext; 
 
		// **PD** 
		// Load actual size of button into a local variable 
		// called m_sizeButton. C++ will use this instead of 
		// CToolBar::m_sizeButton. 
		// 
		CSize m_sizeButton = GetButtonSize(pData, i); 
 
		dx = m_sizeButton.cx; 
		dxNext = dx - CX_OVERLAP; 
 
		if (x + dx > nWidth) 
		{ 
			BOOL bFound = FALSE; 
			for (int j = i; j >= 0  &&  !(pData[j].fsState & TBSTATE_WRAP); j--) 
			{ 
				// Find last separator that isn't hidden 
				// a separator that has a command ID is not 
				// a separator, but a custom control. 
				if ((pData[j].fsStyle & TBSTYLE_SEP) && 
					(pData[j].idCommand == 0) && 
					!(pData[j].fsState & TBSTATE_HIDDEN)) 
				{ 
					bFound = TRUE; i = j; x = 0; 
					pData[j].fsState |= TBSTATE_WRAP; 
					nResult++; 
					break; 
				} 
			} 
			if (!bFound) 
			{ 
				for (int j = i - 1; j >= 0 && !(pData[j].fsState & TBSTATE_WRAP); j--) 
				{ 
					// Never wrap anything that is hidden, 
					// or any custom controls 
					if ((pData[j].fsState & TBSTATE_HIDDEN) || 
						((pData[j].fsStyle & TBSTYLE_SEP) && 
						(pData[j].idCommand != 0))) 
						continue; 
 
					bFound = TRUE; i = j; x = 0; 
					pData[j].fsState |= TBSTATE_WRAP; 
					nResult++; 
					break; 
				} 
				if (!bFound) 
					x += dxNext; 
			} 
		} 
		else 
			x += dxNext; 
	} 
	return nResult + 1; 
} 
 
void CCJToolBar::SizeToolBar(TBBUTTON* pData, int nCount, int nLength, BOOL bVert) 
{ 
	ASSERT(pData != NULL && nCount > 0); 
 
	if (!bVert) 
	{ 
		int nMin, nMax, nTarget, nCurrent, nMid; 
 
		// Wrap ToolBar as specified 
		nMax = nLength; 
		nTarget = WrapToolBar(pData, nCount, nMax); 
 
		// Wrap ToolBar vertically 
		nMin = 0; 
		nCurrent = WrapToolBar(pData, nCount, nMin); 
 
		if (nCurrent != nTarget) 
		{ 
			while (nMin < nMax) 
			{ 
				nMid = (nMin + nMax) / 2; 
				nCurrent = WrapToolBar(pData, nCount, nMid); 
 
				if (nCurrent == nTarget) 
					nMax = nMid; 
				else 
				{ 
					if (nMin == nMid) 
					{ 
						WrapToolBar(pData, nCount, nMax); 
						break; 
					} 
					nMin = nMid; 
				} 
			} 
		} 
		CSize size = CalcSize(pData, nCount); 
		WrapToolBar(pData, nCount, size.cx); 
	} 
	else 
	{ 
		CSize sizeMax, sizeMin, sizeMid; 
 
		// Wrap ToolBar vertically 
		WrapToolBar(pData, nCount, 0); 
		sizeMin = CalcSize(pData, nCount); 
 
		// Wrap ToolBar horizontally 
		WrapToolBar(pData, nCount, 32767); 
		sizeMax = CalcSize(pData, nCount); 
 
		while (sizeMin.cx < sizeMax.cx) 
		{ 
			sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2; 
			WrapToolBar(pData, nCount, sizeMid.cx); 
			sizeMid = CalcSize(pData, nCount); 
 
			if (nLength < sizeMid.cy) 
			{ 
				if (sizeMin == sizeMid) 
				{ 
					WrapToolBar(pData, nCount, sizeMax.cx); 
					return; 
				} 
				sizeMin = sizeMid; 
			} 
			else if (nLength > sizeMid.cy) 
				sizeMax = sizeMid; 
			else 
				return; 
		} 
	} 
} 
 
CSize CCJToolBar::CalcLayout(DWORD dwMode, int nLength) 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
	if (dwMode & LM_HORZDOCK) 
		ASSERT(dwMode & LM_HORZ); 
 
	int nCount; 
	TBBUTTON* pData = NULL; 
	CSize sizeResult(0,0); 
 
	//BLOCK: Load Buttons 
	{ 
		nCount = DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
		if (nCount != 0) 
		{ 
			int i; 
			pData = new TBBUTTON[nCount]; 
			for (i = 0; i < nCount; i++) 
				GetButton(i, &pData[i]); 
		} 
	} 
 
	if (nCount > 0) 
	{ 
		if (!(m_dwStyle & CBRS_SIZE_FIXED)) 
		{ 
			BOOL bDynamic = m_dwStyle & CBRS_SIZE_DYNAMIC; 
 
			if (bDynamic && (dwMode & LM_MRUWIDTH)) 
				SizeToolBar(pData, nCount, m_nMRUWidth); 
			else if (bDynamic && (dwMode & LM_HORZDOCK)) 
				SizeToolBar(pData, nCount, 32767); 
			else if (bDynamic && (dwMode & LM_VERTDOCK)) 
				SizeToolBar(pData, nCount, 0); 
			else if (bDynamic && (nLength != -1)) 
			{ 
				CRect rect; rect.SetRectEmpty(); 
				CalcInsideRect(rect, (dwMode & LM_HORZ)); 
				BOOL bVert = (dwMode & LM_LENGTHY); 
				int nLen = nLength + (bVert ? rect.Height() : rect.Width()); 
 
				SizeToolBar(pData, nCount, nLen, bVert); 
			} 
			else if (bDynamic && (m_dwStyle & CBRS_FLOATING)) 
				SizeToolBar(pData, nCount, m_nMRUWidth); 
			else 
				SizeToolBar(pData, nCount, (dwMode & LM_HORZ) ? 32767 : 0); 
		} 
 
		sizeResult = CalcSize(pData, nCount); 
 
		if (dwMode & LM_COMMIT) 
		{ 
			CJX_CONTROLPOS* pControl = NULL; 
			int nControlCount = 0; 
			BOOL bIsDelayed = m_bDelayedButtonLayout; 
			m_bDelayedButtonLayout = FALSE; 
 
			for (int i = 0; i < nCount; i++) 
				if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0)) 
					nControlCount++; 
 
			if (nControlCount > 0) 
			{ 
				pControl = new CJX_CONTROLPOS[nControlCount]; 
				nControlCount = 0; 
 
				for(int i = 0; i < nCount; i++) 
				{ 
					if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0)) 
					{ 
						pControl[nControlCount].nIndex = i; 
						pControl[nControlCount].nID = pData[i].idCommand; 
 
						CRect rect; 
						GetItemRect(i, &rect); 
						ClientToScreen(&rect); 
						pControl[nControlCount].rectOldPos = rect; 
 
						nControlCount++; 
					} 
				} 
			} 
 
			if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC)) 
				m_nMRUWidth = sizeResult.cx; 
			for (i = 0; i < nCount; i++) 
				SetButton(i, &pData[i]); 
 
			if (nControlCount > 0) 
			{ 
				for (int i = 0; i < nControlCount; i++) 
				{ 
					CWnd* pWnd = GetDlgItem(pControl[i].nID); 
					if (pWnd != NULL) 
					{ 
						CRect rect; 
						pWnd->GetWindowRect(&rect); 
						CPoint pt = rect.TopLeft() - pControl[i].rectOldPos.TopLeft(); 
						GetItemRect(pControl[i].nIndex, &rect); 
						pt = rect.TopLeft() + pt; 
						pWnd->SetWindowPos(NULL, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); 
					} 
				} 
				delete[] pControl; 
			} 
			m_bDelayedButtonLayout = bIsDelayed; 
		} 
		delete[] pData; 
	} 
 
	//BLOCK: Adjust Margins 
	{ 
		CRect rect; rect.SetRectEmpty(); 
		CalcInsideRect(rect, (dwMode & LM_HORZ)); 
		sizeResult.cy -= rect.Height(); 
		sizeResult.cx -= rect.Width(); 
 
		CSize size = CCJToolBarBase::CalcFixedLayout((dwMode & LM_STRETCH), (dwMode & LM_HORZ)); 
		sizeResult.cx = max(sizeResult.cx, size.cx); 
		sizeResult.cy = max(sizeResult.cy, size.cy); 
	} 
	return sizeResult; 
} 
 
CSize CCJToolBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz) 
{ 
	DWORD dwMode = bStretch ? LM_STRETCH : 0; 
	dwMode |= bHorz ? LM_HORZ : 0; 
 
	return CalcLayout(dwMode); 
} 
 
CSize CCJToolBar::CalcDynamicLayout(int nLength, DWORD dwMode) 
{ 
	if ((nLength == -1) && !(dwMode & LM_MRUWIDTH) && !(dwMode & LM_COMMIT) && 
		((dwMode & LM_HORZDOCK) || (dwMode & LM_VERTDOCK))) 
	{ 
		return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZDOCK); 
	} 
	return CalcLayout(dwMode, nLength); 
} 
 
void CCJToolBar::GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	TBBUTTON button; 
	GetButton(nIndex, &button); 
	nID = button.idCommand; 
	nStyle = MAKELONG(button.fsStyle, button.fsState); 
	iImage = button.iBitmap; 
} 
 
void CCJToolBar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage) 
{ 
	ASSERT_VALID(this); 
 
	TBBUTTON button; 
	GetButton(nIndex, &button); 
	TBBUTTON save; 
	memcpy(&save, &button, sizeof(save)); 
	button.idCommand = nID; 
	button.iBitmap = iImage; 
	button.fsStyle = (BYTE)LOWORD(nStyle); 
	button.fsState = (BYTE)HIWORD(nStyle); 
	if (memcmp(&save, &button, sizeof(save)) != 0) 
	{ 
		SetButton(nIndex, &button); 
		m_bDelayedButtonLayout = TRUE; 
	} 
} 
 
int CCJToolBar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const 
{ 
	ASSERT_VALID(this); 
	ASSERT(::IsWindow(m_hWnd)); 
 
	// check child windows first by calling CCJToolBarBase 
	int nHit = CCJToolBarBase::OnToolHitTest(point, pTI); 
	if (nHit != -1) 
		return nHit; 
 
	// now hit test against CCJToolBar buttons 
	CCJToolBar* pBar = (CCJToolBar*)this; 
	int nButtons = (int)pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
	for (int i = 0; i < nButtons; i++) 
	{ 
		CRect rect; 
		TBBUTTON button; 
		if (pBar->DefWindowProc(TB_GETITEMRECT, i, (LPARAM)&rect)) 
		{ 
			++rect.bottom; 
			++rect.right; 
			if (rect.PtInRect(point) && 
				pBar->DefWindowProc(TB_GETBUTTON, i, (LPARAM)&button) && 
				!(button.fsStyle & TBSTYLE_SEP)) 
			{ 
				int nHit = GetItemID(i); 
				if (pTI != NULL && pTI->cbSize >= sizeof(AFX_OLDTOOLINFO)) 
				{ 
					pTI->hwnd = m_hWnd; 
					pTI->rect = rect; 
					pTI->uId = nHit; 
					pTI->lpszText = LPSTR_TEXTCALLBACK; 
				} 
				// found matching rect, return the ID of the button 
				return nHit != 0 ? nHit : -1; 
			} 
		} 
	} 
	return -1; 
} 
 
BOOL CCJToolBar::SetButtonText(int nIndex, LPCTSTR lpszText) 
{ 
	// attempt to lookup string index in map 
	int nString = -1; 
	void* p; 
	if (m_pStringMap != NULL && m_pStringMap->Lookup(lpszText, p)) 
		nString = (int)p; 
 
	// add new string if not already in map 
	if (nString == -1) 
	{ 
		// initialize map if necessary 
		if (m_pStringMap == NULL) 
			m_pStringMap = new CMapStringToPtr; 
 
		// add new string to toolbar list 
		CString strTemp(lpszText, lstrlen(lpszText)+1); 
		nString = (int)DefWindowProc(TB_ADDSTRING, 0, (LPARAM)(LPCTSTR)strTemp); 
		if (nString == -1) 
			return FALSE; 
 
		// cache string away in string map 
		m_pStringMap->SetAt(lpszText, (void*)nString); 
		ASSERT(m_pStringMap->Lookup(lpszText, p)); 
	} 
 
	// change the toolbar button description 
	TBBUTTON button; 
	GetButton(nIndex, &button); 
	button.iString = nString; 
	SetButton(nIndex, &button); 
 
	return TRUE; 
} 
 
CString CCJToolBar::GetButtonText(int nIndex) const 
{ 
	CString strResult; 
	GetButtonText(nIndex, strResult); 
	return strResult; 
} 
 
void CCJToolBar::GetButtonText(int nIndex, CString& rString) const 
{ 
	if (m_pStringMap != NULL) 
	{ 
		// get button information (need button.iString) 
		TBBUTTON button; 
		GetButton(nIndex, &button); 
 
		// look in map for matching iString 
		POSITION pos = m_pStringMap->GetStartPosition(); 
		CString str; void* p; 
		while (pos) 
		{ 
			m_pStringMap->GetNextAssoc(pos, str, p); 
			if ((int)p == button.iString) 
			{ 
				rString = str; 
				return; 
			} 
		} 
	} 
	rString.Empty(); 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJToolBar message handlers 
 
BEGIN_MESSAGE_MAP(CCJToolBar, CCJToolBarBase) 
	//{{AFX_MSG_MAP(CCJToolBar) 
	ON_WM_NCHITTEST() 
	ON_WM_NCPAINT() 
	ON_WM_PAINT() 
	ON_WM_NCCALCSIZE() 
	ON_WM_WINDOWPOSCHANGING() 
	ON_WM_WINDOWPOSCHANGED() 
	ON_WM_SYSCOLORCHANGE() 
	ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize) 
	ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize) 
	ON_WM_NCCREATE() 
	ON_WM_ERASEBKGND() 
	ON_WM_DESTROY() 
	ON_WM_RBUTTONDOWN() 
	ON_MESSAGE(WM_SETTINGCHANGE, OnPreserveZeroBorderHelper) 
	//}}AFX_MSG_MAP 
	ON_MESSAGE(WM_SETFONT, OnPreserveZeroBorderHelper) 
	//ON_NOTIFY_REFLECT(TBN_DROPDOWN,		 OnToolBarBtnDropDown) 
	ON_NOTIFY_REFLECT(TBN_BEGINADJUST,	 OnToolBarBeginAdjust) 
	ON_NOTIFY_REFLECT(TBN_BEGINDRAG,	 OnToolBarBeginDrag) 
	ON_NOTIFY_REFLECT(TBN_CUSTHELP,		 OnToolBarCustomHelp) 
	ON_NOTIFY_REFLECT(TBN_ENDADJUST,	 OnToolBarEndAdjust) 
	ON_NOTIFY_REFLECT(TBN_ENDDRAG,		 OnToolBarEndDrag) 
	ON_NOTIFY_REFLECT(TBN_GETBUTTONINFO, OnToolBarGetButtonInfo) 
	ON_NOTIFY_REFLECT(TBN_QUERYDELETE,	 OnToolBarQueryDelete) 
	ON_NOTIFY_REFLECT(TBN_QUERYINSERT,	 OnToolBarQueryInsert) 
	ON_NOTIFY_REFLECT(TBN_RESET,		 OnToolBarReset) 
	ON_NOTIFY_REFLECT(TBN_TOOLBARCHANGE, OnToolBarChange) 
END_MESSAGE_MAP() 
 
//////////////// 
// Override to avoid MFC in case I'm inside a ReBar 
// 
BOOL CCJToolBar::OnEraseBkgnd(CDC*) 
{ 
	return (BOOL)Default(); 
} 
 
UINT CCJToolBar::OnNcHitTest(CPoint) 
{ 
	return HTCLIENT; 
} 
 
////////////////// 
// Calcluate size of client area. Adds room for grippers 
// 
void CCJToolBar::OnNcCalcSize(BOOL /*bCalcValidRects*/, NCCALCSIZE_PARAMS* lpncsp) 
{ 
	// calculate border space (will add to top/bottom, subtract from right/bottom) 
	CRect rect; rect.SetRectEmpty(); 
	BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0; 
	CCJToolBarBase::CalcInsideRect(rect, bHorz); 
	ASSERT(_ComCtlVersion != -1); 
	ASSERT(_ComCtlVersion >= VERSION_IE4 || rect.top >= 2); 
 
	// adjust non-client area for border space 
	lpncsp->rgrc[0].left += rect.left; 
	lpncsp->rgrc[0].top += rect.top; 
	// previous versions of COMCTL32.DLL had a built-in 2 pixel border 
	if (_ComCtlVersion < VERSION_IE4) 
		lpncsp->rgrc[0].top -= 2; 
	lpncsp->rgrc[0].right += rect.right; 
	lpncsp->rgrc[0].bottom += rect.bottom; 
} 
 
////////////////// 
// **PD** 
// Part 2 of correction for MFC is to recalculate everything when the bar 
// goes from docked to undocked because the AdjustSize calculation happens 
// when the bar is in the old state, and thus wrong. After the bar is 
// docked/undocked, I'll recalculate with the new style and commit the change. 
// 
void CCJToolBar::OnBarStyleChange(DWORD dwOldStyle, DWORD dwNewStyle) 
{ 
	// a dynamically resizeable toolbar can not have the CBRS_FLOAT_MULTI 
	ASSERT(!((dwNewStyle & CBRS_SIZE_DYNAMIC) && 
		(m_dwDockStyle & CBRS_FLOAT_MULTI))); 
	 
	// a toolbar can not be both dynamic and fixed in size 
	ASSERT (!((dwNewStyle & CBRS_SIZE_FIXED) && 
		(dwNewStyle & CBRS_SIZE_DYNAMIC))); 
	 
	// CBRS_SIZE_DYNAMIC can not be disabled once it has been enabled 
	ASSERT (((dwOldStyle & CBRS_SIZE_DYNAMIC) == 0) || 
		((dwNewStyle & CBRS_SIZE_DYNAMIC) != 0)); 
	 
	if (m_hWnd != NULL && 
		((dwOldStyle & CBRS_BORDER_ANY) != (dwNewStyle & CBRS_BORDER_ANY))) 
	{ 
		// recalc non-client area when border styles change 
		SetWindowPos(NULL, 0, 0, 0, 0, 
			SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME); 
	} 
	m_bDelayedButtonLayout = TRUE; 
 
	if (dwOldStyle != dwNewStyle) { 
		DWORD dwMode = 0; 
		if ((dwNewStyle & CBRS_SIZE_DYNAMIC) && (dwNewStyle & CBRS_FLOATING)) 
			dwMode = LM_HORZ | LM_MRUWIDTH; 
		else if (dwNewStyle & CBRS_ORIENT_HORZ) 
			dwMode = LM_HORZ | LM_HORZDOCK; 
		else 
			dwMode =  LM_VERTDOCK; 
 
		CalcDynamicLayout(-1, dwMode | LM_COMMIT); 
	} 
} 
 
void CCJToolBar::OnNcPaint() 
{ 
	EraseNonClient(); 
} 
 
void CCJToolBar::OnWindowPosChanging(LPWINDOWPOS lpwndpos) 
{ 
	// not necessary to invalidate the borders 
	DWORD dwStyle = m_dwStyle; 
	m_dwStyle &= ~(CBRS_BORDER_ANY); 
	CCJToolBarBase::OnWindowPosChanging(lpwndpos); 
	m_dwStyle = dwStyle; 
} 
 
void CCJToolBar::OnPaint() 
{ 
	if (m_bDelayedButtonLayout) 
		Layout(); 
 
	Default(); 
} 
 
LRESULT CCJToolBar::OnSetButtonSize(WPARAM, LPARAM lParam) 
{ 
	return OnSetSizeHelper(m_sizeButton, lParam); 
} 
 
LRESULT CCJToolBar::OnSetBitmapSize(WPARAM, LPARAM lParam) 
{ 
	return OnSetSizeHelper(m_sizeImage, lParam); 
} 
 
LRESULT CCJToolBar::OnSetSizeHelper(CSize& size, LPARAM lParam) 
{ 
	//WINBUG: The IE4 version of COMCTL32.DLL supports a zero border, but 
	//  only if TBSTYLE_TRANSPARENT is on during the the TB_SETBITMAPSIZE 
	//  and/or TB_SETBUTTONSIZE messages.  In order to enable this feature 
	//  all the time (so we get consistent border behavior, dependent only 
	//  on the version of COMCTL32.DLL) we turn on TBSTYLE_TRANSPARENT 
	//  whenever these messages go through.  It would be nice that in a 
	//  future version, the system toolbar would just allow you to set 
	//  the top and left borders to anything you please. 
 
	BOOL bModify = FALSE; 
	ASSERT(_ComCtlVersion != -1); 
	DWORD dwStyle = 0; 
	if (_ComCtlVersion >= VERSION_IE4) 
	{ 
		dwStyle = GetStyle(); 
		bModify = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT); 
	} 
 
	LRESULT lResult = Default(); 
	if (lResult) 
		size = lParam; 
 
	if (bModify) 
		SetWindowLong(m_hWnd, GWL_STYLE, dwStyle); 
 
	return lResult; 
} 
 
LRESULT CCJToolBar::OnPreserveZeroBorderHelper(WPARAM, LPARAM lParam) 
{ 
	UNUSED_ALWAYS( lParam ); 
 
	BOOL bModify = FALSE; 
	ASSERT(_ComCtlVersion != -1); 
	DWORD dwStyle = 0; 
	if (_ComCtlVersion >= VERSION_IE4) 
	{ 
		dwStyle = GetStyle(); 
		bModify = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT); 
	} 
 
	LRESULT lResult = Default(); 
 
	if (bModify) 
		SetWindowLong(m_hWnd, GWL_STYLE, dwStyle); 
 
	return lResult; 
} 
 
void CCJToolBar::OnSysColorChange() 
{ 
	// re-color bitmap for toolbar 
	if (m_hInstImageWell != NULL && m_hbmImageWell != NULL) 
	{ 
		HBITMAP hbmNew; 
		hbmNew = _LoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell); 
		if (hbmNew != NULL) 
			AddReplaceBitmap(hbmNew); 
	} 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJToolBar idle update through CCJToolCmdUI class 
 
class CCJToolCmdUI : public CCmdUI        // class private to this file ! 
{ 
public: // re-implementations only 
	virtual void Enable(BOOL bOn); 
	virtual void SetCheck(int nCheck); 
	virtual void SetText(LPCTSTR lpszText); 
}; 
 
void CCJToolCmdUI::Enable(BOOL bOn) 
{ 
	m_bEnableChanged = TRUE; 
	CCJToolBar* pToolBar = (CCJToolBar*)m_pOther; 
	ASSERT(pToolBar != NULL); 
	ASSERT_KINDOF(CCJToolBar, pToolBar); 
	ASSERT(m_nIndex < m_nIndexMax); 
 
	UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) & ~TBBS_DISABLED; 
	if (!bOn) 
	{ 
		nNewStyle |= TBBS_DISABLED; 
		// WINBUG: If a button is currently pressed and then is disabled 
		// COMCTL32.DLL does not unpress the button, even after the mouse 
		// button goes up!  We work around this bug by forcing TBBS_PRESSED 
		// off when a button is disabled. 
		nNewStyle &= ~TBBS_PRESSED; 
	} 
	ASSERT(!(nNewStyle & TBBS_SEPARATOR)); 
	pToolBar->SetButtonStyle(m_nIndex, nNewStyle); 
} 
 
void CCJToolCmdUI::SetCheck(int nCheck) 
{ 
	ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate 
	CCJToolBar* pToolBar = (CCJToolBar*)m_pOther; 
	ASSERT(pToolBar != NULL); 
	ASSERT_KINDOF(CCJToolBar, pToolBar); 
	ASSERT(m_nIndex < m_nIndexMax); 
 
	UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) & 
				~(TBBS_CHECKED | TBBS_INDETERMINATE); 
	if (nCheck == 1) 
		nNewStyle |= TBBS_CHECKED; 
	else if (nCheck == 2) 
		nNewStyle |= TBBS_INDETERMINATE; 
	ASSERT(!(nNewStyle & TBBS_SEPARATOR)); 
	pToolBar->SetButtonStyle(m_nIndex, nNewStyle | TBBS_CHECKBOX); 
} 
 
void CCJToolCmdUI::SetText(LPCTSTR) 
{ 
	// ignore it 
} 
 
void CCJToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) 
{ 
	CCJToolCmdUI state; 
	state.m_pOther = this; 
 
	state.m_nIndexMax = (UINT)DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
	for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) 
	{ 
		// get buttons state 
		TBBUTTON button; 
		GetButton(state.m_nIndex, &button); 
		state.m_nID = button.idCommand; 
 
		// ignore separators 
		if (!(button.fsStyle & TBSTYLE_SEP)) 
		{ 
			// allow reflections 
			if (CWnd::OnCmdMsg(0, 
				MAKELONG((int)CN_UPDATE_COMMAND_UI, WM_COMMAND+WM_REFLECT_BASE), 
				&state, NULL)) 
				continue; 
 
			// allow the toolbar itself to have update handlers 
			if (CWnd::OnCmdMsg(state.m_nID, CN_UPDATE_COMMAND_UI, &state, NULL)) 
				continue; 
 
			// allow the owner to process the update 
			state.DoUpdate(pTarget, bDisableIfNoHndler); 
		} 
	} 
 
	// update the dialog controls added to the toolbar 
	UpdateDialogControls(pTarget, bDisableIfNoHndler); 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJToolBar diagnostics 
 
#ifdef _DEBUG 
void CCJToolBar::AssertValid() const 
{ 
	// Note: CCJToolBarBase::AssertValid is not called because it checks for 
	//  m_nCount and m_pData to be in sync, which they are not in CCJToolBar. 
 
#ifndef _VC_VERSION_5 
	ASSERT(m_hbmImageWell == NULL || 
		( afxData.bWin95 || ::GetObjectType(m_hbmImageWell) == OBJ_BITMAP)); 
#else 
	ASSERT(m_hbmImageWell == NULL || 
		( ::GetObjectType(m_hbmImageWell) == OBJ_BITMAP)); 
#endif 
 
	if (m_hInstImageWell != NULL && m_hbmImageWell != NULL) 
		ASSERT(m_hRsrcImageWell != NULL); 
} 
 
void CCJToolBar::Dump(CDumpContext& dc) const 
{ 
	CCJToolBarBase::Dump(dc); 
 
	dc << "m_hbmImageWell = " << (UINT)m_hbmImageWell; 
	dc << "\nm_hInstImageWell = " << (UINT)m_hInstImageWell; 
	dc << "\nm_hRsrcImageWell = " << (UINT)m_hRsrcImageWell; 
	dc << "\nm_sizeButton = " << m_sizeButton; 
	dc << "\nm_sizeImage = " << m_sizeImage; 
 
	if (dc.GetDepth() > 0) 
	{ 
		CCJToolBar* pBar = (CCJToolBar*)this; 
		int nCount = pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
		for (int i = 0; i < nCount; i++) 
		{ 
			TBBUTTON button; 
			GetButton(i, &button); 
			dc << "\ntoolbar button[" << i << "] = {"; 
			dc << "\n\tnID = " << button.idCommand; 
			dc << "\n\tnStyle = " << MAKELONG(button.fsStyle, button.fsState); 
			if (button.fsStyle & TBSTYLE_SEP) 
				dc << "\n\tiImage (separator width) = " << button.iBitmap; 
			else 
				dc <<"\n\tiImage (bitmap image index) = " << button.iBitmap; 
			dc << "\n}"; 
		} 
	} 
 
	dc << "\n"; 
} 
#endif 
 
#ifdef AFX_INIT_SEG 
#pragma code_seg(AFX_INIT_SEG) 
#endif 
 
IMPLEMENT_DYNAMIC(CCJToolBar, CCJToolBarBase) 
 
/////////////////////////////////////////////////////////////////////////////