www.pudn.com > StyleToolkit_demo_src.zip > StyleButton.cpp


// 
// StyleButton.cpp : Version 1.0 - see article at CodeProject.com 
// 
// Author:  Darren Sessions 
//           
// 
// Description: 
//     StyleButton is a CButton derived control that uses GDI+  
//     to support alternate image formats 
// 
// History 
//     Version 1.0 - 2008 July 1 
//     - Initial public release 
// 
// License: 
//     This software is released under the Code Project Open License (CPOL), 
//     which may be found here:  http://www.codeproject.com/info/eula.aspx 
//     You are free to use this software 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 "StyleButton.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// StyleButton 
 
StyleButton::StyleButton() 
{ 
	m_pStdImage = NULL; 
	m_pAltImage = NULL; 
 
	m_bHaveBitmaps = FALSE; 
	m_bHaveAltImage = FALSE; 
 
	m_pCurBtn = NULL; 
 
	m_bIsDisabled = FALSE; 
	m_bIsToggle = FALSE; 
 
	m_bIsHovering = FALSE; 
	m_bIsTracking = FALSE; 
 
	m_nCurState = STD_STATE; 
	m_nButtonType = BUTTON_TYPE_INVALID; 
 
	m_pToolTip = NULL; 
 
	m_bHavePStyle = m_bHaveHStyle = m_bHaveGStyle = NULL; 
	m_bHaveAltStyle = m_bHaveAltPStyle = m_bHaveAltHStyle = NULL; 
} 
 
StyleButton::~StyleButton() 
{ 
	if(m_pStdImage) delete m_pStdImage; 
	if(m_pAltImage) delete m_pAltImage; 
	if(m_pToolTip)	delete m_pToolTip; 
} 
 
BEGIN_MESSAGE_MAP(StyleButton, CButton) 
	//{{AFX_MSG_MAP(StyleButton) 
	ON_WM_DRAWITEM() 
	ON_WM_ERASEBKGND() 
	ON_WM_CTLCOLOR_REFLECT() 
	ON_WM_MOUSEMOVE() 
	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave) 
	ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover) 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
 
 
//============================================================================= 
// 
// LoadStdImage() 
// 
// Purpose:     The LoadStdImage() Loads the image for the button.  This  
//				function must be called at a minimum or the button wont do  
//				anything. 
// 
// Parameters:   
//		[IN]	id 
//				resource id, one of the resources already imported with the  
//				resource editor, usually begins with IDR_   
// 
//		[IN]	pType 
//				pointer to string describing the resource type 
//				 
// Returns:     BOOL 
//				Non zero if successful, otherwise zero 
// 
//============================================================================= 
BOOL StyleButton::LoadStdImage(UINT id, LPCTSTR pType) 
{ 
	m_pStdImage = new GResource; 
	m_nButtonType = BUTTON_TYPE_IMAGE; 
	return m_pStdImage->Load(id, pType); 
} 
 
//============================================================================= 
// 
// LoadAltImage() 
// 
// Purpose:     The LoadAltImage() Loads the altername image for the button.   
//				This function call is optional 
// Parameters:   
//		[IN]	id 
//				resource id, one of the resources already imported with the  
//				resource editor, usually begins with IDR_   
// 
//		[IN]	pType 
//				pointer to string describing the resource type 
//				 
// Returns:     BOOL 
//				Non zero if successful, otherwise zero 
// 
//============================================================================= 
BOOL StyleButton::LoadAltImage(UINT id, LPCTSTR pType) 
{ 
	m_bHaveAltImage = TRUE; 
	m_pAltImage = new GResource; 
	return (m_pAltImage->Load(id, pType)); 
} 
 
 
//============================================================================= 
// 
//	The framework calls this member function when a child control is about to  
//	be drawn.  All the bitmaps are created here on the first call. Every thing 
//	is done with a memory DC except the background, which get's it's information  
//	from the parent. The background is needed for transparent portions of PNG  
//	images. An always on top app (such as Task Manager) that is in the way can  
//	cause it to get an incorrect background.  To avoid this, the parent should  
//	call the SetBkGnd function with a memory DC when it creates the background. 
//				 
//============================================================================= 
HBRUSH StyleButton::CtlColor(CDC* pScreenDC, UINT nCtlColor)  
{ 
	if(!m_bHaveBitmaps) 
	{ 
		// Need to call LoadStdImage or LoadStdStyle at a minimum 
		ASSERT(m_nButtonType != BUTTON_TYPE_INVALID); 
 
		CRect rect; 
		GetClientRect(rect); 
 
		// do everything with a temporary mem dc 
		MemDC pDC(pScreenDC, rect); 
 
		// create a graphics object 
		Graphics graphics(pDC->m_hDC); 
 
		// background 
		if (m_dcBk.m_hDC == NULL) 
		{ 
			CRect rect1; 
			CClientDC clDC(GetParent()); 
			GetWindowRect(rect1); 
			GetParent()->ScreenToClient(rect1); 
 
			CBitmap bmp; 
			m_dcBk.CreateCompatibleDC(&clDC); 
			bmp.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height()); 
			m_dcBk.SelectObject(&bmp); 
			m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY); 
			bmp.DeleteObject(); 
		} 
 
		// standard image 
		if (m_dcStd.m_hDC == NULL) 
		{ 
			PaintBk(pDC); 
 
			if(m_nButtonType == BUTTON_TYPE_IMAGE) 
			{ 
				graphics.DrawImage(*m_pStdImage, 0, 0); 
			} 
			else 
			{ 
				m_StdStyle.PaintStyle(pDC, rect); 
			} 
		 
			m_dcStd.Create(pDC, rect); 
 
			// standard image pressed 
			if (m_dcStdP.m_hDC == NULL) 
			{ 
				PaintBk(pDC); 
 
				if(m_nButtonType == BUTTON_TYPE_IMAGE) 
				{ 
					graphics.DrawImage(*m_pStdImage, 1, 1); 
				} 
				else 
				{ 
					// check for pressed style or create from std 
					if(m_bHavePStyle) 
					{ 
						m_StdPStyle.PaintStyle(pDC, rect); 
					} 
					else 
					{ 
						m_StdPStyle = m_StdStyle; 
						m_StdPStyle.SetPosition(1,1); 
						m_StdPStyle.m_Stacks[0].Regenerate(); 
						m_StdPStyle.PaintStyle(pDC, rect); 
					} 
				} 
 
				m_dcStdP.Create(pDC, rect); 
			} 
 
			// standard image hot 
			if(m_dcStdH.m_hDC == NULL) 
			{ 
				PaintBk(pDC); 
 
				if(m_nButtonType == BUTTON_TYPE_IMAGE) 
				{ 
					DrawHotBitmap(&graphics, m_pStdImage->m_pBitmap); 
				} 
				else 
				{ 
					// check for hot style or create from std 
					if(m_bHaveHStyle) 
					{ 
						m_StdHStyle.PaintStyle(pDC, rect); 
					} 
					else 
					{ 
						m_StdStyle.m_Stacks[0].SetClipping(&graphics); 
						Bitmap *pBmp = m_StdStyle.GetBitmap(pDC, rect); 
						DrawHotBitmap(&graphics, pBmp); 
						delete pBmp; 
						m_StdStyle.m_Stacks[0].RestoreClipping(&graphics); 
					} 
				} 
 
				m_dcStdH.Create(pDC, rect); 
			} 
 
			// grayscale image 
			if(m_dcStdG.m_hDC == NULL) 
			{ 
				PaintBk(pDC); 
 
				if(m_nButtonType == BUTTON_TYPE_IMAGE) 
				{ 
					DrawGrayBitmap(&graphics, m_pStdImage->m_pBitmap); 
				} 
				else 
				{ 
					// check for disabled style or create from std 
					if(m_bHaveGStyle) 
					{ 
						m_StdGStyle.PaintStyle(pDC, rect); 
					} 
					else 
					{ 
						m_StdStyle.m_Stacks[0].SetClipping(&graphics); 
						Bitmap *pBmp = m_StdStyle.GetBitmap(pDC, rect); 
						DrawGrayBitmap(&graphics, pBmp); 
						delete pBmp; 
						m_StdStyle.m_Stacks[0].RestoreClipping(&graphics); 
					} 
				} 
 
				m_dcStdG.Create(pDC, rect); 
			} 
		} 
 
		// alternate image 
		if( (m_dcAlt.m_hDC == NULL) && (m_bHaveAltImage || m_bHaveAltStyle) ) 
		{ 
			PaintBk(pDC); 
 
			if(m_nButtonType == BUTTON_TYPE_IMAGE) 
			{ 
				graphics.DrawImage(*m_pAltImage, 0, 0); 
			} 
			else 
			{ 
				m_AltStyle.PaintStyle(pDC, rect); 
			} 
		 
			m_dcAlt.Create(pDC, rect); 
 
			// alternate image pressed 
			if(m_dcAltP.m_hDC == NULL) 
			{ 
				PaintBk(pDC); 
 
				if(m_nButtonType == BUTTON_TYPE_IMAGE) 
				{ 
					graphics.DrawImage(*m_pAltImage, 1, 1); 
				} 
				else 
				{ 
					// check for pressed style or create from std 
					if(m_bHaveAltPStyle) 
					{ 
						m_AltPStyle.PaintStyle(pDC, rect); 
					} 
					else 
					{ 
						m_AltPStyle = m_AltStyle; 
						m_AltPStyle.SetPosition(1,1); 
						m_AltPStyle.m_Stacks[0].Regenerate(); 
						m_AltPStyle.PaintStyle(pDC, rect); 
					} 
				} 
			 
				m_dcAltP.Create(pDC, rect); 
			} 
 
			// alternate image hot 
			if(m_dcAltH.m_hDC == NULL) 
			{ 
				PaintBk(pDC); 
				if(m_nButtonType == BUTTON_TYPE_IMAGE) 
				{ 
					DrawHotBitmap(&graphics, m_pAltImage->m_pBitmap); 
				} 
				else 
				{ 
					m_AltStyle.m_Stacks[0].SetClipping(&graphics); 
					Bitmap *pBmp = m_AltStyle.GetBitmap(pDC, rect); 
					DrawHotBitmap(&graphics, pBmp); 
					delete pBmp; 
					m_AltStyle.m_Stacks[0].RestoreClipping(&graphics); 
				} 
 
				m_dcAltH.Create(pDC, rect); 
			} 
		} 
 
		if(m_pCurBtn == NULL) 
		{ 
			m_pCurBtn = &m_dcStd; 
		} 
 
		m_bHaveBitmaps = TRUE; 
	} 
 
	return NULL; 
} 
 
//============================================================================= 
// paint the background 
//============================================================================= 
void StyleButton::PaintBk(CDC *pDC) 
{ 
	CRect rect; 
	GetClientRect(rect); 
	pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY); 
} 
 
//============================================================================= 
// paint the bitmap currently pointed to with m_pCurBtn 
//============================================================================= 
void StyleButton::PaintBtn(CDC *pDC) 
{ 
	CRect rect; 
	GetClientRect(rect); 
	pDC->BitBlt(0, 0, rect.Width(), rect.Height(), m_pCurBtn, 0, 0, SRCCOPY); 
} 
 
//============================================================================= 
// enables the toggle mode 
// returns if it doesn't have the alternate image 
//============================================================================= 
void StyleButton::EnableToggle(BOOL bEnable) 
{ 
	if(!(m_bHaveAltImage || m_bHaveAltStyle)) return; 
 
	m_bIsToggle = bEnable;  
 
	// this actually makes it start in the std state since toggle is called before paint 
	if(bEnable)	m_pCurBtn = &m_dcAlt; 
	else		m_pCurBtn = &m_dcStd; 
 
} 
 
//============================================================================= 
// sets the image type and disabled state then repaints 
//============================================================================= 
void StyleButton::SetImage(int type) 
{ 
	m_nCurState = type; 
 
	(type == DIS_STATE) ? m_bIsDisabled = TRUE : m_bIsDisabled = FALSE; 
 
	Invalidate(); 
} 
 
//============================================================================= 
// set the control to owner draw 
//============================================================================= 
void StyleButton::PreSubclassWindow() 
{ 
	// Set control to owner draw 
	ModifyStyle(0, BS_OWNERDRAW, SWP_FRAMECHANGED); 
 
	CButton::PreSubclassWindow(); 
} 
 
//============================================================================= 
// disable double click  
//============================================================================= 
BOOL StyleButton::PreTranslateMessage(MSG* pMsg)  
{ 
	if (pMsg->message == WM_LBUTTONDBLCLK) 
		pMsg->message = WM_LBUTTONDOWN; 
 
	if (m_pToolTip != NULL) 
	{ 
		if (::IsWindow(m_pToolTip->m_hWnd)) 
		{ 
			m_pToolTip->RelayEvent(pMsg);		 
		} 
	} 
 
	return CButton::PreTranslateMessage(pMsg); 
} 
 
 
//============================================================================= 
// overide the erase function 
//============================================================================= 
BOOL StyleButton::OnEraseBkgnd(CDC* pDC)  
{ 
	return TRUE; 
} 
 
//============================================================================= 
// Paint the button depending on the state of the mouse 
//============================================================================= 
void StyleButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)  
{ 
	CDC* pDC = CDC::FromHandle(lpDIS->hDC); 
 
	// handle disabled state 
	if(m_bIsDisabled) 
	{ 
		m_pCurBtn = &m_dcStdG; 
		PaintBtn(pDC); 
		return; 
	} 
 
	BOOL bIsPressed = (lpDIS->itemState & ODS_SELECTED); 
 
	// handle toggle button 
	if(m_bIsToggle && bIsPressed) 
	{ 
		(m_nCurState == STD_STATE) ? m_nCurState = ALT_STATE : m_nCurState = STD_STATE; 
	} 
 
	// determine which bitmap to copy 
	if(bIsPressed) 
	{ 
		if(m_nCurState == STD_STATE) 
			m_pCurBtn = &m_dcStdP; 
		else 
			m_pCurBtn = &m_dcAltP; 
	} 
	else if(m_bIsHovering) 
	{ 
 
		if(m_nCurState == STD_STATE) 
			m_pCurBtn = &m_dcStdH; 
		else 
			m_pCurBtn = &m_dcAltH; 
	} 
	else 
	{ 
		if(m_nCurState == STD_STATE) 
			m_pCurBtn = &m_dcStd; 
		else 
			m_pCurBtn = &m_dcAlt; 
	} 
 
	// paint the button 
	PaintBtn(pDC); 
} 
 
//============================================================================= 
LRESULT StyleButton::OnMouseHover(WPARAM wparam, LPARAM lparam)  
//============================================================================= 
{ 
	m_bIsHovering = TRUE; 
	Invalidate(); 
	DeleteToolTip(); 
 
	// Create a new Tooltip with new Button Size and Location 
	SetToolTipText(m_tooltext); 
 
	if (m_pToolTip != NULL) 
	{ 
		if (::IsWindow(m_pToolTip->m_hWnd)) 
		{ 
			//Display ToolTip 
			m_pToolTip->Update(); 
		} 
	} 
 
	return 0; 
} 
 
 
//============================================================================= 
LRESULT StyleButton::OnMouseLeave(WPARAM wparam, LPARAM lparam) 
//============================================================================= 
{ 
	m_bIsTracking = FALSE; 
	m_bIsHovering = FALSE; 
	Invalidate(); 
	return 0; 
} 
 
//============================================================================= 
void StyleButton::OnMouseMove(UINT nFlags, CPoint point)  
//============================================================================= 
{ 
	if (!m_bIsTracking) 
	{ 
		TRACKMOUSEEVENT tme; 
		tme.cbSize = sizeof(tme); 
		tme.hwndTrack = m_hWnd; 
		tme.dwFlags = TME_LEAVE|TME_HOVER; 
		tme.dwHoverTime = 1; 
		m_bIsTracking = _TrackMouseEvent(&tme); 
	} 
	 
	CButton::OnMouseMove(nFlags, point); 
} 
 
//============================================================================= 
//	 
//	Call this member function with a memory DC from the code that paints  
//	the parents background.  Passing the screen DC defeats the purpose of  
//  using this function. 
// 
//============================================================================= 
void StyleButton::SetBkGnd(CDC* pDC) 
{ 
	CRect rect, rectS; 
	CBitmap bmp, *pOldBitmap; 
 
	GetClientRect(rect); 
	GetWindowRect(rectS); 
	GetParent()->ScreenToClient(rectS); 
 
	m_dcBk.DeleteDC(); 
 
	m_dcBk.CreateCompatibleDC(pDC); 
	bmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); 
	pOldBitmap = m_dcBk.SelectObject(&bmp); 
	m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rectS.left, rectS.top, SRCCOPY); 
	bmp.DeleteObject(); 
} 
 
 
//============================================================================= 
// Set the tooltip with a string resource 
//============================================================================= 
void StyleButton::SetToolTipText(UINT nId, BOOL bActivate) 
{ 
	// load string resource 
	m_tooltext.LoadString(nId); 
 
	// If string resource is not empty 
	if (m_tooltext.IsEmpty() == FALSE) 
	{ 
		SetToolTipText(m_tooltext, bActivate); 
	} 
 
} 
 
//============================================================================= 
// Set the tooltip with a CString 
//============================================================================= 
void StyleButton::SetToolTipText(CString spText, BOOL bActivate) 
{ 
	// We cannot accept NULL pointer 
	if (spText.IsEmpty()) return; 
 
	// Initialize ToolTip 
	InitToolTip(); 
	m_tooltext = spText; 
 
	// If there is no tooltip defined then add it 
	if (m_pToolTip->GetToolCount() == 0) 
	{ 
		CRect rectBtn;  
		GetClientRect(rectBtn); 
		m_pToolTip->AddTool(this, m_tooltext, rectBtn, 1); 
	} 
 
	// Set text for tooltip 
	m_pToolTip->UpdateTipText(m_tooltext, this, 1); 
	m_pToolTip->SetDelayTime(2000); 
	m_pToolTip->Activate(bActivate); 
} 
 
//============================================================================= 
void StyleButton::InitToolTip() 
//============================================================================= 
{ 
	if (m_pToolTip == NULL) 
	{ 
		m_pToolTip = new CToolTipCtrl; 
		// Create ToolTip control 
		m_pToolTip->Create(this); 
		m_pToolTip->Activate(TRUE); 
	} 
}  
 
//============================================================================= 
void StyleButton::DeleteToolTip() 
//============================================================================= 
{ 
	// Destroy Tooltip incase the size of the button has changed. 
	if (m_pToolTip != NULL) 
	{ 
		delete m_pToolTip; 
		m_pToolTip = NULL; 
	} 
} 
 
//============================================================================= 
void StyleButton::LoadStdStyle(Style style) 
//============================================================================= 
{ 
	m_StdStyle = style; 
	m_nButtonType = BUTTON_TYPE_STYLE; 
} 
 
//============================================================================= 
void StyleButton::LoadHotStyle(Style style) 
//============================================================================= 
{ 
	m_StdHStyle = style; 
	m_bHaveHStyle = TRUE; 
} 
 
//============================================================================= 
void StyleButton::LoadPressedStyle(Style style) 
//============================================================================= 
{ 
	m_StdPStyle = style; 
	m_bHavePStyle = TRUE; 
} 
 
 
//============================================================================= 
void StyleButton::LoadAltStyle(Style style) 
//============================================================================= 
{ 
	m_AltStyle = style; 
	m_bHaveAltStyle = TRUE; 
} 
 
//============================================================================= 
void StyleButton::LoadAltHotStyle(Style style) 
//============================================================================= 
{ 
	m_AltHStyle = style; 
	m_bHaveAltHStyle = TRUE; 
} 
 
//============================================================================= 
void StyleButton::LoadAltPressedStyle(Style style) 
//============================================================================= 
{ 
	m_AltPStyle = style; 
	m_bHaveAltPStyle = TRUE; 
} 
 
//============================================================================= 
void StyleButton::DrawHotBitmap(Graphics* pGraphics, Bitmap* pBmp) 
//============================================================================= 
{ 
	ColorMatrix HotMat = {	1.05f, 0.00f, 0.00f, 0.00f, 0.00f, 
							0.00f, 1.05f, 0.00f, 0.00f, 0.00f, 
							0.00f, 0.00f, 1.05f, 0.00f, 0.00f, 
							0.00f, 0.00f, 0.00f, 1.00f, 0.00f, 
							0.05f, 0.05f, 0.05f, 0.00f, 1.00f	}; 
 
	ImageAttributes ia; 
	ia.SetColorMatrix(&HotMat); 
 
	float width = (float)pBmp->GetWidth(); 
	float height = (float)pBmp->GetHeight(); 
 
	RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height; 
 
	pGraphics->DrawImage((Image*)pBmp, grect, 0, 0, width, height, UnitPixel, &ia); 
 
} 
 
//============================================================================= 
void StyleButton::DrawGrayBitmap(Graphics* pGraphics, Bitmap* pBmp) 
//============================================================================= 
{ 
	ColorMatrix GrayMat = {	0.30f, 0.30f, 0.30f, 0.00f, 0.00f, 
							0.59f, 0.59f, 0.59f, 0.00f, 0.00f, 
							0.11f, 0.11f, 0.11f, 0.00f, 0.00f, 
							0.00f, 0.00f, 0.00f, 1.00f, 0.00f, 
							0.00f, 0.00f, 0.00f, 0.00f, 1.00f	}; 
 
	ImageAttributes ia; 
	ia.SetColorMatrix(&GrayMat); 
 
	float width = (float)pBmp->GetWidth(); 
	float height = (float)pBmp->GetHeight(); 
 
	RectF grect; grect.X=0, grect.Y=0; grect.Width = width; grect.Height = height; 
 
	pGraphics->DrawImage((Image*)pBmp, grect, 0, 0, width, height, UnitPixel, &ia); 
 
}