Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

CButtonSSL

, 4 Dec 2001 CPOL
An owner-drawn, flat, menu button that correctly handles the default state
buttonssl_demo.zip
SSLButtonExTest.dsp
SSLButtonExTest.dsw
res
bitmap1.bmp
bitmap2.bmp
bitmap3.bmp
bitmap4.bmp
bitmap5.bmp
Hand.cur
icon1.ico
icon2.ico
icon3.ico
icon4.ico
SSLButtonExTest.ico
tick.bmp
vssver.scc
buttonssl_doc.zip
demo.gif
inherit.gif
buttonssl_src.zip
// CButtonSSL.cpp : implementation file
//

#include "stdafx.h"
#include "ButtonSSL.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define DROP_BUTTON_WIDTH	16
#define BS_TYPEMASK			SS_TYPEMASK
#define TIMER_MOUSE_OVER  1

/////////////////////////////////////////////////////////////////////////////
// CButtonSSL

/////////////////////////////////////////////////////////////////////////////
// Construction

CButtonSSL::CButtonSSL () : COddButton () {
	m_bImageLoaded = FALSE;
	m_bMenuLoaded = FALSE;
	m_bMenuPushed = FALSE;
	m_bBtnPushed = FALSE;
	m_bMouseOnBtn = FALSE;
	
	m_nStyle = SSL_BS_FLAT;
	m_nTextAlign = SSL_TEXT_CENTER | SSL_TEXT_VCENTER;
	m_nImageAlign = SSL_IMAGE_LEFT | SSL_IMAGE_TOP;
	m_nImageWidth = 0;
	m_nImageHeight = 0;

	m_ToolTip.m_hWnd = NULL;
	m_hCursor = NULL;
	// Fix for GDI Resource leak. Thanks to Dieter Hammer
	m_hIcon = NULL;
	SetSSLDefaultColors (FALSE);

	ZeroMemory(&m_bmp, sizeof m_bmp);
	ZeroMemory(&m_szURL, sizeof(m_szURL));

	m_bsState = SSL_STATE_UP;
	m_nCheck = 0;
}

/////////////////////////////////////////////////////////////////////////////
// Destruction

CButtonSSL::~CButtonSSL () {
	m_font.DeleteObject ();
	// Fix for GDI Resource leak. Thanks to Dieter Hammer
	if (NULL != m_hIcon) {
	   ::DestroyIcon (m_hIcon);
	}
}

BEGIN_MESSAGE_MAP(CButtonSSL, COddButton)
	//{{AFX_MSG_MAP(CButtonSSL)
	ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_SETFOCUS()
	ON_WM_SETCURSOR()
	ON_WM_CTLCOLOR_REFLECT()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	ON_MESSAGE (BM_GETCHECK, OnGetCheck)
	ON_MESSAGE (BM_SETCHECK, OnSetCheck)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CButtonSSL message handlers

void CButtonSSL::PreSubclassWindow() {
	// Determine what type of button this is
	COddButton::PreSubclassWindow ();

	if (NULL == (HFONT)m_font) {
		// If a font isn't already set, get the default font
		CFont* pFont = GetFont();
		if (!pFont) {
			HFONT hFont = (HFONT)GetStockObject (DEFAULT_GUI_FONT);
			if (NULL == hFont) {
				hFont = (HFONT) GetStockObject (ANSI_VAR_FONT);
			}
			if (hFont) {
				pFont = CFont::FromHandle (hFont);
			}
		}
		ASSERT (pFont->GetSafeHandle ());
		LOGFONT lf;
		pFont->GetLogFont (&lf);
		m_font.CreateFontIndirect (&lf);
	}

}

// Thanks to Tomasz Sowinski for helping me with the DDX problems
// Many thanks to Joe Newcomer for pointing out the error of my ways
// by not supplying all of the requried parameters
LRESULT CButtonSSL::OnGetCheck (WPARAM wParam, LPARAM lParam) {
	UINT nType = GetControlType ();
	if (BS_CHECKBOX == (BS_CHECKBOX & nType) || 
		BS_AUTOCHECKBOX == (BS_AUTOCHECKBOX & nType) ||
		BS_RADIOBUTTON == (BS_RADIOBUTTON & nType) || 
		BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & nType)) {
		if (0 == m_nCheck) {
			return BST_UNCHECKED;
		}
		else {
			return BST_CHECKED;
		}
	}
	return 0;
}

// Thanks to Tomasz Sowinski for helping me with the DDX problems
// Many thanks to Joe Newcomer for pointing out the error of my ways
// by not supplying all of the requried parameters
LRESULT CButtonSSL::OnSetCheck (WPARAM fCheck, LPARAM lParam) {
	if (BST_CHECKED == fCheck) {
		m_nCheck = 1;
		m_bBtnPushed = TRUE;
	}
	else if (BST_UNCHECKED == fCheck) {
		m_nCheck = 0;
		m_bBtnPushed = FALSE;
	}

	Invalidate ();

	return 0;
}

BOOL CButtonSSL::OnClicked () {
	// Update the check state for radio buttons and check boxes
	UINT nType = GetControlType ();
	if (BS_CHECKBOX == (BS_CHECKBOX & nType) || BS_AUTOCHECKBOX== (BS_AUTOCHECKBOX & nType) /*||
		BS_RADIOBUTTON & nType || BS_AUTORADIOBUTTON & nType*/) {
		m_nCheck = !m_nCheck;
		m_bBtnPushed = !m_bBtnPushed;
		Invalidate ();
	}
	else if (BS_RADIOBUTTON == (BS_RADIOBUTTON & nType) ||
		BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & nType)) {
        // Send BM_SETCHECK (BST_CHECKED) to this button nad
        // BM_SETCHECK (BST_UNCHECKED) to all other buttons in this group
        SendMessage (BM_SETCHECK, BST_CHECKED, 0);
        // Start by searching forwards
        HWND hWndDlg = this->GetParent ()->m_hWnd;
        CWnd* pWnd = FromHandle (::GetNextDlgGroupItem (hWndDlg, this->m_hWnd, FALSE));
        while (NULL != pWnd && this != pWnd) {
            pWnd->SendMessage (BM_SETCHECK, BST_UNCHECKED, 0);
            pWnd = FromHandle (::GetNextDlgGroupItem (hWndDlg, pWnd->m_hWnd, FALSE));
        }
        // Then search backwards
        pWnd = FromHandle (::GetNextDlgGroupItem (hWndDlg, this->m_hWnd, TRUE));
        while (NULL != pWnd && this != pWnd) {
            pWnd->SendMessage (BM_SETCHECK, BST_UNCHECKED, 0);
            pWnd = FromHandle (::GetNextDlgGroupItem (hWndDlg, pWnd->m_hWnd, TRUE));
        }
	}
	else {
		// Handle the URL (if any)
		if (::lstrlen(m_szURL) > 0) {
			::ShellExecute(NULL, _T("open"), m_szURL, NULL, NULL, SW_SHOWMAXIMIZED);
		}
	}
	
	return FALSE; // Allow the parent to handle the click event
}

void CButtonSSL::OnMouseMove (UINT nFlags, CPoint point) {
	CWnd*	pWnd;		// Active window
	CWnd*	pParent;	// Window that owns the button

	CButton::OnMouseMove (nFlags, point);

	// Ignore if left button pressed on entering button
	if ((nFlags & MK_LBUTTON) && FALSE == m_bMouseOnBtn) {
		return;
	}

	pWnd = GetActiveWindow();
	pParent = GetOwner();

	if ((GetCapture () != this) && (pParent != NULL)) {
		m_bMouseOnBtn = TRUE;
		// Start the timer to check for the mouse leaving
		SetTimer (TIMER_MOUSE_OVER, 100, NULL);
		SetCapture ();
		Invalidate ();
	}
	else {
		ClientToScreen (&point);
		CWnd* wndUnderMouse = WindowFromPoint (point);
		if (NULL != wndUnderMouse && wndUnderMouse->m_hWnd != this->m_hWnd) {
			// Redraw only if mouse goes out
			if (TRUE == m_bMouseOnBtn) {
				m_bMouseOnBtn = FALSE;
				Invalidate ();
			}
			// Release capture if the left button is not pressed
			if (!(nFlags & MK_LBUTTON)) {
				ReleaseCapture ();
			}
		}
	}
}

void CButtonSSL::OnLButtonDown(UINT nFlags, CPoint point) {
	if (TRUE == m_bMenuPushed) {
		m_bMenuPushed = FALSE;
		Invalidate();
		return;
	}

	// Show the menu if the click is on the drop-arrow
	// and the SSL_BS_MENU_BTN style is set
	if (TRUE == HitMenuButton(point) && (SSL_BS_MENU_BTN & m_nStyle)) {
		m_bMenuPushed = TRUE;
		Invalidate();

		CRect rect;
		GetWindowRect (rect);

		int x = rect.right;
		int y = rect.bottom;

		CMenu* pMenu = m_menu.GetSubMenu (0);
		pMenu->TrackPopupMenu (TPM_RIGHTALIGN | TPM_LEFTBUTTON, x, y, GetParent ());

		m_bMenuPushed = FALSE;
	}
	else {
		m_bBtnPushed = TRUE;
	}

	Invalidate();
	CButton::OnLButtonDown (nFlags, point);
}

void CButtonSSL::OnLButtonUp(UINT nFlags, CPoint point) {
	CButton::OnLButtonUp (nFlags, point);

	// Check box state updating done in Clicked handler
	UINT nType = GetControlType ();
	if (BS_CHECKBOX != (BS_CHECKBOX & nType) &&
		BS_AUTOCHECKBOX != (BS_AUTOCHECKBOX & nType) &&
		BS_RADIOBUTTON != (BS_RADIOBUTTON & nType) &&
		BS_AUTORADIOBUTTON != (BS_AUTORADIOBUTTON & nType)) {
		if (TRUE == m_bBtnPushed) {
			m_bBtnPushed = FALSE;
			ReleaseCapture();
			Invalidate();
		}
	}
}

void CButtonSSL::OnSetFocus(CWnd* pOldWnd) {
	CButton::OnSetFocus (pOldWnd);
	
	Invalidate ();
}

BOOL CButtonSSL::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) {
	// Use the specifed cursor if there is one
	if (NULL != m_hCursor) {
		::SetCursor (m_hCursor);
		return TRUE;
	}
	
	return CButton::OnSetCursor(pWnd, nHitTest, message);
}

// Fix for button state problem. Inspired by Chris Maunder's control
// subclassing article
void CButtonSSL::OnTimer (UINT nIDEvent) {
	if (TIMER_MOUSE_OVER == nIDEvent) {
		// Get the current mouse position
		CPoint Point (GetMessagePos ());
		ScreenToClient (&Point);

		// Get the bounds of the button
		CRect Rect;
		GetClientRect (Rect);

		// Check if the mouse is over the button
		if (FALSE == Rect.PtInRect (Point)) {
			// Update the state if it isn't
			m_bMouseOnBtn = false;
			KillTimer (TIMER_MOUSE_OVER);
			// and redraw the button
			Invalidate ();
		}
	}

	COddButton::OnTimer (nIDEvent);
}

HBRUSH CButtonSSL::CtlColor (CDC* pDC, UINT nCtlColor) {
	return (HBRUSH)::GetStockObject (NULL_BRUSH); 
}

void CButtonSSL::DrawBackground (CDC* pDC, LPCRECT pRect) {
	COLORREF crColor = ::GetSysColor (COLOR_BTNFACE);

	if (SSL_STATE_UP == m_bsState) {
		crColor = m_crColors[SSL_UP_BK_COLOR];
	}
	if (SSL_STATE_OVER == m_bsState) {
		crColor = m_crColors[SSL_OVER_BK_COLOR];
	}
	if (SSL_STATE_DOWN == m_bsState) {
		crColor = m_crColors[SSL_DOWN_BK_COLOR];
	}

	CBrush		brBackground(crColor);

	pDC->FillRect(pRect, &brBackground);
}

void CButtonSSL::DrawBorder (CDC* pDC, LPRECT lpRect) {
	if (TRUE == m_bBtnPushed) {
		pDC->Draw3dRect (lpRect, ::GetSysColor (COLOR_BTNSHADOW), ::GetSysColor (COLOR_BTNHIGHLIGHT));
		return;
	}
	if (!(SSL_BS_FLAT & m_nStyle)) {
		if (TRUE == m_bBtnPushed) {
			pDC->DrawFrameControl (lpRect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);
		}
		else {
			pDC->DrawFrameControl (lpRect, DFC_BUTTON, DFCS_BUTTONPUSH);
		}
		return;
	}
	if (TRUE == m_bMouseOnBtn && FALSE == m_bBtnPushed) {
		pDC->Draw3dRect (lpRect, ::GetSysColor (COLOR_BTNHIGHLIGHT), ::GetSysColor (COLOR_BTNSHADOW));
	}
}

void CButtonSSL::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) {
	if (lpDrawItemStruct->CtlType != ODT_BUTTON) {
		return;
	}

	CDC cdcMem;
	CBitmap bmp;

	CRect btnRect (lpDrawItemStruct->rcItem);
	CRect trueRect (btnRect);

	CDC* pDC = CDC::FromHandle (lpDrawItemStruct->hDC);

	// Check to see if it's a checked checkbox or radio button
	UINT nType = GetControlType ();
	if ((BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & nType) ||
		BS_RADIOBUTTON == (BS_RADIOBUTTON & nType) ||
		BS_AUTOCHECKBOX == (BS_AUTOCHECKBOX & nType) || 
		BS_CHECKBOX == (BS_CHECKBOX & nType)) && 
		FALSE == m_bBtnPushed) {
		if (0 == m_nCheck) {
			m_bBtnPushed = FALSE;
		}
		else {
			m_bBtnPushed = TRUE;
		}
	}

	// Check to see if the button is disabled
	m_bDisabled = (ODS_DISABLED == (ODS_DISABLED & lpDrawItemStruct->itemState));

	// Update the button state
	if (TRUE == m_bDisabled) {
		m_bsState = SSL_STATE_DISABLED;
	}
	else {
		m_bsState = SSL_STATE_UP;
		if (TRUE == m_bMouseOnBtn) {
			m_bsState = SSL_STATE_OVER;
		}
		if (TRUE == m_bBtnPushed) {
			m_bsState = SSL_STATE_DOWN;
		}
	}

#ifdef _TRACESTATE
	TraceState (m_bsState);
#endif
	
	if (TRUE == IsDefault ()) {
		btnRect.DeflateRect (1, 1);
	}

	// Button Background and Border
	DrawBackground (pDC, &trueRect);
	DrawBorder (pDC, &btnRect);

	CRect rectFocus (btnRect);	// Focus rectangle
	rectFocus.DeflateRect (3, 3);

	if (SSL_BS_MENU_BTN & m_nStyle) {
		rectFocus.right -= DROP_BUTTON_WIDTH;	// Exclude drop button from focus rectangle
	}
	// Default Button State
	if (TRUE == IsDefault () && FALSE == m_bDisabled && !(SSL_BS_FLAT & m_nStyle)) {
		pDC->FrameRect (&lpDrawItemStruct->rcItem, 
			CBrush::FromHandle ((HBRUSH)GetStockObject (BLACK_BRUSH)));
		if (TRUE == m_bBtnPushed && FALSE == m_bMenuPushed) {
			pDC->FrameRect (&btnRect, CBrush::FromHandle (GetSysColorBrush (COLOR_3DSHADOW))); 
		}
	}

	// State Focus
	if ((lpDrawItemStruct->itemState & ODS_FOCUS || TRUE == m_bBtnPushed) && !(SSL_BS_FLAT & m_nStyle) &&
		BS_AUTOCHECKBOX != (BS_AUTOCHECKBOX & nType) && BS_CHECKBOX != (BS_CHECKBOX & nType)) {
		if (FALSE == m_bMenuPushed) {
			pDC->DrawFocusRect (&rectFocus);
		}
	}

	// Action Focus
	if ((lpDrawItemStruct->itemAction & ODA_FOCUS) && !(SSL_BS_FLAT & m_nStyle)) {
		if (FALSE == m_bMenuPushed) {
			pDC->DrawFocusRect (&rectFocus);
		}
	}

	// Draw the image on the button.
	// Extract the image as an icon regardless of what was added originally
	if (TRUE == m_bImageLoaded) {
		HICON hIcon = m_imageList.ExtractIcon (m_bsState);
		// Defer to the Up image. This should only happen if there isn't a disabled image
		if (NULL == hIcon) {
			hIcon = m_imageList.ExtractIcon (SSL_STATE_UP);
			pDC->DrawState (GetImagePoint (trueRect),
				CSize (m_nImageWidth, m_nImageHeight), hIcon, 
				DST_ICON | (m_bsState == SSL_STATE_DISABLED ? DSS_DISABLED : DSS_NORMAL), (CBrush*)NULL);
		}
		else {
			pDC->DrawState (GetImagePoint (trueRect),
				CSize (m_nImageWidth, m_nImageHeight), hIcon, 
				DST_ICON | DSS_NORMAL, (CBrush*)NULL);
		}
		// Fix for GDI Resource leak. Thanks to Dieter Hammer
		if (hIcon) {
		  ::DestroyIcon (hIcon);
		}
	}

	// Draw out text
	pDC->SelectObject (&m_font);
	CRect rectText (rectFocus);

	CString strCaption;
	GetWindowText (strCaption);
	pDC->SetBkMode (TRANSPARENT);

	if (ODS_DISABLED & lpDrawItemStruct->itemState) {
		pDC->SetBkColor (GetSysColor (COLOR_BTNFACE));
		rectFocus.OffsetRect (1,1);
		pDC->SetTextColor (GetSysColor (COLOR_WINDOW));
		pDC->DrawText (strCaption, rectFocus, DT_SINGLELINE | m_nTextAlign);

		rectFocus.OffsetRect (-1,-1);
		pDC->SetTextColor (GetSysColor (COLOR_GRAYTEXT));
		pDC->DrawText (strCaption, rectFocus, DT_SINGLELINE | m_nTextAlign);
	}
	else {
		if (SSL_STATE_UP == m_bsState) {
			pDC->SetBkColor (m_crColors[SSL_UP_BK_COLOR]);
			pDC->SetTextColor (m_crColors[SSL_UP_TEXT_COLOR]);
		}
		if (SSL_STATE_OVER == m_bsState ) {
			pDC->SetBkColor (m_crColors[SSL_OVER_BK_COLOR]);
			pDC->SetTextColor (m_crColors[SSL_OVER_TEXT_COLOR]);
		}
		if (SSL_STATE_DOWN == m_bsState) {
			pDC->SetBkColor (m_crColors[SSL_DOWN_BK_COLOR]);
			pDC->SetTextColor (m_crColors[SSL_DOWN_TEXT_COLOR]);
		}
		pDC->DrawText (strCaption, rectFocus, DT_SINGLELINE | m_nTextAlign);
	}

	if (SSL_BS_MENU_BTN & m_nStyle) {
		// Drop down split
		CRect rectSplit (btnRect);
		rectSplit.DeflateRect (2,2);
		rectSplit.right -= DROP_BUTTON_WIDTH;
		if (!(SSL_BS_FLAT & m_nStyle) || TRUE == m_bMouseOnBtn) {
			CPen brFace (PS_SOLID, 1, ::GetSysColor (COLOR_BTNSHADOW));
			pDC->SelectObject (&brFace);
			pDC->MoveTo (rectSplit.right, rectSplit.top);
			pDC->LineTo (rectSplit.right, rectSplit.bottom);

			CPen brLite (PS_SOLID, 1, ::GetSysColor (COLOR_BTNHIGHLIGHT));
			pDC->SelectObject (&brLite);
			pDC->MoveTo (rectSplit.right + 1 , rectSplit.top);
			pDC->LineTo (rectSplit.right + 1, rectSplit.bottom);
		}
		rectSplit.left = rectSplit.right;
		rectSplit.right += DROP_BUTTON_WIDTH;

		CPoint pt (rectSplit.CenterPoint ());
		pt += CPoint (m_bBtnPushed, m_bBtnPushed);

		CPen penBlack (PS_SOLID, 1, m_bDisabled ? 
			GetSysColor (COLOR_GRAYTEXT) : GetSysColor (COLOR_WINDOWTEXT));
		pDC->SelectObject (&penBlack);
		DrawArrow (pDC, pt);

		// Drop down state
		if (TRUE == m_bMenuPushed && FALSE == m_bDisabled) {    
			rectSplit.InflateRect (1, 1);
			pDC->DrawEdge (rectSplit, BDR_SUNKENOUTER, BF_RECT);
		}
	}
}

BOOL CButtonSSL::PreTranslateMessage(MSG* pMsg) 
{
	// Initialise the tooltip if necessary
	InitToolTipCtrl ();
	m_ToolTip.RelayEvent (pMsg);

	return CButton::PreTranslateMessage(pMsg);
}

/////////////////////////////////////////////////////////////////////////////
// CButtonSSL implementation

/////////////////////////////////////////////////////////////////////////////
// Initialisation

void CButtonSSL::SetSSLButtonStyle (UINT nStyle) {
	m_nStyle = nStyle;

	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	if (TRUE == IsDefault ()) {
		PostMessage (BM_SETSTYLE, BS_PUSHBUTTON, TRUE);
	}
	else {
		PostMessage (BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE);
	}
}

UINT CButtonSSL::GetSSLButtonStyle () {
	return m_nStyle;
}

/////////////////////////////////////////////////////////////////////////////
// Image Functions

BOOL CButtonSSL::SetSSLButtonBitmap(UINT nResourceID, COLORREF crMask) {
	BOOL bReturn = m_bmpImage.LoadBitmap (nResourceID);
	if (TRUE == bReturn) {
		m_bmpImage.GetObject (sizeof m_bmp, &m_bmp);
		m_nImageWidth = m_bmp.bmWidth;
		m_nImageHeight = m_bmp.bmHeight;
		if (NULL != m_imageList.m_hImageList) {
			m_imageList.Detach ();
		}
		bReturn = m_imageList.Create (nResourceID, m_bmp.bmWidth, 1, crMask);
		if (TRUE == bReturn) {
			if (m_imageList.Add (&m_bmpImage, crMask) > -1) {
				m_bImageLoaded = TRUE;
			}
			else {
				m_bImageLoaded = FALSE;
			}
		}
		else {
			m_bImageLoaded = FALSE;
		}
	}

	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	return bReturn;
}

BOOL CButtonSSL::SetSSLButtonBitmaps (UINT nUpID, COLORREF crUpMask, 
					   UINT nOverID/* = 0*/, COLORREF crOverMask/* = SSL_MASK*/,
					   UINT nDownID/* = 0*/, COLORREF crDownMask/* = SSL_MASK*/, 
					   UINT nDisabledID/* = 0*/, COLORREF crDisabledMask/* = SSL_MASK*/) {
	ASSERT (0 != nUpID);
	BOOL bReturn = m_bmpImage.LoadBitmap (nUpID);
	if (TRUE == bReturn) {
		m_bmpImage.GetObject (sizeof m_bmp, &m_bmp);
		m_nImageWidth = m_bmp.bmWidth;
		m_nImageHeight = m_bmp.bmHeight;
		if (NULL != m_imageList.m_hImageList) {
			m_imageList.Detach ();
		}
		bReturn = m_imageList.Create (m_nImageWidth, m_nImageHeight, ILC_MASK | ILC_COLOR32, 4, 0);
		if (TRUE == bReturn) {
			if (m_imageList.Add (&m_bmpImage, crUpMask) > -1) {
				m_bImageLoaded = TRUE;
				BOOL bOver = FALSE;
				// Add the Over image
				if (nOverID > 0) {
					AddBitmap (nOverID, crOverMask);
					bOver = TRUE;
				}
				else {
					// Use Up image if Over not supplied
					AddBitmap (nUpID, crUpMask);
				}
				// Add the Down image
				if (nDownID > 0) {
					AddBitmap (nDownID, crDownMask);
				}
				else {
					// If there's no Down image supplied
					if (TRUE == bOver) {
						// Use the Over image is supplied
						AddBitmap (nOverID, crOverMask);
					}
					else {
						// Otherwise use the Up image
						AddBitmap (nUpID, crUpMask);
					}
				}
				// Add the Disabled image
				// If one isn't one DrawState will use the Up image and draw it disabled.
				if (nDisabledID > 0) {
					AddBitmap (nDisabledID, crDisabledMask);
				}
			}
			else {
				m_bImageLoaded = FALSE;
			}
		}
	}


	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	return bReturn;
}

BOOL CButtonSSL::SetSSLButtonIcon(UINT nResourceID) {
	BOOL bReturn = FALSE;
	HINSTANCE hInstance = AfxFindResourceHandle (MAKEINTRESOURCE (nResourceID), RT_GROUP_ICON);

	m_hIcon = (HICON)::LoadImage (hInstance, MAKEINTRESOURCE (nResourceID), IMAGE_ICON, 0, 0, 0);
	if (NULL != m_hIcon) {
		ICONINFO iconInfo;
		ZeroMemory (&iconInfo, sizeof (ICONINFO));
		bReturn = ::GetIconInfo (m_hIcon, &iconInfo);
		if (TRUE == bReturn) {
			m_nImageWidth = iconInfo.xHotspot * 2;
			m_nImageHeight = iconInfo.yHotspot * 2;
			::DeleteObject(iconInfo.hbmMask);
			::DeleteObject(iconInfo.hbmColor);
			if (NULL != m_imageList.m_hImageList) {
				m_imageList.Detach ();
			}
			bReturn = m_imageList.Create (m_nImageWidth, m_nImageHeight, ILC_COLOR32 | ILC_MASK, 1, 1);
			if (TRUE == bReturn) {
				if (m_imageList.Add (m_hIcon) > -1) {
					m_bImageLoaded = TRUE;
				}
				else {
					m_bImageLoaded = FALSE;
				}
			}
		}
	}


	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	return bReturn;
}

BOOL CButtonSSL::SetSSLButtonIcons (UINT nUpID, UINT nOverID/* = 0*/,
					   UINT nDownID/* = 0*/, UINT nDisabledID/* = 0*/) {
	ASSERT (0 != nUpID);
	BOOL bReturn = FALSE;
	HINSTANCE hInstance = AfxFindResourceHandle (MAKEINTRESOURCE (nUpID), RT_GROUP_ICON);

	m_hIcon = (HICON)::LoadImage (hInstance, MAKEINTRESOURCE (nUpID), IMAGE_ICON, 0, 0, 0);
	if (NULL != m_hIcon) {
		ICONINFO iconInfo;
		ZeroMemory (&iconInfo, sizeof (ICONINFO));
		bReturn = ::GetIconInfo (m_hIcon, &iconInfo);
		if (TRUE == bReturn) {
			m_nImageWidth = iconInfo.xHotspot * 2;
			m_nImageHeight = iconInfo.yHotspot * 2;
			::DeleteObject (iconInfo.hbmMask);
			::DeleteObject (iconInfo.hbmColor);
			if (NULL != m_imageList.m_hImageList) {
				m_imageList.Detach ();
			}
			bReturn = m_imageList.Create (m_nImageWidth, m_nImageHeight, ILC_MASK | ILC_COLOR32, 4, 0);
			if (TRUE == bReturn) {
				if (m_imageList.Add (m_hIcon) > -1) {
					m_bImageLoaded = TRUE;
					BOOL bOver = FALSE;
					// Add the Over image
					if (nOverID > 0) {
						AddIcon (nOverID);
						bOver = TRUE;
					}
					else {
						// Use Up image if Over not supplied
						AddIcon (nUpID);
					}
					// Add the Down image
					if (nDownID > 0) {
						AddIcon (nDownID);
					}
					else {
						// If there's no Down image supplied
						if (TRUE == bOver) {
							// Use the Over image is supplied
							AddIcon (nOverID);
						}
						else {
							// Otherwise use the Up image
							AddIcon (nUpID);
						}
					}
					// Add the Disabled image
					// If one isn't one DrawState will use the Up image and draw it disabled.
					if (nDisabledID > 0) {
						AddIcon (nDisabledID);
					}
				}
				else {
					m_bImageLoaded = FALSE;
				}
			}
		}
	}

	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	return bReturn;
}

void CButtonSSL::SetSSLImageAlign (UINT nImageAlign) {
	m_nImageAlign = nImageAlign;
	Invalidate ();
}

UINT CButtonSSL::GetSSLImageAlign () {
	return m_nImageAlign;
}

/////////////////////////////////////////////////////////////////////////////
// Text Functions

void CButtonSSL::SetSSLTextAlign (UINT nTextAlign) {
	m_nTextAlign = nTextAlign;
	Invalidate ();
}

UINT CButtonSSL::GetSSLTextAlign () {
	return m_nTextAlign;
}

BOOL CButtonSSL::SetSSLButtonFont (LPCTSTR lpszFaceName, int nSizePoints/* = 8*/,
									BOOL bUnderline/* = FALSE*/, BOOL bBold/* = FALSE*/,
									BOOL bStrikeOut/* = FALSE*/, BOOL bItalic/* = FALSE*/) {
	if (NULL != (HFONT)m_font) {
		m_font.DeleteObject ();
	}

	CDC* pDC = GetDC ();
	LOGFONT lf;
	memset (&lf, 0, sizeof(LOGFONT));

	strcpy (lf.lfFaceName, lpszFaceName);
	lf.lfHeight = -MulDiv (nSizePoints, GetDeviceCaps (pDC->m_hDC, LOGPIXELSY), 72);
	lf.lfUnderline = bUnderline;
	if (TRUE == bBold) {
		lf.lfWeight = FW_BOLD;
	}
	lf.lfStrikeOut = bStrikeOut;
	lf.lfItalic = bItalic;

	BOOL bReturn = m_font.CreateFontIndirect (&lf);

	Invalidate ();
	ReleaseDC (pDC);

	return bReturn;
}

BOOL CButtonSSL::SetSSLButtonFont (CFont& font) {
	if (NULL != (HFONT)m_font) {
		m_font.DeleteObject ();
	}

	LOGFONT lf;
	font.GetLogFont (&lf);

	BOOL bReturn = m_font.CreateFontIndirect (&lf);

	Invalidate ();
	
	return bReturn;
}

/////////////////////////////////////////////////////////////////////////////
// Colour Functions

void CButtonSSL::SetSSLDefaultColors (BOOL bRedraw/* = TRUE*/) {
	m_crColors[SSL_UP_BK_COLOR]	= ::GetSysColor(COLOR_BTNFACE);
	m_crColors[SSL_UP_TEXT_COLOR]	= ::GetSysColor(COLOR_BTNTEXT);
	m_crColors[SSL_OVER_BK_COLOR]	= ::GetSysColor(COLOR_BTNFACE);
	m_crColors[SSL_OVER_TEXT_COLOR]	= ::GetSysColor(COLOR_BTNTEXT);
	m_crColors[SSL_DOWN_BK_COLOR]	= ::GetSysColor(COLOR_BTNFACE);
	m_crColors[SSL_DOWN_TEXT_COLOR]	= ::GetSysColor(COLOR_BTNTEXT);

	if (TRUE == bRedraw) {
		Invalidate();
	}
}

BOOL CButtonSSL::SetSSLColor (BYTE byColorIndex, COLORREF crColor, BOOL bRedraw/* = TRUE*/) {
	if (byColorIndex > SSL_MAX_COLORS) {
		return FALSE;
	}

	m_crColors[byColorIndex] = crColor;

	if (TRUE == bRedraw) {
		Invalidate ();
	}
	
	return TRUE;
}

BOOL CButtonSSL::GetSSLColor (BYTE byColorIndex, COLORREF* pcrColor) {
	if (byColorIndex > SSL_MAX_COLORS) {
		return FALSE;
	}

	*pcrColor = m_crColors[byColorIndex];

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Check Box Functions

void CButtonSSL::SetSSLCheck (int nCheck, BOOL bRedraw/* = TRUE*/) {
	UINT nType = GetControlType ();
	if (BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & nType) ||
		BS_RADIOBUTTON == (BS_RADIOBUTTON & nType) ||
		BS_AUTOCHECKBOX == (BS_AUTOCHECKBOX & nType) ||
		BS_CHECKBOX == (BS_CHECKBOX & nType)) {
		if (0 == nCheck) {
			m_nCheck = 0;
			m_bBtnPushed = false;
		}
		else {
			m_nCheck = 1;
			m_bBtnPushed = true;
		}

		if (TRUE == bRedraw) {
			Invalidate ();
		}
	}
}

int CButtonSSL::GetSSLCheck () {
	return m_nCheck;
}

/////////////////////////////////////////////////////////////////////////////
// Menu Functions

BOOL CButtonSSL::SetSSLButtonMenu (UINT nMenuResourceID) {
	// Ensure the SSL_BS_MENU_BTN style is set
	m_nStyle |= SSL_BS_MENU_BTN;
	if (NULL != m_menu.m_hMenu) {
		m_menu.DestroyMenu ();
	}
	BOOL bReturn = m_menu.LoadMenu (nMenuResourceID);

	m_bMenuLoaded |= bReturn;

	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	return bReturn;
}

BOOL CButtonSSL::SetSSLButtonMenu (LPCTSTR lpszResourceName) {
	// Ensure the SSL_BS_MENU_BTN style is set
	m_nStyle |= SSL_BS_MENU_BTN;
	if (NULL != m_menu.m_hMenu) {
		m_menu.DestroyMenu ();
	}
	BOOL bReturn = m_menu.LoadMenu (lpszResourceName);

	m_bMenuLoaded |= bReturn;


	// Resize the button if necessary
	if (SSL_BS_AUTOSIZE == (SSL_BS_AUTOSIZE & m_nStyle)) {
		SetSize ();
	}

	return bReturn;
}

UINT CButtonSSL::CheckSSLMenuItem (UINT nIDCheckItem, UINT nCheck/* = MF_CHECKED*/) {
	if (FALSE == m_bMenuLoaded) {
		return MF_DOES_NOT_EXIST;
	}

    return m_menu.CheckMenuItem (nIDCheckItem, nCheck);
}

BOOL CButtonSSL::CheckSSLMenuRadioItem (UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags) {
    if (FALSE == m_bMenuLoaded) {
        return FALSE;
    }

    return m_menu.CheckMenuRadioItem (nIDFirst, nIDLast, nIDItem, nFlags);
}

UINT CButtonSSL::EnableSSLMenuItem (UINT nIDEnableItem, UINT nEnable) {
    if (FALSE == m_bMenuLoaded) {
        return MF_DOES_NOT_EXIST;
    }

    return m_menu.EnableMenuItem (nIDEnableItem, nEnable);
}

UINT CButtonSSL::GetSSLMenuItemCount () const {
    if (FALSE == m_bMenuLoaded) {
        return MF_DOES_NOT_EXIST;
    }

    return m_menu.GetMenuItemCount ();
}

UINT CButtonSSL::GetSSLMenuItemID (int nPos) const {
    if (FALSE == m_bMenuLoaded) {
        return MF_DOES_NOT_EXIST;
    }

    return m_menu.GetMenuItemID (nPos);
}

UINT CButtonSSL::GetSSLMenuState (UINT nID, UINT nFlags) {
	if (FALSE == m_bMenuLoaded) {
		return MF_DOES_NOT_EXIST;
	}
	
	return m_menu.GetMenuState (nID, nFlags);
}

int CButtonSSL::GetSSLMenuString (UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags) {
    if (FALSE == m_bMenuLoaded) {
        return 0;
    }

    return m_menu.GetMenuString (nIDItem, lpString, nMaxCount, nFlags);
}

int CButtonSSL::GetSSLMenuString (UINT nIDItem, CString& rString, UINT nFlags) {
    if (FALSE == m_bMenuLoaded) {
        return 0;
    }

    return m_menu.GetMenuString (nIDItem, rString, nFlags);
}

BOOL CButtonSSL::GetSSLMenuItemInfo (UINT nIDItem, LPMENUITEMINFO lpMenuItemInfo,
                                     BOOL ByPos/*=FALSE*/) {
    if (FALSE == m_bMenuLoaded) {
        return FALSE;
    }

    return m_menu.GetMenuItemInfo (nIDItem, lpMenuItemInfo, ByPos);
}

BOOL CButtonSSL::ModifySSLMenu (UINT nPosition, UINT nFlags, UINT nIDNewItem/*=0*/,
                                LPCTSTR lpszNewItem/*=NULL*/) {
    if (FALSE == m_bMenuLoaded) {
        return FALSE;
    }

    return m_menu.ModifyMenu (nPosition, nFlags, nIDNewItem, lpszNewItem);
}

BOOL CButtonSSL::ModifySSLMenu (UINT nPosition, UINT nFlags, UINT nIDNewItem,
                                const CBitmap* pBmp) {
    if (FALSE == m_bMenuLoaded) {
        return FALSE;
    }

    return m_menu.ModifyMenu (nPosition, nFlags, nIDNewItem, pBmp);
}

BOOL CButtonSSL::RemoveSSLMenu (UINT nPosition, UINT nFlags) {
    if (FALSE == m_bMenuLoaded) {
        return FALSE;
    }

    return m_menu.RemoveMenu (nPosition, nFlags);
}

BOOL CButtonSSL::SetSSLMenuItemBitmaps (UINT nPosition, UINT nFlags,
                                        const CBitmap* pBmpUnchecked,
                                        const CBitmap* pBmpChecked) {
    if (FALSE == m_bMenuLoaded) {
        return FALSE;
    }

    return m_menu.SetMenuItemBitmaps (nPosition, nFlags, pBmpUnchecked,
                                      pBmpChecked);
}

/////////////////////////////////////////////////////////////////////////////
// Miscellaneous Functions

BOOL CButtonSSL::SetSSLButtonCursor (UINT nResourceID) {
	HINSTANCE hInstResource;
	// Destroy any previous cursor
	if (NULL != m_hCursor) {
		::DestroyCursor (m_hCursor);
		m_hCursor = NULL;
	}

	// If we want a cursor
	if (nResourceID != NULL) {
		hInstResource = AfxFindResourceHandle (MAKEINTRESOURCE (nResourceID), RT_GROUP_CURSOR);
		// Load icon resource
		m_hCursor = (HCURSOR)::LoadImage (hInstResource, MAKEINTRESOURCE (nResourceID),
			IMAGE_CURSOR, 0, 0, 0);
		// If something wrong then return FALSE
		if (NULL == m_hCursor) {
			return FALSE;
		}
	}

	return TRUE;
}

void CButtonSSL::SetSSLButtonToolTip (LPCTSTR lpszTipText, BOOL bActivate) {
	// Can't have a NULL text string
	if (NULL == lpszTipText) {
		return;
	}

	// Initialise the tooltip
	InitToolTipCtrl ();

	// Need to add a tooltip if none defined
	if (0 == m_ToolTip.GetToolCount ()) {
		CRect rectBtn;
		GetClientRect (rectBtn);
		m_ToolTip.AddTool (this, lpszTipText, rectBtn, 1);
	}

	// Set the text
	m_ToolTip.UpdateTipText (lpszTipText, this, 1);
	m_ToolTip.Activate (bActivate);
}

void CButtonSSL::SetSSLButtonToolTip (UINT nResourceID, BOOL bActivate) {
	CString strTipText;
	strTipText.LoadString (nResourceID);
	if (FALSE == strTipText.IsEmpty ()) {
		SetSSLButtonToolTip ((LPCTSTR)strTipText, bActivate);
	}
}

void CButtonSSL::GetSSLButtonToolTip (CString& strTipText) {
	m_ToolTip.GetText (strTipText, this, 1);
}

// The following appeared in Paul DiLascia's Jan 1998 MSJ articles.
// It loads a "hand" cursor from the winhlp32.exe module
void CButtonSSL::SetSSLButtonURL (LPCTSTR lpszURL) {
	if (NULL != lpszURL) {
		// Store the URL
		::lstrcpyn(m_szURL, lpszURL, _MAX_PATH);

		if (NULL == m_hCursor) {
			// Try to load the 'standard' hand cursor
			CString szWndDir;
			GetWindowsDirectory (szWndDir.GetBuffer (MAX_PATH), MAX_PATH);
			szWndDir.ReleaseBuffer ();

			// Try cursor #106 in WinHlp32.exe
			CString szHlp32 = szWndDir + _T("\\winhlp32.exe");
			HMODULE hModule = LoadLibrary (szHlp32);
			if (NULL != hModule) {
				HCURSOR hHandCursor;
				hHandCursor = ::LoadCursor (hModule, MAKEINTRESOURCE (106));
				if (NULL != hHandCursor) {
					m_hCursor = CopyCursor (hHandCursor);
				}
			}
		}
	}
	else {
		// Remove any existing URL
		::ZeroMemory(&m_szURL, sizeof(m_szURL));
	}
}

/////////////////////////////////////////////////////////////////////////////
// CButtonSSL helper functions

void CButtonSSL::DrawArrow (CDC* pDC, CPoint ptTip) {
	CPoint ptDest;

	pDC->SetPixel (ptTip, RGB (0, 0, 0));

	ptTip -= CPoint (1, 1);
	pDC->MoveTo (ptTip);

	ptDest = ptTip;
	ptDest += CPoint (3, 0);
	pDC->LineTo (ptDest);

	ptTip -= CPoint (1, 1);
	pDC->MoveTo (ptTip);

	ptDest = ptTip;
	ptDest += CPoint (5, 0);
	pDC->LineTo (ptDest);

	ptTip -= CPoint (1, 1);
	pDC->MoveTo (ptTip);

	ptDest = ptTip;
	ptDest += CPoint (7, 0);
	pDC->LineTo (ptDest);
}

BOOL CButtonSSL::HitMenuButton (CPoint pt)
{
	if (FALSE == m_bMenuLoaded)
		return FALSE; // Don't allow menu button drop down if no menu items are loaded

	ClientToScreen(&pt);

	CRect rect;
	GetWindowRect (rect);
	rect.left = rect.right - DROP_BUTTON_WIDTH;

	return rect.PtInRect (pt);
}

void CButtonSSL::InitToolTipCtrl () {
	if (NULL == m_ToolTip.m_hWnd) {
		m_ToolTip.Create (this);
		m_ToolTip.Activate(FALSE); // Inactive initially
		m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, 300); // Enable multiline tooltip
	}
}

void CButtonSSL::AddBitmap (UINT nResourceID, COLORREF crMask) {
	m_bmpImage.Detach ();
	m_bmpImage.LoadBitmap (nResourceID);
	m_imageList.Add (&m_bmpImage, crMask);
}

void CButtonSSL::AddIcon (UINT nResourceID) {
	HINSTANCE hInstance = AfxFindResourceHandle (MAKEINTRESOURCE (nResourceID), RT_GROUP_ICON);

	m_hIcon = (HICON)::LoadImage (hInstance, MAKEINTRESOURCE (nResourceID), IMAGE_ICON, 0, 0, 0);
	if (NULL != m_hIcon) {
		ICONINFO iconInfo;
		ZeroMemory (&iconInfo, sizeof (ICONINFO));
		::GetIconInfo (m_hIcon, &iconInfo);
		::DeleteObject(iconInfo.hbmMask);
		::DeleteObject(iconInfo.hbmColor);
		m_imageList.Add (m_hIcon);
	}
}

CPoint CButtonSSL::GetImagePoint (CRect btnRect) {
	int nLeft = 3, nTop = 3; // Default position for SSL_IMAGE_LEFT / _TOP
	// Determine the left coordinate
	if (SSL_IMAGE_CENTER == (SSL_IMAGE_CENTER & m_nImageAlign)) {
		if (SSL_BS_MENU_BTN == (SSL_BS_MENU_BTN & m_nStyle)) {
			nLeft = (btnRect.Width () - m_nImageWidth - DROP_BUTTON_WIDTH) / 2;
		}
		else {
			nLeft = (btnRect.Width () - m_nImageWidth) / 2;
		}
	}
	else if (SSL_IMAGE_RIGHT == (SSL_IMAGE_RIGHT & m_nImageAlign)) {
		nLeft = btnRect.right - m_nImageWidth - 3;
		if (SSL_BS_MENU_BTN == (SSL_BS_MENU_BTN & m_nStyle)) {
			nLeft -= DROP_BUTTON_WIDTH;
		}
	}
	// Determine the top coordinate
	if (SSL_IMAGE_VCENTER == (SSL_IMAGE_VCENTER & m_nImageAlign)) {
		nTop = (btnRect.Height () - m_nImageHeight) / 2;
	}
	else if (SSL_IMAGE_BOTTOM == (SSL_IMAGE_BOTTOM & m_nImageAlign)) {
		nTop = btnRect.bottom - m_nImageHeight - 3;
	}

	return CPoint (nLeft + m_bBtnPushed, nTop + m_bBtnPushed);
}

void CButtonSSL::SetSize () {
	// Get the size of the button text
	CDC* pDC = GetDC ();
	SIZE size;
	CString szText;
	GetWindowText (szText);
	// Need to select the font to ensure correct sizing for GetTextExtentPoint32
	pDC->SelectObject (&m_font); 
	GetTextExtentPoint32 (pDC->m_hDC, szText, szText.GetLength (), &size);

	// Get the current size of the button
	CRect btnRect;
	GetWindowRect (btnRect);

	// Adjust the size according to the contents
	btnRect.right = btnRect.left + max (m_nImageWidth, size.cx) + 6;
	btnRect.bottom = btnRect.top + max (m_nImageHeight, size.cy) + 6;

	// Take in to account the drop button if there is one
	if (SSL_BS_MENU_BTN == (SSL_BS_MENU_BTN & m_nStyle)) {
		btnRect.right += DROP_BUTTON_WIDTH + 3;
	}

	// Resize the button
	SetWindowPos (NULL, 0, 0, btnRect.Width (), btnRect.Height (), SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
}

void CButtonSSL::TraceState (ButtonState state) {
	CString strState = _T("Button State: ");
	switch (state) {
	case SSL_STATE_UP:
		strState += _T("Up");
		break;
	case SSL_STATE_OVER:
		strState += _T("Over");
		break;
	case SSL_STATE_DOWN:
		strState += _T("Down");
		break;
	case SSL_STATE_DISABLED:
		strState += _T("Disabled");
		break;
	default:
		strState += _T("Invalid");
	}
	TRACE ("%s. m_bDisabled: %i. m_bMouseOnBtn: %i. m_bBtnPushed: %i. m_bMenuPushed: %i.\n",
		strState, m_bDisabled, m_bMouseOnBtn, m_bBtnPushed, m_bMenuPushed);
}

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)

Share

About the Author

Derek Lakin
Software Developer (Senior)
United Kingdom United Kingdom
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 5 Dec 2001
Article Copyright 2001 by Derek Lakin
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid