Click here to Skip to main content
15,881,248 members
Articles / Desktop Programming / MFC

XTrueColorToolBar - True-color toolbar with support for Office-style color button

Rate me:
Please Sign up or sign in to vote.
4.69/5 (20 votes)
10 Jan 2008CPOL5 min read 72.6K   4.2K   63  
XTrueColorToolBar is an MFC class based on CToolBar that provides support for true-color bitmaps, with optional support for an Office-style color picker button.
// XTrueColorToolBar.cpp  Version 1.0 - article available at www.codeproject.com
//
// Author:  Hans Dietrich
//          hdietrich@gmail.com
//
// Description:
//     XTrueColorToolBar is an MFC class derived from CToolBar that provides 
//     support for true-color bitmaps, with optional support for an 
//     Office-style color picker button.  It is based on Peter Lee's
//     article "Full-Featured 24-bit Color Toolbar", which you can find
//     at http://www.codeguru.com/Cpp/controls/toolbar/article.php/c2537.
//
// History
//     Version 1.0 - 2007 December 17
//     - Initial public release
//
// License:
//     This software is released into the public domain.  You are free to use
//     it in any way you like, except that you may not sell this source code.
//
//     This software is provided "as is" with no expressed or implied warranty.
//     I accept no liability for any damage or loss of business that this 
//     software may cause.
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XTrueColorToolBar.h"

#pragma warning(disable : 4996)	// disable bogus deprecation warning


//=============================================================================
CXTrueColorToolBar::CXTrueColorToolBar()
//=============================================================================
{
	m_nNumImages        = 0;
	m_nImageWidth       = 16;
	m_nImageHeight      = 16;
	m_nToolBarBitDepth  = ILC_COLOR24;
	m_crCurrentColor    = RGB(0,0,0);
	m_crMaskColor       = RGB(0,0,1);
	m_crBackgroundColor = RGB(192,192,192);
}

//=============================================================================
// Create copy of toolbar image list
BOOL CXTrueColorToolBar::CreateTemplate(COLORREF rgb)
//=============================================================================
{
	BOOL rc = FALSE;

	SetMaskColor(rgb);

	if (m_ImageTemplate.GetSafeHandle() != NULL)
		m_ImageTemplate.DeleteImageList();

	CToolBarCtrl& ctrl = GetToolBarCtrl();
	CImageList *pList = ctrl.GetImageList();

	if (pList)
	{
		// create copy of toolbar image list
		rc =  m_ImageTemplate.Create(pList);

		if (!rc)
		{
			TRACE(_T("ERROR - failed to create image template\n"));
			ASSERT(FALSE);
		}
	}
	else
	{
		TRACE(_T("ERROR - failed to get toolbar image list\n"));
		ASSERT(pList);
	}

	return rc;
}

//=============================================================================
// Find every pixel of the default background color in the specified
// bitmap and set each one to the user's button color.
void CXTrueColorToolBar::ReplaceBackgroundColor(CBitmap& bm, 
												COLORREF OldColor, 
												COLORREF NewColor)
//=============================================================================
{
	// figure out how many pixels there are in the bitmap
	BITMAP bmInfo;
	VERIFY(bm.GetBitmap(&bmInfo));
	
	// add support for additional bit depths here if you choose
	VERIFY(bmInfo.bmBitsPixel == 24);
	VERIFY(bmInfo.bmWidthBytes == (bmInfo.bmWidth * 3));

	UINT nPixels = bmInfo.bmHeight * bmInfo.bmWidth;

	// get a pointer to the pixels
    DIBSECTION ds;

    VERIFY(bm.GetObject(sizeof(DIBSECTION), &ds) == sizeof(DIBSECTION));

	RGBTRIPLE *pixels = reinterpret_cast<RGBTRIPLE*>(ds.dsBm.bmBits);
	VERIFY(pixels);

	RGBTRIPLE NewBackgroundColor = {
		GetBValue(NewColor), GetGValue(NewColor), GetRValue(NewColor) };

	RGBTRIPLE OldBackgroundColor = {
		GetBValue(OldColor), GetGValue(OldColor), GetRValue(OldColor) };

	// search through the pixels, substituting the new background
	// color for any pixel that has the old background color

	int nCount = 0;
	for (UINT i = 0; i < nPixels; ++i)
	{
		if (pixels[i].rgbtBlue  == OldBackgroundColor.rgbtBlue &&
			pixels[i].rgbtGreen == OldBackgroundColor.rgbtGreen &&
			pixels[i].rgbtRed   == OldBackgroundColor.rgbtRed)
		{
			pixels[i] = NewBackgroundColor;
			nCount++;
		}
	}
	TRACE(_T("ReplaceColor: replaced %d pixels\n"), nCount);
}

//=============================================================================
// Find every pixel set to old color in mask bitmap and set corresponding
// pixel in toolbar bitmap to new color.
void CXTrueColorToolBar::ReplaceMaskColor(CBitmap& bm1,		// toolbar bitmap
										  CBitmap& bm2,		// mask bitmap
										  COLORREF OldColor, 
										  COLORREF NewColor)
//=============================================================================
{
	// figure out how many pixels there are in the bitmap
	BITMAP bmInfo1, bmInfo2;
	VERIFY(bm1.GetBitmap(&bmInfo1));
	VERIFY(bm2.GetBitmap(&bmInfo2));
	
	// add support for additional bit depths here if you choose
	VERIFY(bmInfo1.bmBitsPixel == 24);
	VERIFY(bmInfo1.bmWidthBytes == (bmInfo1.bmWidth * 3));
	VERIFY(bmInfo2.bmBitsPixel == 24);
	VERIFY(bmInfo2.bmWidthBytes == (bmInfo2.bmWidth * 3));

	UINT nPixels1 = bmInfo1.bmHeight * bmInfo1.bmWidth;
	UINT nPixels2 = bmInfo2.bmHeight * bmInfo2.bmWidth;
	//TRACE(_T("bmInfo1.bmHeight=%d  bmInfo1.bmWidth=%d\n"), bmInfo1.bmHeight, bmInfo1.bmWidth);
	//TRACE(_T("bmInfo2.bmHeight=%d  bmInfo2.bmWidth=%d\n"), bmInfo2.bmHeight, bmInfo2.bmWidth);
	//TRACE(_T("nPixels1=%d  nPixels2=%d\n"), nPixels1, nPixels2);
	ASSERT(nPixels1 == nPixels2);

	// get a pointer to the pixels
    DIBSECTION ds1, ds2;

    VERIFY(bm1.GetObject(sizeof(DIBSECTION), &ds1) == sizeof(DIBSECTION));
    VERIFY(bm2.GetObject(sizeof(DIBSECTION), &ds2) == sizeof(DIBSECTION));

	RGBTRIPLE *pixels1 = reinterpret_cast<RGBTRIPLE*>(ds1.dsBm.bmBits);
	VERIFY(pixels1 != NULL);
	RGBTRIPLE *pixels2 = reinterpret_cast<RGBTRIPLE*>(ds2.dsBm.bmBits);
	VERIFY(pixels2 != NULL);

	RGBTRIPLE NewMaskColor = {
		GetBValue(NewColor), GetGValue(NewColor), GetRValue(NewColor) };

	RGBTRIPLE OldMaskColor = {
		GetBValue(OldColor), GetGValue(OldColor), GetRValue(OldColor) };

	// search through the pixels, substituting the new mask
	// color for any pixel that has the old mask color

	int nCount = 0;
	for (UINT i = 0; i < nPixels1; ++i)
	{
		if (pixels2[i].rgbtBlue  == OldMaskColor.rgbtBlue &&
			pixels2[i].rgbtGreen == OldMaskColor.rgbtGreen &&
			pixels2[i].rgbtRed   == OldMaskColor.rgbtRed)
		{
			pixels1[i] = NewMaskColor;
			nCount++;
		}
	}
	TRACE(_T("ReplaceMaskColor: replaced %d pixels\n"), nCount);
}

//=============================================================================
// create an image list for the specified BMP resource
void CXTrueColorToolBar::MakeToolbarImageList(UINT inBitmapID, 
											  CImageList& outImageList)
//=============================================================================
{
	CBitmap bm;

	// If we use CBitmap::LoadBitmap() to load the bitmap, the colors
	// will be reduced to the bit depth of the main screen and we won't
	// be able to access the pixels directly. To avoid those problems,
	// we'll load the bitmap as a DIBSection instead and attach the
	// DIBSection to the CBitmap.
	VERIFY(bm.Attach(::LoadImage(
		::AfxFindResourceHandle(MAKEINTRESOURCE(inBitmapID), RT_BITMAP),
								MAKEINTRESOURCE(inBitmapID), IMAGE_BITMAP, 0, 0,
								(LR_DEFAULTSIZE | LR_CREATEDIBSECTION))));

	// replace the background color in the toolbar bitmap with the 
	// current system toolbar background color
	ReplaceBackgroundColor(bm, m_crBackgroundColor, GetSysColor(COLOR_BTNFACE));

	// create a 24 bit image list with the same dimensions and number
	// of buttons as the toolbar
	VERIFY(outImageList.Create(m_nImageWidth, 
							   m_nImageHeight, 
							   m_nToolBarBitDepth, 
							   m_nNumImages, 
							   0));

	// attach the bitmap to the image list
	VERIFY(outImageList.Add(&bm, RGB(0,0,0)) != -1);
}

//=============================================================================
// load the high color toolbar images and attach them to toolbar
void CXTrueColorToolBar::AttachToolbarImages(UINT nBitmapId,
											 int nNumImages,
											 int nToolbar)	// 0 = normal, 1 = disabled, 2 = hot
//=============================================================================
{
	ASSERT(nNumImages > 0);

	m_nNumImages = nNumImages;

	if (m_TrueColorImages.GetSafeHandle())
		m_TrueColorImages.DeleteImageList();

	// make true-color image list for the bitmap
	MakeToolbarImageList(nBitmapId, m_TrueColorImages);
	ASSERT(m_TrueColorImages.GetSafeHandle());

	// get the toolbar control associated with the CToolbar object
	CToolBarCtrl& ctrl = GetToolBarCtrl();

	// attach the image list to the toolbar control
	switch (nToolbar)
	{
		default:
		case 0:
			ctrl.SetImageList(&m_TrueColorImages);
			break;
		case 1:
			ctrl.SetDisabledImageList(&m_TrueColorImages);
			break;
		case 2:
			ctrl.SetHotImageList(&m_TrueColorImages);
			break;
	}
}

//=============================================================================
// Get image list index from command ID
int CXTrueColorToolBar::GetImageIndex(UINT nCommandId)
//=============================================================================
{
	CToolBarCtrl& ctrl = GetToolBarCtrl();

	int nButtonIndex = ctrl.CommandToIndex(nCommandId);

	UINT nID = 0;
	UINT nStyle = 0;
	int iImage = 0;
	GetButtonInfo(nButtonIndex, nID, nStyle, iImage);

	return iImage;
}

//=============================================================================
// Get bitmap from image list
BOOL CXTrueColorToolBar::GetBitmapFromImageList(CBitmap& bm, CImageList* pList)
//=============================================================================
{
	BOOL rc = FALSE;

	ASSERT(pList && pList->GetSafeHandle());

	if (pList && pList->GetSafeHandle())
	{
		IMAGEINFO ImageInfo;
		rc = pList->GetImageInfo(0, &ImageInfo);

		if (rc)
		{
			rc = bm.Attach(ImageInfo.hbmImage);

			if (!rc)
			{
				TRACE(_T("ERROR - Attach failed\n"));
				ASSERT(FALSE);
			}
		}
		else
		{
			TRACE(_T("ERROR - GetImageInfo failed\n"));
			ASSERT(FALSE);
		}
	}

	return rc;
}

//=============================================================================
// Set color of color button 
void CXTrueColorToolBar::SetColorButton(UINT nCommandId, COLORREF rgb)
//=============================================================================
{
	CToolBarCtrl& ctrl = GetToolBarCtrl();
	CImageList *pList = ctrl.GetImageList();

	if (pList && pList->GetSafeHandle())
	{
		CBitmap bmTemplate;
		VERIFY(GetBitmapFromImageList(bmTemplate, &m_ImageTemplate));

		int iImage = GetImageIndex(nCommandId);

		// restore original toolbar image from saved image template
		VERIFY(pList->Replace(iImage, &bmTemplate, NULL));

		CBitmap bmToolbar;
		VERIFY(GetBitmapFromImageList(bmToolbar, pList));

		// replace mask color in toolbar image list with new color
		ReplaceMaskColor(bmToolbar, bmTemplate, m_crMaskColor, rgb);

		m_crCurrentColor = rgb;

		RedrawWindow();
	}
}

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

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

License

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


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions