// CJToolBar.cpp : implementation file
// Copyright © 1998-1999 CodeJock.com, All Rights Reserved.
// See ReadMe.txt for TERMS OF USE.
//
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
 *
 * $Date: 1999/11/30 21:45:49 $
 * $Revision: 1.1.1.3 $
 * $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("CJToolBar %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
//
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);
}

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

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