Click here to Skip to main content
15,886,578 members
Articles / Desktop Programming / ATL

Simulate COM Connection Points from a .NET Library

Rate me:
Please Sign up or sign in to vote.
4.91/5 (25 votes)
8 Mar 2006CPOL14 min read 63.3K   1.4K   36  
An article on accessing a VB.NET library from a MFC/ATL COM client.
//  FooButton.h
//  A versatile bitmap button
//  Copyright (c) 1997-2005 Ravi Bhavnani (ravib@ravib.com)
//
//  You are allowed to include the source code in any product (commercial, shareware,
//  freeware or otherwise) when your product is released in binary form.  You are allowed
//  to modify the source code in any way you want except you cannot modify the copyright
//  details at the top of each module.  If you want to distribute source code with your
//  application, then you are only allowed to distribute versions released by the author.
//  This is to maintain a single distribution point for the source code. 

#include "stdafx.h"
#include "FooButton.h"

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

#ifndef COLOR_HOTLIGHT
#define COLOR_HOTLIGHT 26
#endif

// Pointy finger cursor is defined on Win2000 and up
#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif

#define C_BitmapBorder_X      5     // horizontal border around bitmap for text buttons
#define C_DropDown_Width      5     // width of dropdown indicator
#define C_DropDown_Height     3     // height of dropdown indicator
#define C_DropDown_X          10    // border between left edge of drop down indicator and right edge of button
#define C_DropDown_Y          8     // border between top of drop down indicator and bottom of button
#define C_BitmapCheckGap      5     // gap between right edge of bitmap and left edge of checkbox

#define C_HyperlinkColor      RGB (0, 0, 224)   // hyperlink color

int FooButton::m_nRefs = 0;                               // # FooButton instances
CMapStringToPtr FooButton::m_btnGroups;                   // button groups
HINSTANCE FooButton::m_hMsimg32 = NULL;                   // handle to MSIMG32.dll
MSIMG32callBack FooButton::m_dllGradientFillFunc = NULL;  // pointer to GradientFill() API

// Gradient colors
COLORREF FooButton::m_rgbTopGradient = RGB (255, 255, 255);
COLORREF FooButton::m_rgbBottomGradient = RGB (200, 200, 216);
COLORREF FooButton::m_rgbTopGradientHot = RGB (255, 255, 255);
COLORREF FooButton::m_rgbBottomGradientHot = RGB (220, 220, 220);

/////////////////////////////////////////////////////////////////////////////
// FooButton

//! Standard constructor.
FooButton::FooButton()
{
  // Set default properties
  setType (FooButton::Type::pushButton);
  setTextStyle (FooButton::Text::singleLineCenter);
  setTextColor (GetSysColor (COLOR_BTNTEXT));
  setFocusStyle (FooButton::Focus::noFocus);
  setBitmapId (0);

  // Initialize state
  m_bChecked = false;
  m_bTracking = false;
  m_bMultiClicked = false;
  m_bDefaultButton = false;
  m_bGradient = false;

  // Load MSIMG32.dll if possible
  if (m_nRefs == 0) {
      ASSERT (m_hMsimg32 == NULL);
      m_hMsimg32 = LoadLibrary (_T("msimg32.dll"));
      if (m_hMsimg32 != NULL) {
          m_dllGradientFillFunc =
            ((MSIMG32callBack) GetProcAddress (m_hMsimg32, "GradientFill"));
          ASSERT (m_dllGradientFillFunc != NULL);
      }
  }

  // Keep track of how many instances exist
  FooButton::m_nRefs++;
}

//! Standard destructor.
FooButton::~FooButton()
{
  // Free button group storage and MSIMG32.dll
  FooButton::m_nRefs--;
  if (FooButton::m_nRefs == 0) {
      FooButton::reset();
      if (m_hMsimg32 != NULL) {
          VERIFY (FreeLibrary (m_hMsimg32));
          m_dllGradientFillFunc = NULL;
      }
  }
}

BEGIN_MESSAGE_MAP(FooButton, CButton)
	//{{AFX_MSG_MAP(FooButton)
  ON_WM_ERASEBKGND()
  ON_WM_KILLFOCUS()
  ON_WM_LBUTTONDOWN()
  ON_WM_LBUTTONUP()
  ON_WM_MOUSEMOVE()
  ON_WM_SETCURSOR()
  ON_WM_SETFOCUS()
  ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// FooButton operations

//! Resets static storage used by the class.  This method should be called
//! when the application exits.
void FooButton::reset()
{
  POSITION pos = m_btnGroups.GetStartPosition();
  while (pos != NULL) {
    CString strGroupName;
    void* pGroup = NULL;
    m_btnGroups.GetNextAssoc (pos, strGroupName, pGroup);
    ASSERT (pGroup != NULL);
    delete (CPtrArray *) pGroup;
  }
}

//! Sets the button's type.
//! @param  type    The button's type.
void FooButton::setType
  (FooButton::Type type)
{
  // Initialize button properties
  m_bDropDown = false;
  m_bMulti = false;
  m_bStatic = false;
  m_bHot = false;
  m_bHyperlink = false;

  // Set button type
  switch (type) {
    case FooButton::Type::staticButton:
      m_bStatic = true;
      break;

    case FooButton::Type::checkBox:
    case FooButton::Type::radio:
    case FooButton::Type::checkButton:
    case FooButton::Type::pushButton:
      break;

    case FooButton::Type::pushButtonDropDown:
      m_bDropDown = true;
      break;

    case FooButton::Type::hotCheckButton:
    case FooButton::Type::hotPushButton:
      m_bHot = true;
      break;

    case FooButton::Type::hotPushButtonDropDown:
      m_bHot = true;
      m_bDropDown = true;
      break;

    case FooButton::Type::pushButtonMulti:
      m_bMulti = true;
      break;

    case FooButton::Type::hotPushButtonMulti:
      m_bHot = true;
      m_bMulti = true;
      break;

    case FooButton::Type::hyperlink:
      m_bHyperlink = true;
      break;

    default:
      ASSERT (FALSE);
      return;
  }
  m_type = type;

  // Reset checked mode
  if ((m_type != FooButton::Type::checkButton) &&
      (m_type != FooButton::Type::hotCheckButton))
     m_bChecked = false;

  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
}

//! Sets the button's text style.
//! @param  textStyle   The button's text style.
void FooButton::setTextStyle
  (FooButton::Text textStyle)
{
  // Reset text styles
  m_bText = false;
  m_bCenter = false;
  m_bMultiLine = false;

  // Set text style
  switch (textStyle) {
    case FooButton::Text::none:
      break;

    case FooButton::Text::singleLine:
      m_bText = true;
      break;

    case FooButton::Text::singleLineCenter:
      m_bText = true;
      m_bCenter = true;
      break;

    case FooButton::Text::multiLine:
      m_bText = true;
      m_bMultiLine = true;
      break;

    default:
      ASSERT (FALSE);
      return;
  }
  m_textStyle = textStyle;

  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
}

//! Sets the button's focus style.
//! @param  focusStyle   The button's focus style.
void FooButton::setFocusStyle
  (FooButton::Focus focusStyle)
{
  m_focusStyle = focusStyle;
  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
}

//! Sets the button's bitmap id.
//! @param  nBitmapId       Bitmap id (-1 means none).
//! @param  rgbTransparent  Bitmap's transparency color (default = RGB(255,0,255)
void FooButton::setBitmapId
  (int nBitmapId,
   COLORREF rgbTransparent)
{
  m_nBitmapId = nBitmapId;
  m_rgbTransparent = rgbTransparent;
  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
}

//! Sets the button's checked state if the button type is checkButton or
//! hotCheckButton.
//! @param    bChecked    Flag: button is checked.
//! @return   The button's checked state.
void FooButton::check
  (bool bChecked)
{
  if ((m_type == FooButton::Type::checkButton) ||
      (m_type == FooButton::Type::hotCheckButton) ||
      (m_type == FooButton::Type::checkBox) ||
      (m_type == FooButton::Type::radio)) {
      m_bChecked = bChecked;
      HWND hWnd = GetSafeHwnd();
      if ((hWnd != NULL) && ::IsWindow (hWnd))
         Invalidate();
  }

  // If checked, inform the button's parent group (if any)
  if (isChecked() && !m_strButtonGroup.IsEmpty()) {

      // Get at the button's parent group
      void* pGroup = NULL;
      getButtonGroup (m_strButtonGroup, pGroup);
      ASSERT (pGroup != NULL);
      int nButtons = ((CPtrArray *) pGroup)->GetSize();

      // Uncheck the other buttons in the group
      for (int nIndex=0; (nIndex < nButtons); nIndex++) {
          HWND hWnd = (HWND) ((CPtrArray *) pGroup)->GetAt (nIndex);
          ASSERT (hWnd != NULL);
          if (hWnd != GetSafeHwnd()) {
              if (IsWindow (hWnd)) {
                  FooButton* pButton = (FooButton *) CWnd::FromHandle (hWnd);
                  ASSERT (pButton != NULL);
                  if ((pButton != this) && pButton->isChecked())
                     pButton->check (false);
              }
          }
      }
  }
}

//! Sets a multi pushbutton to its normal unpressed state.
void FooButton::clearMultiClick()
{
  if ((m_type == FooButton::Type::pushButtonMulti) ||
      (m_type == FooButton::Type::hotPushButtonMulti)) {
      m_bMultiClicked = false;
      HWND hWnd = GetSafeHwnd();
      if ((hWnd != NULL) && ::IsWindow (hWnd))
         Invalidate();
  }
}

//! Displays a popup menu if the button type is pushButton, pushButtonDropDown,
//! hotPushButton or hotPushButtonDropDown.
//! @param    pMenu   The popup menu to be displayed.
//! @return   The menu selection.
int FooButton::displayPopupMenu
  (CMenu* pMenu)
{
  Invalidate();
  if (pMenu != NULL) {
      CRect  rectButton;
      GetWindowRect (&rectButton);
      return (pMenu->TrackPopupMenu
                (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD,
                rectButton.left, rectButton.bottom, GetParent()));
  }
  return (0);
}

//! Enables/disables the button.
//! @param    bEnable   Flag: button is enabled.
void FooButton::enable
  (bool bEnable)
{
  EnableWindow (bEnable ? TRUE : FALSE);
  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
}

//! Adds the button to a named button group.
//! @param    strGroupName    Name of button group.
//! @return   true if successful, false otherwise.
bool FooButton::addToGroup
  (CString strGroupName)
{
  // Group name must be specified
  if (strGroupName.IsEmpty())
     return (false);

  // Create the group if it doesn't exist
  void* pGroup = NULL;
  getButtonGroup (strGroupName, pGroup);
  if (pGroup == NULL) {
      pGroup = new CPtrArray();
      ASSERT (pGroup != NULL);
      m_btnGroups.SetAt (strGroupName, pGroup);
  }

  // Remove the button from its current group
  removeFromGroup();

  // Add button's HWND to group if not already present
  m_strButtonGroup = strGroupName;
  int nButtons = ((CPtrArray *) pGroup)->GetSize();
  for (int nIndex=0; (nIndex < nButtons); nIndex++)
      if (((CPtrArray *) pGroup)->GetAt (nIndex) == this)
         return (true);
  ((CPtrArray *) pGroup)->Add (GetSafeHwnd());

  return (true);
}

//! Removes the button from its parent group.
//! @param    pGroup    The FooButtonGroup.
//! @return   true if successful, false otherwise.
bool FooButton::removeFromGroup()
{
  // Return if not a member of a button group
  if (m_strButtonGroup.IsEmpty())
     return (false);

  // Return if group doesn't exist
  void* pGroup = NULL;
  getButtonGroup (m_strButtonGroup, pGroup);
  if (pGroup == NULL) {
      ASSERT (FALSE);
      return (false);
  }

  // Remove the button's HWND from the group
  int nButtons = ((CPtrArray *) pGroup)->GetSize();
  for (int nIndex=0; (nIndex < nButtons); nIndex++) {
      if (((CPtrArray *) pGroup)->GetAt (nIndex) == GetSafeHwnd()) {
          ((CPtrArray *) pGroup)->RemoveAt (nIndex);
          m_strButtonGroup.Empty();
          return (true);
      }
  }

  // Button wasn't part of the group
  ASSERT (FALSE);
  return (false);
}

/////////////////////////////////////////////////////////////////////////////
// FooButton message handlers

void FooButton::PreSubclassWindow() 
{

#ifdef _DEBUG
  // We really should be only subclassing a button control
	TCHAR buffer[255];
	GetClassName (m_hWnd, buffer, sizeof(buffer) / sizeof(TCHAR));
	ASSERT (CString (buffer) == _T("Button"));
#endif

  // Check if it's a default button
	if (GetStyle() & 0x0FL)
     m_bDefaultButton = true;

  // Make the button owner-drawn
	ModifyStyle (0x0FL, BS_OWNERDRAW, SWP_FRAMECHANGED);
	CButton::PreSubclassWindow();
}

void FooButton::OnKillFocus(CWnd* pNewWnd)
{
  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();

  CButton::OnKillFocus (pNewWnd);
}

void FooButton::OnLButtonDown (UINT nFlags, CPoint point)
{
  multiHitTest (point);   // sets m_bMultiClicked
  CButton::OnLButtonDown (nFlags, point);
}

void FooButton::OnLButtonUp (UINT nFlags, CPoint point)
{
  // If this is a checkbutton...
  if ((m_type == FooButton::Type::checkButton) ||
      (m_type == FooButton::Type::hotCheckButton) ||
      (m_type == FooButton::Type::checkBox) ||
      (m_type == FooButton::Type::radio)) {

      // Check ourself if mouse was released over button
      CRect rectButton;
      GetClientRect (&rectButton);
      if (rectButton.PtInRect (point))
         if ((m_type != FooButton::Type::radio) && m_strButtonGroup.IsEmpty())
            check (!isChecked());
         else
            check (true);
  }

  // Standard handling
  CButton::OnLButtonUp (nFlags, point);
  m_bMultiClicked = false;
  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
}

void FooButton::OnMouseMove(UINT nFlags, CPoint point) 
{
  // Start tracking the mouse if we're a hot button
  if (m_bHot && !m_bTracking) {

		  TRACKMOUSEEVENT tme;
		  tme.cbSize = sizeof(tme);
		  tme.hwndTrack = m_hWnd;
		  tme.dwFlags = TME_LEAVE;
		  tme.dwHoverTime = HOVER_DEFAULT;
		  m_bTracking = _TrackMouseEvent(&tme);

      HWND hWnd = GetSafeHwnd();
      if ((hWnd != NULL) && ::IsWindow (hWnd))
         Invalidate();
  }

  // Do default action
	CButton::OnMouseMove(nFlags, point);
}

LRESULT FooButton::OnMouseLeave
  (WPARAM wParam,
   LPARAM lParam)
{
  // To ignore "unreferenced formal parameter" warning
  wParam = wParam;
  lParam = lParam;

	m_bTracking = false;
	m_bMultiClicked = false;

  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();
	return 0;
}

BOOL FooButton::OnEraseBkgnd(CDC* pDC) 
{
  // Draw gray background
  RECT  rect;
  GetClientRect (&rect);
  pDC->FillSolidRect (&rect, ::GetSysColor (COLOR_BTNFACE));
	return CButton::OnEraseBkgnd(pDC);
}

BOOL FooButton::OnSetCursor (CWnd* /* pWnd */, UINT /* nHitTest */, UINT /* message */)
{
  if (m_bHyperlink) {
      HCURSOR hCursor = ::LoadCursor (NULL, IDC_HAND);
      if (NULL != hCursor)
         ::SetCursor (hCursor);
  }
  return FALSE;
}

void FooButton::OnSetFocus(CWnd* pOldWnd)
{
  HWND hWnd = GetSafeHwnd();
  if ((hWnd != NULL) && ::IsWindow (hWnd))
     Invalidate();

  CButton::OnSetFocus (pOldWnd);
}

void FooButton::DrawItem (LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
  CDC* pDC = CDC::FromHandle (lpDrawItemStruct->hDC);
  ASSERT (pDC != NULL);

  // Draw frame for non-radio and non-checkbox buttons
  if ((m_type != FooButton::Type::checkBox) && (m_type != FooButton::Type::radio))
     drawFrame (pDC, lpDrawItemStruct);

  // Draw button bitmap
  int nBmpRightEdge = 0;
  drawBitmap (pDC, lpDrawItemStruct, nBmpRightEdge);

  // Draw frame for radio and checkbox buttons
  if ((m_type == FooButton::Type::checkBox) || (m_type == FooButton::Type::radio))
     drawRadioCheckFrame (pDC, lpDrawItemStruct, nBmpRightEdge);

  // Draw drop-down indicator
  drawDropDown (pDC, lpDrawItemStruct);

  // Draw caption
  int nCaptionRightEdge = 0;
  drawCaption (pDC, lpDrawItemStruct, nBmpRightEdge, nCaptionRightEdge);

  // Draw focus rectangle
  drawFocus (pDC, lpDrawItemStruct, nBmpRightEdge, nCaptionRightEdge);

  // Draw default button border
  drawDefaultBorder (pDC, lpDrawItemStruct);

  return;
}

/////////////////////////////////////////////////////////////////////////////
// FooButton helper functions

//! Draws the button's frame
void FooButton::drawFrame
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);

  // Draw button outline if we're not a static button or a hyperlink
  if (!m_bStatic && !m_bHyperlink) {
      if (m_bHot && !m_bChecked)
          drawHotButtonFrame (lpDrawItemStruct);
      else {
          // Draw frame for non-hot button
          UINT uFrameCtrl = DFCS_BUTTONPUSH;
          if (m_bChecked)
             uFrameCtrl |= DFCS_CHECKED;
          else
             if (((lpDrawItemStruct->itemState & ODS_SELECTED) == ODS_SELECTED) && !m_bMulti)
                uFrameCtrl |= DFCS_PUSHED;
          if ((lpDrawItemStruct->itemState & ODS_DISABLED) == ODS_DISABLED)
             uFrameCtrl |= DFCS_INACTIVE;
          pDC->DrawFrameControl (&(lpDrawItemStruct->rcItem), DFC_BUTTON , uFrameCtrl);

          // Fill frame with gradient background, if required
          if (isGradient()) {

              CRect rect = lpDrawItemStruct->rcItem;
              rect.left++;
              rect.top++;
              rect.right -= 2;
              rect.bottom -= 2;

              COLORREF rgbTop = (m_bTracking ? m_rgbTopGradientHot : m_rgbTopGradient);
              COLORREF rgbBottom = (m_bTracking ? m_rgbBottomGradientHot : m_rgbBottomGradient);

              drawGradientRect (pDC, rect, rgbTop, rgbBottom, true);
          }

          // For multi pushbuttons, fill the area of the drop-down indicator with
          // a light color if the multi button is currently pressed
          if (m_bMulti && m_bMultiClicked)
             drawMultiDropDownRegion (pDC, lpDrawItemStruct);
      }
  }
}

//! Draws the button's frame for a radio/checkbox button
void FooButton::drawRadioCheckFrame
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct,
   int& nLeftEdge)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);

  // Nothing to do if not a radiobutton or checkbox
  if ((m_type != FooButton::Type::checkBox) && (m_type != FooButton::Type::radio))
     return;

  // Compute frame dimensions
  CRect rectFrame = lpDrawItemStruct->rcItem;
  rectFrame.left = nLeftEdge;
  if (nLeftEdge > 0)
     rectFrame.left += C_BitmapCheckGap;

  int nFrameWidthHeight = GetSystemMetrics (SM_CXSIZE) - 5;
  rectFrame.right = rectFrame.left + nFrameWidthHeight;
  int nOffset = (rectFrame.Height() - nFrameWidthHeight) / 2;
  rectFrame.top += nOffset;
  rectFrame.bottom = rectFrame.top + nFrameWidthHeight;
  nLeftEdge = rectFrame.right;

  // Draw frame
  UINT uFrameCtrl = (m_type == FooButton::Type::checkBox ? DFCS_BUTTONCHECK : DFCS_BUTTONRADIO);
  if (m_bChecked)
     uFrameCtrl |= DFCS_CHECKED;
  if ((lpDrawItemStruct->itemState & ODS_SELECTED) == ODS_SELECTED)
     uFrameCtrl |= DFCS_PUSHED;
  if ((lpDrawItemStruct->itemState & ODS_DISABLED) == ODS_DISABLED)
     uFrameCtrl |= DFCS_INACTIVE;
  pDC->DrawFrameControl (rectFrame, DFC_BUTTON , uFrameCtrl);
}

//! Draws the button's bitmap
void FooButton::drawBitmap
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct,
   int& nBitmapRightEdge)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);

  // Load bitmap - return if unable
  nBitmapRightEdge = 0;
  if (m_nBitmapId == 0)
     return;
  HBITMAP hBitmap = ::LoadBitmap (AfxGetResourceHandle(), MAKEINTRESOURCE (m_nBitmapId));
  ASSERT (hBitmap != NULL);
  if (hBitmap == NULL)
     return;

  // Get bitmap dimensions
  BITMAP  rBitmap;
  ::GetObject (hBitmap, sizeof (BITMAP), &rBitmap);
  CSize bitmapSize;
  bitmapSize.cx = rBitmap.bmWidth;
  bitmapSize.cy = rBitmap.bmHeight;

  // Position the bitmap - if the button contains no text or is a checkbox/radio,
  // leave C_BitmapBorder_X pixels from the L edge.  Otherwise, center it horizontally.
  CRect rectButton;
  GetClientRect (&rectButton);
  CPoint pt;
  if (m_bText || (m_type == FooButton::Type::checkBox) || (m_type == FooButton::Type::radio))
     pt.x = rectButton.left + C_BitmapBorder_X;
  else
     pt.x = (rectButton.right - rBitmap.bmWidth) / 2;
  pt.y = (rectButton.bottom - rBitmap.bmHeight) / 2;
  if ((m_type != FooButton::Type::checkBox) && (m_type != FooButton::Type::radio))
     if (!m_bStatic && !m_bHyperlink && !m_bMultiClicked)
        if (lpDrawItemStruct->itemState & ODS_SELECTED)
           pt.Offset (1, 1);

  // Draw the bitmap
  if ((lpDrawItemStruct->itemState & ODS_DISABLED) == ODS_DISABLED)
      DisabledBlt (pDC->m_hDC,
                   pt.x, pt.y,
                   rBitmap.bmWidth, rBitmap.bmHeight,
                   hBitmap,
                   0, 0);
  else
      TransparentBlt (pDC->m_hDC,
                      pt.x, pt.y,
                      rBitmap.bmWidth, rBitmap.bmHeight,
                      hBitmap,
                      0, 0,
                      m_rgbTransparent,
                      NULL);

  // Return right edge of bitmap
  nBitmapRightEdge = pt.x + rBitmap.bmWidth;

  // Free bitmap
  ::DeleteObject (hBitmap);
}

//! Draws the button's caption
void FooButton::drawCaption
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct,
   int nLeftEdge,
   int& nRightEdge)
{
  // Nothing to do if the isn't set up to display text
  nRightEdge = 0;
  if (!m_bText)
     return;

  // Tweak edges
  if (!m_bCenter)
     nLeftEdge += C_BitmapBorder_X; // looks nicer
  CRect rectButton;
  GetClientRect (&rectButton);
  nRightEdge = rectButton.right;
  if (m_bDropDown)
     nRightEdge -= 6;
  else
     if (m_bMulti) {
        // bool bBitmap = (m_nBitmapId != 0);
        // nRightEdge -= (bBitmap ? 12 : 10);
        nRightEdge -= 12;
     }

  // Get available caption width - return if none
  int nAvailableWidth = nRightEdge - nLeftEdge;
  if (nAvailableWidth <= 0)
     return;

  // Get caption text
  CString strCaption;
  GetWindowText (strCaption);

  // Determine drawing format
  DWORD  dwFormat = DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
  dwFormat |= (m_bCenter ? DT_CENTER : DT_LEFT);
  if (m_bMultiLine)
     dwFormat = DT_WORDBREAK | DT_LEFT | DT_WORD_ELLIPSIS | DT_VCENTER;

  // Determine dimensions of caption
  CRect rectCaption;
  GetClientRect (&rectCaption);
  int nHeight = ::DrawText (pDC->m_hDC, strCaption.GetBuffer(0),
                            strCaption.GetLength(),
                            &rectCaption,
                            dwFormat | DT_CALCRECT);
  strCaption.ReleaseBuffer();
  int nWidth = rectCaption.Width();

  // Position caption horizontally within available area
  rectCaption.left = nLeftEdge;
  rectCaption.right = nRightEdge;
  if (m_bCenter && (nAvailableWidth > nWidth)) {
      int nOffsetX = (nAvailableWidth - nWidth) / 2;
      rectCaption.left += nOffsetX;
      rectCaption.right -= nOffsetX;
  }

  // Position caption vertically within button
  int nOffsetY = (rectButton.Height() - nHeight)/2;
  rectCaption.top += nOffsetY;
  rectCaption.bottom = rectCaption.top + nHeight;

  // Offset caption if button is pressed
  if ((m_type != FooButton::Type::checkBox) && (m_type != FooButton::Type::radio))
     if (!m_bStatic && !m_bHyperlink && !m_bMultiClicked)
        if (lpDrawItemStruct->itemState & ODS_SELECTED)
           rectCaption.OffsetRect (1, 1);

  // Set text color
  pDC->SetBkMode (TRANSPARENT);
  pDC->SetBkColor (::GetSysColor (COLOR_BTNFACE));
  pDC->SetTextColor (m_bHyperlink ? C_HyperlinkColor : m_rgbText);

  // Set underline mode if hyperlink
  CFont* pOldFont = NULL;
  CFont underlineFont;
  if (m_bHyperlink) {
      LOGFONT logFont;
      VERIFY (GetFont()->GetLogFont (&logFont));
      logFont.lfUnderline = TRUE;
      VERIFY (underlineFont.CreateFontIndirect (&logFont));
      pOldFont = pDC->SelectObject (&underlineFont);
  }

  // Draw caption
  if ((lpDrawItemStruct->itemState & ODS_DISABLED) == ODS_DISABLED)
     {
       ::OffsetRect (&rectCaption, 1, 1);
       ::SetTextColor (pDC->m_hDC, ::GetSysColor (COLOR_3DHILIGHT));
       ::DrawTextEx (pDC->m_hDC, strCaption.GetBuffer(0), strCaption.GetLength(),
                     &rectCaption,
                     dwFormat,
                     NULL);
        strCaption.ReleaseBuffer();

       ::OffsetRect (&rectCaption, -1, -1);
       ::SetTextColor (pDC->m_hDC, ::GetSysColor (COLOR_GRAYTEXT));
       ::DrawTextEx (pDC->m_hDC, strCaption.GetBuffer(0), strCaption.GetLength(),
                     &rectCaption,
                     dwFormat,
                     NULL);
     }
  else
     ::DrawTextEx (pDC->m_hDC, strCaption.GetBuffer(0), strCaption.GetLength(),
                   &rectCaption,
                   dwFormat,
                   NULL);
  strCaption.ReleaseBuffer();

  // Free underline font
  if (pOldFont != NULL)
     pDC->SelectObject (pOldFont);
}

//! Draws the button's drop-down indicator
void FooButton::drawDropDown
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);

  // Drop-down indicator is drawn for drop-down or multi buttons only
  CRect rectButton;
  GetClientRect (&rectButton);
  int nDropDownLeftEdge = rectButton.right;
  if (!m_bDropDown && !m_bMulti)
     return;

  // Create pen of appropriate color
  COLORREF   rgbPen;
  if (m_bHyperlink)
      rgbPen = GetSysColor (COLOR_HOTLIGHT);
  else {
      rgbPen = GetSysColor (COLOR_BTNTEXT);
      if ((lpDrawItemStruct->itemState & ODS_DISABLED) == ODS_DISABLED)
         rgbPen = GetSysColor (COLOR_GRAYTEXT);
  }
  CPen  pen;
  VERIFY (pen.CreatePen (PS_SOLID | PS_INSIDEFRAME, 1, rgbPen));

  // Set X and Y offsets
  int nOffsetX = 0;
  if ((lpDrawItemStruct->itemState & ODS_SELECTED) || m_bMultiClicked)
     nOffsetX = 1;
  int nOffsetY = nOffsetX;

  // Draw the indicator
  CPen* pOldPen = pDC->SelectObject (&pen);
  nDropDownLeftEdge = rectButton.right - C_DropDown_X + nOffsetX;
  drawDropDownIndicator (pDC->m_hDC,
                         nDropDownLeftEdge,
                         rectButton.bottom - C_DropDown_Y + nOffsetY,
                         C_DropDown_Width, C_DropDown_Height);
  pDC->SelectObject (pOldPen);
  nDropDownLeftEdge = rectButton.right - (3 * C_DropDown_X)/2;

  // Draw the vertical separator for multi buttons
  if (m_bMulti && !m_bMultiClicked) {
      rectButton.top += 3;
      rectButton.bottom -= 3;
      rectButton.left = nDropDownLeftEdge;
      rectButton.right = rectButton.left + 2;
      pDC->DrawEdge (&rectButton, BDR_SUNKENOUTER, BF_RECT);
  }
}

//! Draws a multi-button's pressed drop-down region
void FooButton::drawMultiDropDownRegion
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);
  if (!m_bMulti || !m_bMultiClicked)
     return;

  // Get bounds of multi part of button
	CRect rect = lpDrawItemStruct->rcItem;
	rect.left = rect.right - (3 * C_DropDown_X)/2 + 1;
	rect.bottom--;

  // Fill area
  UINT uFrameCtrl = DFCS_BUTTONPUSH | DFCS_CHECKED;
  pDC->DrawFrameControl (&rect, DFC_BUTTON , uFrameCtrl);
}

//! Draws the button's focus rectangle
void FooButton::drawFocus
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct,
   int nLeftEdge,
   int nRightEdge)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);

  // Draw focus rectangle (for non-static non-hot buttons)
  if (!m_bStatic && !m_bHot && (getFocusStyle() != FooButton::Focus::noFocus))
     if ((lpDrawItemStruct->itemState & ODS_FOCUS) == ODS_FOCUS) {
        CRect rectButton;
        GetClientRect (&rectButton);
        rectButton.left = nLeftEdge + 4;
        rectButton.right = nRightEdge - 4;
        rectButton.top += 4;
        rectButton.bottom -= 4;
        ::DrawFocusRect (pDC->m_hDC, &rectButton);
     }
}

//! Draws the border for a default button
void FooButton::drawDefaultBorder
  (CDC* pDC,
   LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  ASSERT (pDC != NULL);
  ASSERT (lpDrawItemStruct != NULL);

  // Only pushbuttons can have a default border
  FooButton::Type btnType = getType();
  if ((btnType != FooButton::Type::pushButton) &&
      (btnType != FooButton::Type::pushButtonDropDown) &&
      (btnType != FooButton::Type::pushButtonMulti))
     return;

  // Nothing to do if the default border isn't required
  if (getFocusStyle() != FooButton::Focus::defaultFocus)
     return;

  // Nothing do if the button doesn't have focus or isn't the default button
  if (((lpDrawItemStruct->itemState & ODS_FOCUS) == 0) || !m_bDefaultButton)
     return;

  COLORREF rgbBorder = GetSysColor (COLOR_3DDKSHADOW);
  CBrush borderBrush (rgbBorder);
  CRect innerRect = lpDrawItemStruct->rcItem;
  if ((lpDrawItemStruct->itemState & ODS_SELECTED) == 0) {

      COLORREF rgbBorder = GetSysColor (COLOR_3DDKSHADOW);
      CPen borderGreyPen (PS_SOLID, 1, rgbBorder);
      CPen* pOldPen = pDC->SelectObject (&borderGreyPen );

      pDC->MoveTo (innerRect.right - 2, innerRect.top + 1);
      pDC->LineTo (innerRect.right - 2, innerRect.bottom - 2);
      pDC->LineTo (innerRect.left, innerRect.bottom - 2);

      COLORREF rgbBrGrey = GetSysColor (COLOR_3DHILIGHT);
      CPen brightGreyPen (PS_SOLID, 1, rgbBrGrey);
      pOldPen = pDC->SelectObject (&brightGreyPen);

      pDC->MoveTo (innerRect.right - 3, innerRect.top + 1);
      pDC->LineTo (innerRect.left + 1, innerRect.top + 1);
      pDC->LineTo (innerRect.left + 1, innerRect.bottom - 2);

      COLORREF rgbDkGrey = GetSysColor (COLOR_BTNSHADOW);
      CPen darkGreyPen (PS_SOLID, 1, rgbDkGrey);
      pOldPen = pDC->SelectObject (&darkGreyPen);
      pDC->MoveTo (innerRect.right - 3, innerRect.top + 2);
      pDC->LineTo (innerRect.right - 3, innerRect.bottom - 3);
      pDC->LineTo (innerRect.left + 1, innerRect.bottom - 3);
      pDC->SelectObject (pOldPen);

  } else {
      innerRect.InflateRect (1, 1, -1, -1);
      pDC->FrameRect (&innerRect, &borderBrush);
  }
  pDC->FrameRect(&lpDrawItemStruct->rcItem, &borderBrush);
}

//
//  Purpose:
//    Copies a bitmap transparently onto the destination DC.
//    See http://www.codeguru.com/Cpp/G-M/bitmap/specialeffects/article.php/c1749/
//
//  Returns:
//    None
//
void FooButton::TransparentBlt
  (HDC      hdcDest,            // destination device context
   int      nXDest,             // x-coord of destination rectangle's upper-left corner
   int      nYDest,             // y-coord of destination rectangle's upper-left corner
   int      nWidth,             // width of destination rectangle
   int      nHeight,            // height of destination rectangle
   HBITMAP  hBitmap,            // source bitmap
   int      nXSrc,              // x-coord of source rectangle's upper-left corner
   int      nYSrc,              // y-coord of source rectangle's upper-left corner
   COLORREF colorTransparent,   // transparent color
   HPALETTE hPal)               // logical palette to be used with bitmap (may be NULL)
{
  CDC dc, memDC, maskDC, tempDC;
  dc.Attach( hdcDest );
  maskDC.CreateCompatibleDC(&dc);
  CBitmap maskBitmap;

  // These store return of SelectObject() calls
  CBitmap* pOldMemBmp = NULL;
  CBitmap* pOldMaskBmp = NULL;
  HBITMAP hOldTempBmp = NULL;

  memDC.CreateCompatibleDC (&dc);
  tempDC.CreateCompatibleDC (&dc);
  CBitmap bmpImage;
  bmpImage.CreateCompatibleBitmap (&dc, nWidth, nHeight);
  pOldMemBmp = memDC.SelectObject (&bmpImage);

  // Select and realize the palette
  if (dc.GetDeviceCaps (RASTERCAPS) & RC_PALETTE && hPal) {
     ::SelectPalette( dc, hPal, FALSE );
     dc.RealizePalette();
     ::SelectPalette( memDC, hPal, FALSE );
  }

  hOldTempBmp = (HBITMAP) ::SelectObject (tempDC.m_hDC, hBitmap);
  memDC.BitBlt (0, 0, nWidth, nHeight, &tempDC, nXSrc, nYSrc, SRCCOPY);

  // Create monochrome bitmap for the mask
  maskBitmap.CreateBitmap (nWidth, nHeight, 1, 1, NULL);
  pOldMaskBmp = maskDC.SelectObject (&maskBitmap);
  memDC.SetBkColor (colorTransparent);

  // Create the mask from the memory DC
  maskDC.BitBlt (0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);

  // Set the background in memDC to black. Using SRCPAINT with black
  // and any other color results in the other color, thus making
  // black the transparent color
  memDC.SetBkColor (RGB (0,0,0));
  memDC.SetTextColor (RGB (255,255,255));
  memDC.BitBlt (0, 0, nWidth, nHeight, &maskDC, 0, 0, SRCAND);

  // Set the foreground to black. See comment above.
  dc.SetBkColor (RGB (255,255,255));
  dc.SetTextColor (RGB (0,0,0));
  dc.BitBlt (nXDest, nYDest, nWidth, nHeight, &maskDC, 0, 0, SRCAND);

  // Combine the foreground with the background
  dc.BitBlt (nXDest, nYDest, nWidth, nHeight, &memDC, 0, 0, SRCPAINT);

  if (hOldTempBmp)
     ::SelectObject (tempDC.m_hDC, hOldTempBmp);
  if (pOldMaskBmp)
     maskDC.SelectObject (pOldMaskBmp);
  if (pOldMemBmp)
     memDC.SelectObject (pOldMemBmp);

  dc.Detach();
}

//
//  Purpose:
//    Draws a bitmap in a disabled mode onto the destination DC.
//    See http://www.codeguru.com/Cpp/G-M/bitmap/specialeffects/article.php/c1699/
//
//  Returns:
//    None
//
void FooButton::DisabledBlt
  (HDC      hdcDest,    // destination DC
   int      nXDest,     // x coord of destination rectangle's upper left corner
   int      nYDest,     // y coord of destination rectangle's upper left corner
   int      nWidth,     // width of destination rectangle
   int      nHeight,    // height of destination rectangle
   HBITMAP  hBitmap,    // source bitmap
   int      nXSrc,      // x-coord of source rectangle's upper-left corner
   int      nYSrc)      // y-coord of source rectangle's upper-left corner
{
  ASSERT (hdcDest && hBitmap);
  ASSERT (nWidth > 0 && nHeight > 0);

  // Create a generic DC for all BitBlts
  HDC hDC = CreateCompatibleDC (hdcDest);
  ASSERT (hDC);

  if (hDC) {

       // Create a DC for the monochrome DIB section
       HDC bwDC = CreateCompatibleDC(hDC);
       ASSERT (bwDC);

       if (bwDC) {

            // Create the monochrome DIB section with a black and white palette
            struct
            {
              BITMAPINFOHEADER  bmiHeader;
              RGBQUAD           bmiColors[2];
            } RGBBWBITMAPINFO = {
                                  {                               // a BITMAPINFOHEADER
                                    sizeof(BITMAPINFOHEADER),     // biSize
                                    nWidth,                       // biWidth;
                                    nHeight,                      // biHeight;
                                    1,                            // biPlanes;
                                    1,                            // biBitCount
                                    BI_RGB,                       // biCompression;
                                    0,                            // biSizeImage;
                                    0,                            // biXPelsPerMeter;
                                    0,                            // biYPelsPerMeter;
                                    0,                            // biClrUsed;
                                    0                             // biClrImportant;
                                  },
                                  {
                                    { 0x00, 0x00, 0x00, 0x00 },
                                    { 0xFF, 0xFF, 0xFF, 0x00 }
                                  }
                                };
            void*     pbitsBW;
            HBITMAP   hBitmapBW = CreateDIBSection (bwDC,
                                                    (LPBITMAPINFO) &RGBBWBITMAPINFO,
                                                    DIB_RGB_COLORS, &pbitsBW, NULL, 0);
            ASSERT (hBitmapBW);

            if (hBitmapBW) {
                 // Attach the monochrome DIB section and the bitmap to the DCs
                 SelectObject (bwDC, hBitmapBW);
                 SelectObject (hDC, hBitmap);

                 // BitBlt the bitmap into the monochrome DIB section
                 BitBlt (bwDC, 0, 0, nWidth, nHeight, hDC, nXSrc, nYSrc, SRCCOPY);

                 // Paint the destination rectangle in gray
                 FillRect (hdcDest,
                           CRect (nXDest, nYDest, nXDest + nWidth, nYDest + nHeight),
                           GetSysColorBrush (COLOR_3DFACE));

                 // BitBlt the black bits in the monochrome bitmap into COLOR_3DHILIGHT bits in the destination DC
                 // The magic ROP comes from the Charles Petzold's book
                 HBRUSH hb = CreateSolidBrush (GetSysColor (COLOR_3DHILIGHT));
                 HBRUSH oldBrush = (HBRUSH) SelectObject (hdcDest, hb);
                 BitBlt (hdcDest, nXDest + 1, nYDest + 1, nWidth, nHeight, bwDC, 0, 0, 0xB8074A);

                 // BitBlt the black bits in the monochrome bitmap into COLOR_3DSHADOW bits in the destination DC
                 hb = CreateSolidBrush (GetSysColor (COLOR_3DSHADOW));
                 DeleteObject (SelectObject(hdcDest, hb));
                 BitBlt (hdcDest, nXDest, nYDest, nWidth, nHeight, bwDC, 0, 0, 0xB8074A);
                 DeleteObject (SelectObject (hdcDest, oldBrush));
            }
            VERIFY (DeleteDC (bwDC));
       }
       VERIFY (DeleteDC(hDC));
  }
}

//! Draws the button's drop-down triangle.
void FooButton::drawDropDownIndicator
  (HDC      hdcDest,            // destination DC
   int      nX,                 // x coord of dropdown indicator's upper left vertex
   int      nY,                 // y coord of dropdown indicator's upper left vertex
   int      nWidthDropDown,     // width of indicator
   int      nHeightDropDown)    // height of indicator
{
int   nCounter;   // generic counter

  ASSERT (hdcDest);
  for (nCounter = 0; (nCounter < nHeightDropDown); nCounter++) {

      POINT   ptStart;    // starting point
      int     nWidth;     // line width

      ptStart.x = nX + nCounter;
      ptStart.y = nY + nCounter;
      nWidth = nWidthDropDown - nCounter;
      if (nWidth < 0)
         break;

      ::MoveToEx (hdcDest, ptStart.x, ptStart.y, NULL);
      ::LineTo (hdcDest, ptStart.x + nWidth - nCounter, ptStart.y);
  }
}

//! Draws the button's frame when hot tracking.
void FooButton::drawHotButtonFrame
  (LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  // Get DC
  ASSERT (lpDrawItemStruct != NULL);
  CDC* pDC = CDC::FromHandle (lpDrawItemStruct->hDC);
  ASSERT (pDC != NULL);

  // Draw frame
  CRect rectButton;
  GetClientRect (&rectButton);
  if (m_bTracking) {
      if (isGradient()) {

          // Draw frame when mouse has just entered a hot gradient button.
          // Use the standard drawFrame() method to do this.
          m_bHot = false;
          drawFrame (pDC, lpDrawItemStruct);
          m_bHot = true;

      } else {

          // Draw frame when mouse is over hot non-gradient button
          COLORREF  rgbHighlight = GetSysColor (COLOR_BTNHIGHLIGHT);
          COLORREF  rgbShadow = GetSysColor (COLOR_BTNSHADOW);
          COLORREF  rgbBackground = GetSysColor (m_bChecked ? COLOR_3DLIGHT : COLOR_BTNFACE);

          pDC->FillSolidRect (&rectButton, rgbBackground);
          if ((lpDrawItemStruct->itemState & ODS_SELECTED) == ODS_SELECTED)
             pDC->Draw3dRect (&rectButton, rgbShadow, rgbHighlight);
          else
             pDC->Draw3dRect (&rectButton, rgbHighlight, rgbShadow);

          // For multi pushbuttons, fill the area of the drop-down indicator with
          // a light color if the multi button is currently pressed
          if (m_bMulti && m_bMultiClicked)
             drawMultiDropDownRegion (pDC, lpDrawItemStruct);
      }
  } else {

      if (isGradient()) {

          // Draw frame when mouse has just left a hot gradient button.
          // Use the standard drawFrame() method to do this.
          m_bHot = false;
          drawFrame (pDC, lpDrawItemStruct);
          m_bHot = true;
      }

  }
}

//! Checks if the mouse was clicked on multi part of button.
void FooButton::multiHitTest
  (CPoint pt)
{
  // Reset multi-clicked state
  m_bMultiClicked = false;
  if (!m_bMulti)
     return;

  // Get bounds of multi part of button
	CRect rect;
	GetWindowRect (rect);
  rect.left = rect.right - 2*C_DropDown_X + 2;

  // Check if mouse was clicked on multi part of button
  ClientToScreen (&pt);
  if (rect.PtInRect (pt))
     m_bMultiClicked = true;
}

//! Gets a button group by name
//! @param  strGroupName    Group name
//! @param  pGroup          Returned group
void FooButton::getButtonGroup
  (CString strGroupName,
   void* & pGroup)
{
  pGroup = NULL;
  m_btnGroups.Lookup (strGroupName, pGroup);
}

//! Draws a gradient rectangle.
void FooButton::drawGradientRect
  (CDC *pDC, CRect r,
   COLORREF cLeft,
   COLORREF cRight, BOOL bVertical)
{
  // Use GradientFill function from MSIMG32.dll if possible
  if (m_dllGradientFillFunc != NULL) {

		  TRIVERTEX rcVertex[2];
		  rcVertex[0].x = r.left;
		  rcVertex[0].y = r.top;
		  rcVertex[0].Red = (unsigned short) (GetRValue (cLeft) << 8);
		  rcVertex[0].Green = (unsigned short) (GetGValue (cLeft) << 8);
		  rcVertex[0].Blue = (unsigned short) (GetBValue (cLeft) << 8);
		  rcVertex[0].Alpha = 0;

		  rcVertex[1].x = r.right;
		  rcVertex[1].y = r.bottom;
		  rcVertex[1].Red = (unsigned short) (GetRValue (cRight) << 8);
		  rcVertex[1].Green = (unsigned short) (GetGValue (cRight) << 8);
		  rcVertex[1].Blue = (unsigned short) (GetBValue (cRight) << 8);
		  rcVertex[1].Alpha = 0;
		  
		  GRADIENT_RECT gRect;
		  gRect.UpperLeft = 0;
		  gRect.LowerRight = 1;
		  
		  // fill the area 
		  m_dllGradientFillFunc
        (*pDC, rcVertex, 2, &gRect, 1,
         bVertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H);

      return;
  }

  // Otherwise, do a manual gradient fill
	CRect stepR;					// rectangle for color's band
	COLORREF color;				// color for the bands
	float fStep;
	
	if(bVertical)
		fStep = ((float)r.Height())/255.0f;	
	else
		fStep = ((float)r.Width())/255.0f;	// width of color's band
	
	for (int iOnBand = 0; iOnBand < 255; iOnBand++) 
	{
		// set current band
		if(bVertical)
		{
			SetRect(&stepR,
				r.left, 
				r.top+(int)(iOnBand * fStep),
				r.right, 
				r.top+(int)((iOnBand+1)* fStep));	
		}
		else
		{
			SetRect(&stepR,
				r.left+(int)(iOnBand * fStep), 
				r.top,
				r.left+(int)((iOnBand+1)* fStep), 
				r.bottom);	
		}

		// set current color
		color = RGB((GetRValue(cRight)-GetRValue(cLeft))*((float)iOnBand)/255.0f+GetRValue(cLeft),
			(GetGValue(cRight)-GetGValue(cLeft))*((float)iOnBand)/255.0f+GetGValue(cLeft),
			(GetBValue(cRight)-GetBValue(cLeft))*((float)iOnBand)/255.0f+GetBValue(cLeft));
		// fill current band
		pDC->FillSolidRect(stepR,color);
	}
}

// End FooButton.cpp

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
Founder SpreadTrends.com
United States United States
I've authored many articles that tackle real-world issues to save my peers in the development community valuable time. For example I've written articles that: show how to decode Ogg Vorbis audio files using the .NET Framework; describe best practices for Improving Entity Framework performance; and demonstrate step-by-step how to create a multi-player game.

Comments and Discussions