Click here to Skip to main content
15,891,184 members
Articles / Desktop Programming / MFC
Article

FooButton

Rate me:
Please Sign up or sign in to vote.
4.85/5 (120 votes)
7 Oct 2006CPOL5 min read 571.7K   15K   231   196
A lightweight general-purpose owner drawn bitmap button.

Introduction

This article describes FooButton, a lightweight owner-drawn button class that's served me well for several years.  Although there are plenty of other excellent button classes at CodeProject, I thought I'd add this trusty friend to the pile in the hope that someone may find it equally useful.

Features

FooButton lets you use a vanilla CButton as a:
  • standard pushbutton
  • pushbutton button with a drop-down indicator
  • multi pushbutton (like IE's "Back" and "Next" buttons)
  • checkbutton
  • hyperlink
  • static text control that's responsive to mouse clicks
  • check box
  • radio button
Support is also provided for:
  • bitmaps (currently only 16-color)
  • left-justified, centered and multi-line captions
  • colored captions
  • gradient shaded button backgrounds
  • popup menus
  • hot tracking
  • optional focus rectangle and "default button" indicator
  • grouped checkbuttons

How to use FooButton

  1. First, associate a standard button control (eg: IDC_FOO_BUTTON) in your dialog with an instance of the object.

    /////////////
    // MyDialog.h
    #include "FooButton.h"
    ...
    FooButton m_fooButton;
    
    ///////////////
    // MyDialog.cpp
    void CMyDialog::DoDataExchange(CDataExchange* pDX)
    {
      CDialog::DoDataExchange(pDX);
      //{{AFX_DATA_MAP(CMyDialog)
      DDX_Control(pDX, IDC_FOO_BUTTON, m_fooButton);
      //}}AFX_DATA_MAP
    }
    

  2. Then, initialize the instance in your dialog's OnInitDialog() method to suit your needs.  In this example, the button is set to display a bitmap and a drop-down indicator.

    // Initialize FooButton
    m_fooButton.setBitmapId (IDB_FOO_BUTTON);
    m_fooButton.setType (FooButton::Type::pushButtonDropDown);
    
         Drop-down pushbutton

API

 MethodPurpose
 getType(), setType()Gets and sets the button's type 
 getTextStyle(), setTextStyle()Gets and sets the button's text style 
 getTextColor(), setTextColor()Gets and sets the button's text color 
 getFocusStyle(), setFocusStyle()Gets and sets the button's focus style 
 getGradient(), setGradient()Gets and sets the button's gradient property 
 getBitmapId(), setBitmapId()Gets and sets the button's (optional) bitmap id 
 displayPopupMenu()Displays a popup menu below the button 
 isChecked(), check()Gets and sets a checkButton's checked state 
 isMultiClicked(), clearMultiClick()Gets and resets a multiPushButton's multi-clicked state 
 addToGroup(), removeFromGroup()Adds/removes a checkButton to/from a button group 
 reset()Frees storage used by all button groups 
 

Using FooButton as a check button

You can freely change any property of the button at run time.  This code snippet turns the button into a checkbutton and checks it.  Use check() and isChecked() to set and retrieve the button's checked state.

// Make it a checkbutton and check it
m_fooButton.setType (FooButton::Type::checkButton);
m_fooButton.check (true);
ASSERT (m_fooButton.isChecked());    // testing
     Unchecked   Checked

Gradient shading

Pushbuttons and checkbuttons can be set to display a gradient shaded background by calling setGradient().  This method has no effect if the button isn't a pushbutton or checkbutton.

// Use a gradient shaded background
m_fooButton.setGradient (true);
    

Button groups

You can make a bunch of checkButtons behave as mutually exclusive radio buttons by adding them to a button group.  A button group is just a named collection of buttons.  FooButton automatically handles group creation, membership and cleanup.

// Make "size" checkbuttons mutually exclusive
m_btnSmall.addToGroup (_T("foo"));
m_btnMedium.addToGroup (_T("foo"));
m_btnLarge.addToGroup (_T("foo"));
m_btnXLarge.addToGroup (_T("foo"));
     A button group

Displaying a popup menu

To display a popup menu in response to a button click, call displayPopupMenu().  You can call this method for any type of FooButton.

void CMyDialog::OnFooButton()
{
  CMenu menu;
  menu.LoadMenu (IDR_POPUP_MENU);
  CMenu* pPopupMenu = menu.GetSubMenu (0);
  int nResult = m_fooButton.displayPopupMenu (pPopupMenu);
  if (0 != nResult)
     PostMessage (WM_COMMAND, nResult);
}
     Normal   Pressed

Multi-pushbuttons

A multi-pushbutton behaves as two buttons in one, similar to IE's "Back" and "Next" buttons.  When the user clicks the button's drop-down region, FooButton sets its "multi-clicked" property to true.  You can query this property by calling isMultiClicked().  Regardless of whether the user clicked in the button's main or drop-down region, a standard notification is sent to the parent.  To clear the button's multi-click property, call clearMultiClick().

void CMyDialog::OnFooButton()
{
  if (m_fooButton.isMultiClicked()) {

      // Display menu if drop-down region was clicked
      CMenu menu;
      menu.LoadMenu (IDR_POPUP_MENU);
      CMenu* pPopupMenu = menu.GetSubMenu (0);
      int nResult = m_fooButton.displayPopupMenu (pPopupMenu);
      if (0 != nResult)
         PostMessage (WM_COMMAND, nResult);

      // Remember to clear the button's multi-click property!
      m_fooButton.clearMultiClick();

  } else {

      // Otherwise do default action
      PostMessage (WM_COMMAND, IDC_DEFAULT_ACTION);
  }
}
     Multi-pushbutton   

Check boxes and radio buttons

You can make a FooButton appear as a standard check box or radio button by using the FooButton:Type::checkBox and FooButton:Type::radio types.  Of course, this is really only useful when you want to also display a bitmap or add menu support to the button.

// Appear as check box and radio button
m_fooButton1.setType (FooButton::Type::checkBox);
m_fooButton2.setType (FooButton::Type::radio);
     Check box   

Hyperlink button

A hyperlink button is just a regular button that renders itself as a hyperlink.  You can navigate to a URL or perform any other action in the button's handler.

// Appear as hyperlink
m_fooButton.setType (FooButton::Type::hyperink);
     

Text color

You can change the color of the button's text at any time by calling setTextColor().  The text of hyperlink buttons is always rendered in C_HyperlinkColor and that of disabled buttons is always rendered in the standard etched format.

// Draw caption in red
m_fooButton.setTextColor (RGB (192, 0, 0));
     Custom caption text color

Focus rectangle

By default, a FooButton doesn't display a focus rectangle.  Call setFocusStyle() with FooButton::Focus::normalFocus to enable the button to display a focus rectangle.

// Allow focus rectangle to be displayed
m_fooButton.setFocusStyle (FooButton::Focus::normalFocus);
     Focus rectangle disabled   

Default button indicator

To enable a default FooButton to display its standard dark border, call setFocusStyle() with FooButton::Focus::defaultFocus.

// Allow focus rectangle and default indicator to be displayed
m_fooButton.setFocusStyle (FooButton::Focus::defaultFocus);
    Default button (unpressed)   

Rendering disabled bitmaps

Use the standard MFC EnableWindow() API to enable and disable the button.  FooButton uses its original bitmap to render a disabled version.

m_fooButton.EnableWindow (TRUE);   // enable button
m_fooButton.EnableWindow (FALSE);  // disable button
     Normal   Disabled

Acknowledgement

Revision history

  • 7 Oct 2006
    • Bug Fixm_hMsimg32 should be set to NULL in destructor. (Thanks, C. Young!)
    • Bug Fix:  Memory leak in DisabledBlt(). (Thanks, Corrado Valli!)

  • 6 Mar 2005
    • Bug Fix:  Added definition of COLOR_HOTLIGHT to enable compilation on older systems.

  • 5 Mar 2005
    • Enhancement:  Added support for gradient shaded buttons.
    • Bug Fix:  Reusing a group name across dialog invocations would cause a crash in FooButton::removeFromGroup().

  • 19 Feb 2005
    • Enhancement:  Added support for colored captions.
    • Enhancement:  Removed requirement to call FooButton::reset() when your app terminates.
    • Enhancement:  Now uses standard Win2000/XP hyperlink cursor.
    • Enhancement:  Code now ASSERTs if you're not subclassing from a button control.
    • Bug Fix:  All calls to Invalidate() now validate window handle, allowing a FooButton to be safely destroyed when clicked.

  • Fixed typo in default state rendering logic
  • 17 Jul 2004
    • Added support for check boxes and radio buttons
    • Fixed typo in default state rendering logic

  • 11 Jul 2004
    • Optimized fix for "unreferenced identifier" compiler warning
    • Exposed focus rectangle and default state modes
    • Added support for button groups

  • 4 Jul 2004
    Added multi-pushbutton and hyperlink styles.

  • 3 Jul 2004
    Submitted to CodeProject.

  • 12 Sep 1998
    Initial version.
  • License

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


    Written By
    Technical Lead
    Canada Canada
    Ravi Bhavnani is an ardent fan of Microsoft technologies who loves building Windows apps, especially PIMs, system utilities, and things that go bump on the Internet. During his career, Ravi has developed expert systems, desktop imaging apps, marketing automation software, EDA tools, a platform to help people find, analyze and understand information, trading software for institutional investors and advanced data visualization solutions. He currently works for a company that provides enterprise workforce management solutions to large clients.

    His interests include the .NET framework, reasoning systems, financial analysis and algorithmic trading, NLP, HCI and UI design. Ravi holds a BS in Physics and Math and an MS in Computer Science and was a Microsoft MVP (C++ and C# in 2006 and 2007). He is also the co-inventor of 3 patents on software security and generating data visualization dashboards. His claim to fame is that he crafted CodeProject's "joke" forum post icon.

    Ravi's biggest fear is that one day he might actually get a life, although the chances of that happening seem extremely remote.

    Comments and Discussions

     
    GeneralRe: XP Themes Pin
    Jason.LYJ7-May-14 19:45
    professionalJason.LYJ7-May-14 19:45 
    GeneralRe: XP Themes [modified] Pin
    David Pritchard2-Aug-11 6:01
    David Pritchard2-Aug-11 6:01 
    GeneralRe: XP Themes Pin
    David Pritchard2-Aug-11 6:03
    David Pritchard2-Aug-11 6:03 
    GeneralRe: XP Themes Pin
    Member 26088026-Aug-11 14:08
    Member 26088026-Aug-11 14:08 
    GeneralRe: XP Themes Pin
    Ravi Bhavnani27-Aug-11 3:39
    professionalRavi Bhavnani27-Aug-11 3:39 
    GeneralRe: XP Themes Pin
    David Pritchard2-Aug-11 22:54
    David Pritchard2-Aug-11 22:54 
    GeneralRe: XP Themes Pin
    David Pritchard2-Aug-11 23:58
    David Pritchard2-Aug-11 23:58 
    GeneralRe: XP Themes Pin
    David Pritchard3-Aug-11 0:04
    David Pritchard3-Aug-11 0:04 
    The final bit. Sorry for the messy formatting.

    C++
    void CButtonST::PrepareImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage)
    {
    	CRect rBtn;
    	GetClientRect(&rBtn);
    	PrepareSTButtonImageRect(bHasTitle, rpItem, rpTitle, bIsPressed, dwWidth, dwHeight, rpImage, m_byAlign, m_nBitmapPadding, m_ptImageOrg, m_ptPressedOffset,
    		rBtn, (GetStyle() & BS_LEFT) == BS_LEFT, m_bIsCheckBox);
    } // End of PrepareImageRect
    void CButtonST::DrawTheIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
    {
    	BYTE		byIndex		= 0;
    	ASSERT(!m_bIsCheckBox && !m_bIsRadio);
    	// Select the icon to use
    	if ((m_bIsCheckBox && bIsPressed) || (!m_bIsCheckBox && (bIsPressed || m_bMouseOnButton)))
    		byIndex = 0;
    	else
    		byIndex = (BYTE)(m_csIcons[1].hIcon == NULL ? 0 : 1);
    	CRect rectClient;
    	GetClientRect(&rectClient);
    	DrawTheSTButtonIcon(pDC, bHasTitle, rpItem, rpCaption, bIsPressed, bIsDisabled, 
    		m_csIcons[byIndex].hIcon, m_csIcons[byIndex].dwWidth, m_csIcons[byIndex].dwHeight, m_byAlign, m_nBitmapPadding, m_ptImageOrg, m_ptPressedOffset,
    		rectClient, (GetStyle() & BS_LEFT) == BS_LEFT, 
    		m_bIsCheckBox);
    } // End of DrawTheIcon
    
    void CButtonST::DrawTheText(CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled)
    {
    	UNREFERENCED_PARAMETER(bIsPressed);
    	UNUSED_ALWAYS(bIsPressed);
    	UNREFERENCED_PARAMETER(bIsDisabled);
    	UNUSED_ALWAYS(bIsDisabled);
    	ASSERT(!m_bIsCheckBox && !m_bIsRadio);
    	DrawTheSTButtonText(GetSafeHwnd(), pDC, lpszText, rpItem, rpCaption, m_bIsPressed, m_bIsDisabled, m_bMouseOnButton, 
    									m_bIsFocused, 
    									 m_bIsCheckBox, m_ptPressedOffset, m_csIcons[0].hIcon, m_csBitmaps[0].hBitmap, 
    									 m_byAlign, GetStyle(), m_nBitmapPadding, 
    									 GetPressedTextColour(), //m_crColors[BTNST_COLOR_FG_IN], 
    									 GetFocusedTextColour(), //m_crColors[BTNST_COLOR_FG_FOCUS], 
    									 GetTextColour(),//m_crColors[BTNST_COLOR_FG_OUT], 
    									 m_crColors[BTNST_COLOR_BK_IN], 
    									 m_crColors[BTNST_COLOR_BK_FOCUS], m_crColors[BTNST_COLOR_BK_OUT], bSTBUTTON_BUTTONISWINDOW);
    
    } // End of DrawTheText
    DWORD CButtonST::OnDrawBackground(CDC* pDC, CRect* pRect)
    {
    	return DrawSTButtonBackground(GetSafeHwnd(), pDC, pRect, m_bIsDefault, m_bIsFocused, m_bMouseOnButton, m_bIsPressed, 
    		m_bIsFlat, m_crColors[BTNST_COLOR_BK_IN], m_crColors[BTNST_COLOR_BK_FOCUS], m_crColors[BTNST_COLOR_BK_OUT]);
    } // End of OnDrawBackground
    
    DWORD DrawSTButtonBackground(HWND hWnd, CDC* pDC, CRect* pRect, IN const int bIsDefault,  
    									  IN const int bIsFocused, IN const int bMouseOnButton, IN const int bIsPressed, 
    									  IN const int bIsFlat, IN const COLORREF colorIn, IN const COLORREF colorFocus, 
    									  IN const COLORREF colorOut)
    {
    	UNUSED_ALWAYS(hWnd);
    	COLORREF	crColor;
    	if (bIsFlat == FALSE)
    	{
    		if (bIsFocused || bIsDefault)
    		{
    			CBrush br(RGB(0,0,0));  
    			pDC->FrameRect(pRect, &br);
    			pRect->DeflateRect(1, 1);
    		} // if
    	} // if
    	if (bMouseOnButton || bIsPressed)
    		crColor = colorIn;
    	else
    	{
    		if (bIsFocused)
    			crColor = colorFocus;
    		else
    			crColor = colorOut;
    	} // else
    	CBrush		brBackground(crColor);
    	pDC->FillRect(pRect, &brBackground);
    	return BTNST_OK;
    }
    DWORD CButtonST::OnDrawBorder(CDC* pDC, CRect* pRect)
    {
    	return DrawSTButtonBorder(pDC, pRect, m_bIsPressed != 0, m_bIsFlat != 0, m_bDrawBorder != 0, m_bMouseOnButton != 0);
    } // End of OnDrawBorder
    DWORD DrawSTButtonBorder(CDC* pDC, CRect* pRect, IN const bool bIsPressed, IN const bool bIsFlat, 
    								 IN const bool bDrawBorder, IN const bool bMouseOnButton)
    {
    	// Draw pressed button
    	if (bIsPressed)
    	{
    		if (bIsFlat)
    		{
    			if (bDrawBorder)
    				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNSHADOW), ::GetSysColor(COLOR_BTNHILIGHT));
    		}
    		else    
    		{
    			CBrush brBtnShadow(GetSysColor(COLOR_BTNSHADOW));
    			pDC->FrameRect(pRect, &brBtnShadow);
    		}
    	}
    	else // ...else draw non pressed button
    	{
    		CPen penBtnHiLight(PS_SOLID, 0, GetSysColor(COLOR_BTNHILIGHT)); // White
    		CPen pen3DLight(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));       // Light gray
    		CPen penBtnShadow(PS_SOLID, 0, GetSysColor(COLOR_BTNSHADOW));   // Dark gray
    		CPen pen3DDKShadow(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW)); // Black
    		if (bIsFlat)
    		{
    			if (bMouseOnButton && bDrawBorder)
    				pDC->Draw3dRect(pRect, ::GetSysColor(COLOR_BTNHILIGHT), ::GetSysColor(COLOR_BTNSHADOW));
    		}
    		else
    		{
    			// Draw top-left borders
    			// White line
    			CPen* pOldPen = pDC->SelectObject(&penBtnHiLight);
    			pDC->MoveTo(pRect->left, pRect->bottom-1);
    			pDC->LineTo(pRect->left, pRect->top);
    			pDC->LineTo(pRect->right, pRect->top);
    			// Light gray line
    			pDC->SelectObject(pen3DLight);
    			pDC->MoveTo(pRect->left+1, pRect->bottom-1);
    			pDC->LineTo(pRect->left+1, pRect->top+1);
    			pDC->LineTo(pRect->right, pRect->top+1);
    			// Draw bottom-right borders
    			// Black line
    			pDC->SelectObject(pen3DDKShadow);
    			pDC->MoveTo(pRect->left, pRect->bottom-1);
    			pDC->LineTo(pRect->right-1, pRect->bottom-1);
    			pDC->LineTo(pRect->right-1, pRect->top-1);
    			// Dark gray line
    			pDC->SelectObject(penBtnShadow);
    			pDC->MoveTo(pRect->left+1, pRect->bottom-2);
    			pDC->LineTo(pRect->right-2, pRect->bottom-2);
    			pDC->LineTo(pRect->right-2, pRect->top);
    			//
    			pDC->SelectObject(pOldPen);
    		} // else
    	} // else
    	return BTNST_OK;
    }
    void DrawTheSTButtonText(HWND hWnd, CDC* pDC, LPCTSTR lpszText, RECT* rpItem, CRect* rpCaption, IN const BOOL bIsPressed, 
    								 IN const BOOL bIsDisabled, IN const BOOL bMouseOnButton, IN const BOOL bIsFocused, 
    								 IN const BOOL bIsCheckBox, IN const CPoint ptPressedOffset, IN const HICON hIcon, 
    								 IN const HBITMAP hBitmap, IN const BYTE byAlign, IN const DWORD style, 
    								 IN const short nBitmapPadding, IN const COLORREF colorForeIn,IN const COLORREF colorForeFocus,
    								 IN const COLORREF colorForeOut,IN const COLORREF colorBackIn,
    								 IN const COLORREF colorBackFocus,IN const COLORREF colorBackOut, IN const bool bButtonIsWindow)
    {
    	// Draw the button's title
    	// If button is pressed then "press" title also
    	if (bIsPressed && bIsCheckBox == FALSE)
    		rpCaption->OffsetRect(ptPressedOffset.x, ptPressedOffset.y);
    	// ONLY FOR DEBUG 
    	//CBrush brBtnShadow(RGB(255, 0, 0));
    	//pDC->FrameRect(rCaption, &brBtnShadow);
    	CTempThemeHandle*	pTempHandleTheme = NULL;
    	int					nState;
    //	CRect					rectTest = *rpCaption;
    	//CFont					fontText;
    	int					nSaveDC;
    //	CFont*				pOldFont = NULL;
    	int					nTotalBlockToCentre = 0;
    	int					nLeftOffsetForBlock = 0;
    	int					nOffsetForCaption = 0;
    	COLORREF				colourThemeText = COLOR_BLACK;
    	bool					bDrawThemeText = true;
    	// Back up DC
    	nSaveDC = pDC->SaveDC();
    	// Center text
    	CRect centerRect = rpCaption;
    //	bool bHaveBitmap = (m_csIcons[0].hIcon || m_csBitmaps[0].hBitmap);
    	bool bHaveBitmap = (hIcon || hBitmap);
    	CRect rectItem = rpItem;
    	// Calculate button state for XP purposes (even if we don't have XP, saves us repeating it)
    	if (bIsPressed)
    	{
    		nState = PBS_PRESSED;
    	}
    	else if (bIsDisabled)
    	{
    		nState = PBS_DISABLED;
    	}
    	else if (bMouseOnButton)
    	{
    		nState = PBS_HOT;
    	}
    	else
    	{
    		nState = PBS_NORMAL;
    	}
    	// Unicode only! Cause DrawThemeText requires Unicode, and it's just tiresome to convert
    	// DP 13/03/2007: In ANSI it's next door to impossible. If we try using the theme text drawing function, 
    	// we run into the problem of trying to determine the correct text rect, which means obtaining the theme font. 
    	// This means calling Unicode-only functions, then building LOGFONTs from LOGFONTW's.... life is just too short.
    #ifdef _UNICODE
    	// Open theme handle if using styles
    	if (CVisualStylesXP::GetInstance()->IsAppUsingThemes())
    	{
    		pTempHandleTheme = new CTempThemeHandle(hWnd, L"BUTTON");
    		// Work out whether the theme colour for text is the same as the foreground colour we need. If not, don't use the theme function.
    		CVisualStylesXP::GetInstance()->GetThemeColor(pTempHandleTheme->GetThemeHandle(), BP_PUSHBUTTON, PBS_NORMAL, TMT_TEXTCOLOR, &colourThemeText);
    		if (colourThemeText != colorForeOut)
    		{
    			bDrawThemeText = false;
    		}
    	}
    #endif
    	// Apply left and right border to ensure text doesn't paint over them
    	rpCaption->left += ::GetSystemMetrics(SM_CXBORDER);
    	rpCaption->right -= ::GetSystemMetrics(SM_CXBORDER);
    	// CALCULATE the rect for the text (don't draw it!). If we selected the theme font previously, it'll be taken into account
    	pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER | DT_CALCRECT);
    	// Patch to make left alignment work, left-horizontal mode only
    	if (byAlign == CButtonST::ST_ALIGN_HORIZ)
    	{
    		if (style & BS_LEFT)
    		{
    			rpCaption->OffsetRect(0, (centerRect.Height() - rpCaption->Height())/2);
    		}
    		else if (style & BS_RIGHT)
    		{
    			// ???
    			//ASSERT(false);
    			TRACE0("Right-aligned button text not supported right now. Come back next week.\n");
    		}
    		// Centre case - centre relative to ENTIRE button
    		// Then adjust to avoid any overlap with icon
    		else
    		{
    			// DP 13/07/2005 Change to stop caption rect being set automatically to client rect of button window
    			// Instead, we calculate offsets within the original rect we received
    			if (!bButtonIsWindow)
    			{
    				if (bHaveBitmap)
    				{
    					// The total offset for caption minus button border (bitmap padding) plus caption (title width) gives us the
    					// whole block we need to centre. Subtract this from the total button width. Divide remainder by two.
    					// This number, plus current caption offset minus button border, gives the offset for the text
    					nTotalBlockToCentre = rpCaption->left - rectItem.left - nBitmapPadding + rpCaption->Width();
    					nLeftOffsetForBlock = (rectItem.Width() - nTotalBlockToCentre)/2;
    					nOffsetForCaption = nLeftOffsetForBlock + rpCaption->left - rectItem.left - nBitmapPadding;
    					(*rpCaption).right = rectItem.left + nOffsetForCaption + (*rpCaption).Width();
    					(*rpCaption).left = rectItem.left + nOffsetForCaption;
    					// Centre vertically
    					(*rpCaption).OffsetRect( 0, (rectItem.Height() - rpCaption->Height())/2);
    				}
    				else
    				{
    					(*rpCaption).OffsetRect( (rectItem.Width() - rpCaption->Width())/2, (rectItem.Height() - rpCaption->Height())/2);
    				}
    			}
    			else
    			{
    				(*rpCaption) = CRect(CPoint( (rectItem.Width() - rpCaption->Width())/2, 
    													 (rectItem.Height() - rpCaption->Height())/2 ), rpCaption->Size());
    			}
    			// Ensure no overlap with icon on left
    			if (bHaveBitmap && bButtonIsWindow && centerRect.left + nBitmapPadding > rpCaption->left)
    			{
    				rpCaption->OffsetRect(centerRect.left + nBitmapPadding - rpCaption->left, 0);
    			}
    		}
    	}
    	// Ensure that, if the text rect exceeds the item rect available, we adjust the top of the rect to keep it
    	// centered. The calculation done by DrawText doesn't seem to do this.
    	// TODO: Check that this really works!
    	CRect rcItem = *rpItem;
    	if (rpCaption->Height() > rcItem.Height())
    	{
    		rpCaption->top -= (rpCaption->Height() - rcItem.Height());
    	}
    	/* RFU
    	rpCaption->OffsetRect(0, (centerRect.Height() - rpCaption->Height())/2);
    	rpCaption->OffsetRect((centerRect.Width() - rpCaption->Width())-4, (centerRect.Height() - rpCaption->Height())/2);
    	*/
    	pDC->SetBkMode(TRANSPARENT);
    #ifdef _UNICODE
    	// DrawThemeText requires Unicode. Use it for ANSI too, because the alternative appears not to work.
    	if (CVisualStylesXP::GetInstance()->IsAppUsingThemes() && bDrawThemeText)
    	{
    		CVisualStylesXP::GetInstance()->DrawThemeText(pTempHandleTheme->GetThemeHandle(), 
    			pDC->GetSafeHdc(), BP_PUSHBUTTON, nState, lpszText, -1,  DT_WORDBREAK | DT_CENTER , 
    			NULL, rpCaption);
    	}
    	else
    #endif
    	{
    		if (bIsDisabled)
    		{
    			rpCaption->OffsetRect(1, 1);
    			pDC->SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
    			pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
    			rpCaption->OffsetRect(-1, -1);
    			pDC->SetTextColor(::GetSysColor(COLOR_3DSHADOW));
    			pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
    		} // if
    		else
    		{
    			if (bMouseOnButton || bIsPressed) 
    			{
    				pDC->SetTextColor(colorForeIn);
    				pDC->SetBkColor(colorBackIn);
    			} // if
    			else 
    			{
    				if (bIsFocused)
    				{ 
    					// DP 13/03/2007: No theme support here, because in ANSI it's next door to impossible. 
    					pDC->SetTextColor(colorForeFocus); 
    					pDC->SetBkColor(colorBackFocus); 
    				} // if
    				else 
    				{
    					// DP 13/03/2007: No theme support here, because in ANSI it's next door to impossible. This patch here
    					// doesn't work. If we try using the theme text drawing function, as above, we run into the problem
    					// of trying to determine the correct text rect, which means obtaining the theme font. This means calling
    					// Unicode-only functions, then building LOGFONTs from LOGFONTW's.... life is just too short.
    					pDC->SetTextColor(colorForeOut); 
    					pDC->SetBkColor(colorBackOut); 
    				} // else
    			} // else
    			pDC->DrawText(lpszText, -1, rpCaption, DT_WORDBREAK | DT_CENTER);
    		} // if
    	}
    	// Restore the DC
    	pDC->RestoreDC(nSaveDC);
    	DELETE_SAFE(pTempHandleTheme);
    } 
    //
    // Parameters:
    //		[IN]	bHasTitle
    //				TRUE if the button has a text
    //		[IN]	rpItem
    //				A pointer to a RECT structure indicating the allowed paint area
    //		[IN/OUT]rpTitle
    //				A pointer to a CRect object indicating the paint area reserved for the
    //				text. This structure will be modified if necessary.
    //		[IN]	bIsPressed
    //				TRUE if the button is currently pressed
    //		[IN]	dwWidth
    //				Width of the image (icon or bitmap)
    //		[IN]	dwHeight
    //				Height of the image (icon or bitmap)
    //		[OUT]	rpImage
    //				A pointer to a CRect object that will receive the area available to the image
    //
    void PrepareSTButtonImageRect(BOOL bHasTitle, RECT* rpItem, CRect* rpTitle, BOOL bIsPressed, DWORD dwWidth, DWORD dwHeight, CRect* rpImage, BYTE byAlign, 
    										short nBitmapPadding, POINT ptImageOrg, POINT ptPressedOffset, IN const CRect rectClient, IN const bool bTextLeftAligned, BOOL bIsCheckBox)
    {
    	CRect rBtn;
    	rpImage->CopyRect(rpItem);
    	switch (byAlign)
    	{
    		case CButtonST::ST_ALIGN_HORIZ:
    			if (bHasTitle == FALSE)
    			{
    				// Center image horizontally
    				rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
    			}
    			else
    			{
    				// Image must be placed just inside the focus rect
    				rpImage->left += ptImageOrg.x;  
    				// DP 28/05/2004 Apply padding to text position if left-aligned.
    				// Otherwise, if centred, centre in button irrespective of icon
    //				if (GetStyle() & BS_LEFT)
    				if (bTextLeftAligned)
    				{
    					rpTitle->left += dwWidth + ptImageOrg.x + nBitmapPadding;
    				}
    				else
    				{
    					rpTitle->left += dwWidth + ptImageOrg.x;
    				}
    			}
    			// Center image vertically
    			rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
    			break;
    		case CButtonST::ST_ALIGN_HORIZ_RIGHT:
    			//GetClientRect(&rBtn);
    			if (bHasTitle == FALSE)
    			{
    				// Center image horizontally
    				rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
    			}
    			else
    			{
    				// Image must be placed just inside the focus rect
    				rpTitle->right = rpTitle->Width() - dwWidth - ptImageOrg.x;
    				rpTitle->left = ptImageOrg.x;
    				rpImage->left = rectClient.right - dwWidth - ptImageOrg.x;
    				// Center image vertically
    				rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);
    			}
    			break;
    		
    		case CButtonST::ST_ALIGN_VERT:
    			// Center image horizontally
    			rpImage->left += ((rpImage->Width() - (long)dwWidth)/2);
    			if (bHasTitle == FALSE)
    			{
    				// Center image vertically
    				rpImage->top += ((rpImage->Height() - (long)dwHeight)/2);           
    			}
    			else
    			{
    				rpImage->top = ptImageOrg.y;
    				rpTitle->top += dwHeight;
    			}
    			break;
    		case CButtonST::ST_ALIGN_OVERLAP:
    			break;
    	} // switch
        
    	// If button is pressed then press image also
    	if (bIsPressed && bIsCheckBox == FALSE)
    		rpImage->OffsetRect(ptPressedOffset.x, ptPressedOffset.y);
    } // End of PrepareImageRect
    void DrawTheSTButtonIcon(CDC* pDC, BOOL bHasTitle, RECT* rpItem, CRect* rpCaption, BOOL bIsPressed, BOOL bIsDisabled, 
    								 HICON hIcon, int nIconWidth, int nIconHeight, BYTE byAlign, 
    										short nBitmapPadding, POINT ptImageOrg, POINT ptPressedOffset, IN const CRect rectClient, IN const bool bTextLeftAligned, 
    										BOOL bIsCheckBox)
    {
    	CRect	rImage;
    	PrepareSTButtonImageRect(bHasTitle, rpItem, rpCaption, bIsPressed, nIconWidth, nIconHeight, &rImage, byAlign, 
    										nBitmapPadding, ptImageOrg, ptPressedOffset, rectClient, bTextLeftAligned, bIsCheckBox);
    	// Ole'!
    	pDC->DrawState(rImage.TopLeft(),
    					rImage.Size(), 
    					hIcon,
    					(bIsDisabled ? DSS_DISABLED : DSS_NORMAL), 
    					(CBrush*)NULL);
    } // End of DrawTheIcon

    - Pfft. Coddled kids. In my day, we used to telnet to port 80, then render the page with pencil and paper-- and that's the way we liked it!
    - Pshaw! Youngster. Your UID barely fits inside 16 bits. In _my_ day we had to whistle the 1's and 0's through an acoustic coupler!

    Tools that support "all of UTF-8 as long as it starts with ASCII" and tools that cannot handle these three [BOM] bytes at all are not really supporting UTF-8.
    - Michael Kaplan

    GeneralRe: XP Themes Pin
    David Pritchard3-Aug-11 0:05
    David Pritchard3-Aug-11 0:05 
    GeneralRe: XP Themes Pin
    Ravi Bhavnani3-Aug-11 1:18
    professionalRavi Bhavnani3-Aug-11 1:18 
    GeneralRe: XP Themes Pin
    David Pritchard3-Aug-11 9:49
    David Pritchard3-Aug-11 9:49 
    GeneralBug fix for FooButton::reset() Pin
    JavaBear23-Apr-07 8:07
    JavaBear23-Apr-07 8:07 
    Generalneed to initialize code at reset() function... Pin
    Hualsoo,KIM5-Jan-07 2:33
    Hualsoo,KIM5-Jan-07 2:33 
    GeneralRe: need to initialize code at reset() function... Pin
    Ravi Bhavnani11-Jan-07 2:12
    professionalRavi Bhavnani11-Jan-07 2:12 
    QuestionIs this possible? Pin
    Neounk21-Dec-06 7:58
    Neounk21-Dec-06 7:58 
    AnswerRe: Is this possible? Pin
    Ravi Bhavnani21-Dec-06 8:12
    professionalRavi Bhavnani21-Dec-06 8:12 
    GeneralRe: Is this possible? Pin
    Neounk21-Dec-06 10:37
    Neounk21-Dec-06 10:37 
    GeneralAntibounce on check-button type Pin
    roby6812-Dec-06 20:34
    roby6812-Dec-06 20:34 
    GeneralBug in OnLButtonUp() [modified] Pin
    Alex Cohn7-Dec-06 3:23
    Alex Cohn7-Dec-06 3:23 
    AnswerRe: Bug in OnLButtonUp() Pin
    Ravi Bhavnani7-Dec-06 3:47
    professionalRavi Bhavnani7-Dec-06 3:47 
    GeneralAbout warning C4482... Pin
    ILoveMJ30-Nov-06 21:06
    ILoveMJ30-Nov-06 21:06 
    AnswerRe: About warning C4482... Pin
    Ravi Bhavnani1-Dec-06 8:12
    professionalRavi Bhavnani1-Dec-06 8:12 
    AnswerRe: About warning C4482... Pin
    phord9-Aug-07 8:22
    phord9-Aug-07 8:22 
    GeneralButton Color Pin
    jamesroy30-Nov-06 15:58
    jamesroy30-Nov-06 15:58 
    AnswerRe: Button Color Pin
    Ravi Bhavnani1-Dec-06 8:11
    professionalRavi Bhavnani1-Dec-06 8:11 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.