Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / WTL

WTL Helper

Rate me:
Please Sign up or sign in to vote.
4.92/5 (116 votes)
27 Aug 200713 min read 703.4K   8.8K   190  
Add-in for Microsoft VC++.NET 2003 that helps to insert message handlers for WTL.
//-----------------------------------------------------------------------------
// 
// @doc
//
// @module	ColorButton.cpp - Color button and popup |
//
// This module contains the definition of color button and popup
//
// Copyright (c) 2000-2002 - Descartes Systems Sciences, Inc.
//
// Based on work by Chris Maunder, Alexander Bischofberger and James White.
//
// http://www.codetools.com/miscctrl/colorbutton.asp
// http://www.codetools.com/miscctrl/colour_picker.asp
//
// Copyright (c) 2000-2002 - Descartes Systems Sciences, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are 
// met:
// 
// 1. Redistributions of source code must retain the above copyright notice, 
//    this list of conditions and the following disclaimer. 
// 2. Neither the name of Descartes Systems Sciences, Inc nor the names of 
//    its contributors may be used to endorse or promote products derived 
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// --- ORIGINAL COPYRIGHT STATEMENT ---
//
// Written by Chris Maunder (chrismaunder@codeguru.com)
// Extended by Alexander Bischofberger (bischofb@informatik.tu-muenchen.de)
// Copyright (c) 1998.
//
// Updated 30 May 1998 to allow any number of colours, and to
//                     make the appearance closer to Office 97. 
//                     Also added "Default" text area.         (CJM)
//
//         13 June 1998 Fixed change of focus bug (CJM)
//         30 June 1998 Fixed bug caused by focus bug fix (D'oh!!)
//                      Solution suggested by Paul Wilkerson.
//
// ColourPopup is a helper class for the colour picker control
// CColourPicker. Check out the header file or the accompanying 
// HTML doc file for details.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. 
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Expect bugs.
// 
// Please use and enjoy. Please let me know of any bugs/mods/improvements 
// that you have found/implemented and I will fix/incorporate them into this
// file. 
//
// @end
//
// $History: ColorButton.cpp $
//      
//      *****************  Version 4  *****************
//      User: Tim Smith    Date: 1/21/02    Time: 2:53p
//      Updated in $/Omni_V2/exe_cnf
//      Ported code to VC7 compiler
//      
//      *****************  Version 3  *****************
//      User: Tim Smith    Date: 9/10/01    Time: 9:05a
//      Updated in $/Omni_V2/exe_cnf
//      
//      *****************  Version 2  *****************
//      User: Tim Smith    Date: 8/28/01    Time: 4:25p
//      Updated in $/Omni_V2/exe_cnf
//      Updated copyright dates.
//      
//      *****************  Version 1  *****************
//      User: Tim Smith    Date: 8/28/01    Time: 3:19p
//      Created in $/Omni_V2/exe_cnf
//
//-----------------------------------------------------------------------------

#include "stdafx.h"
#include "ColorButton.h"
#include <atldlgs.h>
//
// Sizing constants
//

const int g_ciArrowSizeX = 4;
const int g_ciArrowSizeY = 2;

//
// Color table
//

CColorButton::ColorTableEntry CColorButton::gm_sColors [] =
{
    { RGB(0x00, 0x00, 0x00),    _T("Black")             },
    { RGB(0xA5, 0x2A, 0x00),    _T("Brown")             },
    { RGB(0x00, 0x40, 0x40),    _T("Dark Olive Green")  },
    { RGB(0x00, 0x55, 0x00),    _T("Dark Green")        },
    { RGB(0x00, 0x00, 0x5E),    _T("Dark Teal")         },
    { RGB(0x00, 0x00, 0x8B),    _T("Dark blue")         },
    { RGB(0x4B, 0x00, 0x82),    _T("Indigo")            },
    { RGB(0x28, 0x28, 0x28),    _T("Dark grey")         },

    { RGB(0x8B, 0x00, 0x00),    _T("Dark red")          },
    { RGB(0xFF, 0x68, 0x20),    _T("Orange")            },
    { RGB(0x8B, 0x8B, 0x00),    _T("Dark yellow")       },
    { RGB(0x00, 0x93, 0x00),    _T("Green")             },
    { RGB(0x38, 0x8E, 0x8E),    _T("Teal")              },
    { RGB(0x00, 0x00, 0xFF),    _T("Blue")              },
    { RGB(0x7B, 0x7B, 0xC0),    _T("Blue-grey")         },
    { RGB(0x66, 0x66, 0x66),    _T("Grey - 40")         },

    { RGB(0xFF, 0x00, 0x00),    _T("Red")               },
    { RGB(0xFF, 0xAD, 0x5B),    _T("Light orange")      },
    { RGB(0x32, 0xCD, 0x32),    _T("Lime")              }, 
    { RGB(0x3C, 0xB3, 0x71),    _T("Sea green")         },
    { RGB(0x7F, 0xFF, 0xD4),    _T("Aqua")              },
    { RGB(0x7D, 0x9E, 0xC0),    _T("Light blue")        },
    { RGB(0x80, 0x00, 0x80),    _T("Violet")            },
    { RGB(0x7F, 0x7F, 0x7F),    _T("Grey - 50")         },

    { RGB(0xFF, 0xC0, 0xCB),    _T("Pink")              },
    { RGB(0xFF, 0xD7, 0x00),    _T("Gold")              },
    { RGB(0xFF, 0xFF, 0x00),    _T("Yellow")            },    
    { RGB(0x00, 0xFF, 0x00),    _T("Bright green")      },
    { RGB(0x40, 0xE0, 0xD0),    _T("Turquoise")         },
    { RGB(0xC0, 0xFF, 0xFF),    _T("Skyblue")           },
    { RGB(0x48, 0x00, 0x48),    _T("Plum")              },
    { RGB(0xC0, 0xC0, 0xC0),    _T("Light grey")        },

    { RGB(0xFF, 0xE4, 0xE1),    _T("Rose")              },
    { RGB(0xD2, 0xB4, 0x8C),    _T("Tan")               },
    { RGB(0xFF, 0xFF, 0xE0),    _T("Light yellow")      },
    { RGB(0x98, 0xFB, 0x98),    _T("Pale green ")       },
    { RGB(0xAF, 0xEE, 0xEE),    _T("Pale turquoise")    },
    { RGB(0x68, 0x83, 0x8B),    _T("Pale blue")         },
	{ RGB(0xE6, 0xE6, 0xFA),    _T("Lavender")          },
    { RGB(0xFF, 0xFF, 0xFF),    _T("White")             }
};

//
// Other definitions
//

#define DEFAULT_BOX_VALUE	-3
#define CUSTOM_BOX_VALUE	-2
#define INVALID_COLOR		-1
#define MAX_COLORS			100

//
// Sizing definitions
//

static const CSize s_sizeTextHiBorder (3, 3);
static const CSize s_sizeTextMargin (2, 2);
static const CSize s_sizeBoxHiBorder (2, 2);
static const CSize s_sizeBoxMargin (0, 0);
static const CSize s_sizeBoxCore (14, 14);

//
// Ok, here is how all the sizing works.  All picker elements use the
// exact same rules.  They all have 3 elements, their core size, the size
// of the highlight border, an the size of the margin.
//
// For text, the core size is just the extent of the text to be drawn.
// For the color boxes, it is the s_sizeBoxCore value.  (For pointless
// jollies, the box size was modified so it doesn't have to be square.  We
// are talking about changing 3-4 lines of code.  No biggie.)
//
// Also, just a point of note.  Each area has a well defined rectagle. 
// (m_rectDefaultText, m_rectCustomText, and m_rectBoxes)
// Even if there isn't any default or custom text, the rectagles are still
// well defined, but they have no height.  Their UpperLeft () is at the
// proper position and their Width () is valid.  In the case of the boxes,
// m_rectBoxes is the rectagle that contains all the boxes.  These changes
// made drawing and hit test 100000 times easier.
//

//-----------------------------------------------------------------------------
//
// @mfunc <c CColorButton> constructor.
//
// @rdesc None.
//
//-----------------------------------------------------------------------------

CColorButton::CColorButton () : m_wndPicker (this, 1)
{
	m_clrCurrent = CLR_DEFAULT;
	m_clrDefault = ::GetSysColor (COLOR_APPWORKSPACE);
	m_pszDefaultText = _tcsdup (_T ("Automatic"));
	m_pszCustomText = _tcsdup (_T ("More Colors..."));
	m_fPopupActive = FALSE;
	m_fTrackSelection = FALSE;
	m_fMouseOver = FALSE;
}

//-----------------------------------------------------------------------------
//
// @mfunc <c CColorButton> destructor.
//
// @rdesc None.
//
//-----------------------------------------------------------------------------

CColorButton::~CColorButton ()
{
	if (m_pszDefaultText)
		free (m_pszDefaultText);
	if (m_pszCustomText)
		free (m_pszCustomText);
}

//-----------------------------------------------------------------------------
//
// @mfunc Subclass the control
//
// @parm HWND | hWnd | Handle of the window to be subclassed
// 
// @rdesc Return value
//
//		@flag TRUE | Window was subclassed
//		@flag FALSE | Window was not subclassed
//
//-----------------------------------------------------------------------------

BOOL CColorButton::SubclassWindow (HWND hWnd)
{
	CWindowImpl <CColorButton>::SubclassWindow (hWnd);
	ModifyStyle (0, BS_OWNERDRAW);
	if (CTheme::IsThemingSupported())
	{
		OpenThemeData (L"Button");
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle key press
//
// @parm WORD | wNotifyCode | Command notification code
// 
// @parm WORD | wID | ID of the control
//
// @parm HWND | hWndCtl | Handle of the control
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnClicked (WORD wNotifyCode, 
	WORD wID, HWND hWndCtl, BOOL &bHandled) 
{

	//
	// Mark button as active and invalidate button to force a redraw
	//

	m_fPopupActive = TRUE;
	InvalidateRect (NULL);

	//
	// Get the parent window
	//

	HWND hWndParent = GetParent ();

	//
	// Send the drop down notification to the parent
	//

	SendNotification (CPN_DROPDOWN, m_clrCurrent, TRUE); 

	//
	// Save the current color for future reference
	//

    COLORREF clrOldColor = m_clrCurrent;

	//
	// Display the popup
	//

	BOOL fOked = Picker ();

	//
	// Cancel the popup
	//

	m_fPopupActive = FALSE;

	//
	// If the popup was canceled without a selection
	//

	if (!fOked)
	{

		//
		// If we are tracking, restore the old selection
		//

		if (m_fTrackSelection)
		{
			if (clrOldColor != m_clrCurrent)
			{
				m_clrCurrent = clrOldColor;
				SendNotification (CPN_SELCHANGE, m_clrCurrent, TRUE); 
			}
		}
		SendNotification (CPN_CLOSEUP, m_clrCurrent, TRUE); 
		SendNotification (CPN_SELENDCANCEL, m_clrCurrent, TRUE); 
	}
	else
	{
		if (clrOldColor != m_clrCurrent)
		{
			SendNotification (CPN_SELCHANGE, m_clrCurrent, TRUE); 
		}
		SendNotification (CPN_CLOSEUP, m_clrCurrent, TRUE); 
		SendNotification (CPN_SELENDOK, m_clrCurrent, TRUE); 
	}

	//
	// Invalidate button to force repaint
	//

	InvalidateRect (NULL);
	return TRUE;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle mouse move
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnMouseMove (UINT uMsg, WPARAM wParam, 
	LPARAM lParam, BOOL &bHandled) 
{
	if (!m_fMouseOver)
	{
		m_fMouseOver = TRUE;
		TRACKMOUSEEVENT tme;
		tme .cbSize = sizeof (tme);
		tme .dwFlags = TME_LEAVE;
		tme .hwndTrack = m_hWnd;
		_TrackMouseEvent (&tme);
		InvalidateRect (NULL);
	}
	bHandled = FALSE;
	return FALSE;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle mouse leave
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnMouseLeave (UINT uMsg, WPARAM wParam, 
	LPARAM lParam, BOOL &bHandled) 
{
	if (m_fMouseOver)
	{
		m_fMouseOver = FALSE;
		InvalidateRect (NULL);
	}
	bHandled = FALSE;
	return FALSE;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle a draw item request
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnDrawItem (UINT uMsg, WPARAM wParam, 
	LPARAM lParam, BOOL &bHandled) 
{
	LPDRAWITEMSTRUCT lpItem = (LPDRAWITEMSTRUCT) lParam;
	CDC dc (lpItem ->hDC);

	//
	// Get data about the request
	//

	UINT uState = lpItem ->itemState;
	CRect rcDraw = lpItem ->rcItem;

	//
	// If we have a theme
	//

	m_fPopupActive = false;
#if !defined (COLORBUTTON_NOTHEMES)
	if (m_hTheme != NULL)
	{

		//
		// Draw the outer edge
		//

		UINT uFrameState = 0;
		if ((uState & ODS_SELECTED) != 0 || m_fPopupActive)
			uFrameState |= PBS_PRESSED;
		if ((uState & ODS_DISABLED) != 0)
			uFrameState |= PBS_DISABLED;
		if ((uState & ODS_HOTLIGHT) != 0 || m_fMouseOver)
			uFrameState |= PBS_HOT;
		else if ((uState & ODS_DEFAULT) != 0)
			uFrameState |= PBS_DEFAULTED;
		DrawThemeBackground (dc, BP_PUSHBUTTON, 
			uFrameState, &rcDraw, NULL);
		GetThemeBackgroundContentRect (dc, BP_PUSHBUTTON, 
			uFrameState, &rcDraw, &rcDraw);
	}

	//
	// Otherwise, we are old school
	//

	else
#endif
	{
		//
		// Draw the outer edge
		//

		UINT uFrameState = DFCS_BUTTONPUSH | DFCS_ADJUSTRECT;
		if ((uState & ODS_SELECTED) != 0 || m_fPopupActive)
			uFrameState |= DFCS_PUSHED;
		if ((uState & ODS_DISABLED) != 0)
			uFrameState |= DFCS_INACTIVE;
		dc .DrawFrameControl (&rcDraw, DFC_BUTTON, uFrameState);

		//
		// Adjust the position if we are selected (gives a 3d look)
		//
		
		if ((uState & ODS_SELECTED) != 0 || m_fPopupActive)
			rcDraw .OffsetRect (1, 1);
	}

	//
	// Draw focus
	//

	if ((uState & ODS_FOCUS) != 0 || m_fPopupActive) 
	{
		CRect rcFocus (rcDraw.left, rcDraw.top, 
			rcDraw.right - 1, rcDraw.bottom);
		dc .DrawFocusRect(&rcFocus);
	}
	rcDraw .InflateRect (
		- ::GetSystemMetrics(SM_CXEDGE),
		- ::GetSystemMetrics(SM_CYEDGE));

	//
	// Draw the arrow
	//

	{
		CRect rcArrow;
		rcArrow .left   = rcDraw. right - g_ciArrowSizeX - ::GetSystemMetrics (SM_CXEDGE) / 2;
		rcArrow .top    = (rcDraw.bottom + rcDraw.top)/2 - g_ciArrowSizeY / 2;
		rcArrow .right  = rcArrow.left + g_ciArrowSizeX;
		rcArrow .bottom = (rcDraw .bottom + rcDraw .top) / 2 + g_ciArrowSizeY / 2;

		DrawArrow (dc, rcArrow, 0, 
			(uState & ODS_DISABLED) ? ::GetSysColor (COLOR_GRAYTEXT) : RGB (0,0,0));

		rcDraw.right = rcArrow.left - ::GetSystemMetrics (SM_CXEDGE) / 2;
	}

	//
	// Draw separator
	//

	dc .DrawEdge (&rcDraw, EDGE_ETCHED, BF_RIGHT);
	rcDraw.right -= (::GetSystemMetrics (SM_CXEDGE) * 2) + 1 ;

	//
	// Draw color
	//

	if ((uState & ODS_DISABLED) == 0)
	{
		dc .SetBkColor ((m_clrCurrent == CLR_DEFAULT) ? m_clrDefault : m_clrCurrent);
		dc .ExtTextOut (0, 0, ETO_OPAQUE, &rcDraw, NULL, 0, NULL);
		dc .FrameRect (&rcDraw, (HBRUSH)::GetStockObject (BLACK_BRUSH));
	}
	return 1;
}

//-----------------------------------------------------------------------------
//
// @mfunc Draw the arrow of the button
//
// @parm CDC & | dc | Destination DC
//
// @parm const RECT & | rect | Rectangle of the control
//
// @parm int | iDirection | Direction
//
// @parm COLORREF | clrArrow | Color to draw the arrow.
//
// @rdesc None
//
//-----------------------------------------------------------------------------

void CColorButton::DrawArrow (CDC &dc, const RECT &rect, 
	int iDirection, COLORREF clrArrow)
{
	POINT ptsArrow[3];
	
	switch (iDirection)
	{
		case 0 : // Down
			{
				ptsArrow [0] .x = rect .left;
				ptsArrow [0] .y = rect .top;
				ptsArrow [1] .x = rect .right;
				ptsArrow [1] .y = rect .top;
				ptsArrow [2] .x = (rect .left + rect .right) / 2;
				ptsArrow [2] .y = rect .bottom;
				break;
			}
			
		case 1 : // Up
			{
				ptsArrow [0] .x = rect .left;
				ptsArrow [0] .y = rect .bottom;
				ptsArrow [1] .x = rect .right;
				ptsArrow [1] .y = rect .bottom;
				ptsArrow [2] .x = (rect .left + rect .right) / 2;
				ptsArrow [2] .y = rect .top;
				break;
			}
			
		case 2 : // Left
			{
				ptsArrow [0] .x = rect .right;
				ptsArrow [0] .y = rect .top;
				ptsArrow [1] .x = rect .right;
				ptsArrow [1] .y = rect .bottom;
				ptsArrow [2] .x = rect .left;
				ptsArrow [2] .y = (rect .top + rect .bottom) / 2;
				break;
			}
			
		case 3 : // Right
			{
				ptsArrow [0] .x = rect .left;
				ptsArrow [0] .y = rect .top;
				ptsArrow [1] .x = rect .left;
				ptsArrow [1] .y = rect .bottom;
				ptsArrow [2] .x = rect .right;
				ptsArrow [2] .y = (rect .top + rect .bottom) / 2;
				break;
			}
	}
	
	CBrush brArrow;
	brArrow .CreateSolidBrush (clrArrow);
	CPen penArrow;
	penArrow .CreatePen (PS_SOLID, 0, clrArrow);

	HBRUSH hbrOld = dc .SelectBrush (brArrow);
	HPEN hpenOld = dc .SelectPen (penArrow);

	dc .SetPolyFillMode (WINDING);
	dc .Polygon (ptsArrow, 3);

	dc .SelectBrush (hbrOld);
	dc .SelectPen (hpenOld);
	return;
}

//-----------------------------------------------------------------------------
//
// @mfunc Display the picker popup
//
// @rdesc Return value
//
//		@parm TRUE | A new color was selected
//		@parm FALSE | The user canceled the picket
//
//-----------------------------------------------------------------------------

BOOL CColorButton::Picker ()
{
	BOOL fOked = FALSE;

	//
	// See what version we are using
	//

	OSVERSIONINFO osvi;
	osvi .dwOSVersionInfoSize = sizeof (osvi);
	::GetVersionEx (&osvi);
	bool fIsXP = osvi .dwPlatformId == VER_PLATFORM_WIN32_NT &&
		(osvi .dwMajorVersion > 5 || (osvi .dwMajorVersion == 5 &&
		osvi .dwMinorVersion >= 1));

	//
	// Get the flat flag
	//

	m_fPickerFlat = FALSE;
#if (_WIN32_WINNT >= 0x0501)
 	if (fIsXP)
		::SystemParametersInfo (SPI_GETFLATMENU, 0, &m_fPickerFlat, FALSE);
#endif

	//
	// Get all the colors I need
	//

	int nAlpha = 48;
	m_clrBackground = ::GetSysColor (COLOR_MENU);
	m_clrHiLightBorder = ::GetSysColor (COLOR_HIGHLIGHT);
	m_clrHiLight = m_clrHiLightBorder;
#if (WINVER >= 0x0501)
	if (fIsXP)
		m_clrHiLight = ::GetSysColor (COLOR_MENUHILIGHT);
#endif
	m_clrHiLightText = ::GetSysColor (COLOR_HIGHLIGHTTEXT);
	m_clrText = ::GetSysColor (COLOR_MENUTEXT);
	m_clrLoLight = RGB (
		(GetRValue (m_clrBackground) * (255 - nAlpha) + 
			GetRValue (m_clrHiLightBorder) * nAlpha) >> 8,
		(GetGValue (m_clrBackground) * (255 - nAlpha) + 
			GetGValue (m_clrHiLightBorder) * nAlpha) >> 8,
		(GetBValue (m_clrBackground) * (255 - nAlpha) + 
			GetBValue (m_clrHiLightBorder) * nAlpha) >> 8);
   
	//
	// Get the margins
	//

	m_rectMargins .left = ::GetSystemMetrics (SM_CXEDGE);
	m_rectMargins .top = ::GetSystemMetrics (SM_CYEDGE);
	m_rectMargins .right = ::GetSystemMetrics (SM_CXEDGE);
	m_rectMargins .bottom = ::GetSystemMetrics (SM_CYEDGE);

	//
	// Initialize some sizing parameters
	//

	m_nNumColors = sizeof (gm_sColors) / sizeof (ColorTableEntry);
	_ASSERTE (m_nNumColors <= MAX_COLORS);
	if (m_nNumColors > MAX_COLORS)
		m_nNumColors = MAX_COLORS;

	//
	// Initialize our state
	// 

	m_nCurrentSel       = INVALID_COLOR;
	m_nChosenColorSel	= INVALID_COLOR;
	m_clrPicker			= m_clrCurrent;

	//
	// Create the font
	//

	NONCLIENTMETRICS ncm;
	ncm .cbSize = sizeof (NONCLIENTMETRICS);
	SystemParametersInfo (SPI_GETNONCLIENTMETRICS,
		sizeof (NONCLIENTMETRICS), &ncm, 0);
	m_font .CreateFontIndirect (&ncm .lfMessageFont);

	//
	// Create the palette
	//

	struct 
	{
		LOGPALETTE    LogPalette;
		PALETTEENTRY  PalEntry [MAX_COLORS];
	} pal;

	LOGPALETTE *pLogPalette = (LOGPALETTE *) &pal;
	pLogPalette ->palVersion    = 0x300;
	pLogPalette ->palNumEntries = (WORD) m_nNumColors; 

	for (int i = 0; i < m_nNumColors; i++)
	{
		pLogPalette ->palPalEntry [i] .peRed   = GetRValue (gm_sColors [i] .clrColor);
		pLogPalette ->palPalEntry [i] .peGreen = GetGValue (gm_sColors [i] .clrColor);
		pLogPalette ->palPalEntry [i] .peBlue  = GetBValue (gm_sColors [i] .clrColor);
		pLogPalette ->palPalEntry [i] .peFlags = 0;
	}
	m_palette .CreatePalette (pLogPalette);

	//
	// Register the window class used for the picker
	//

	WNDCLASSEX wc;
	wc .cbSize = sizeof (WNDCLASSEX);
	wc .style  = CS_CLASSDC | CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW;
	wc .lpfnWndProc = CContainedWindow::StartWindowProc;
	wc .cbClsExtra  = 0;
	wc .cbWndExtra = 0;
#if (_ATL_VER >= 0x0700)
	wc .hInstance = ATL::_AtlBaseModule .GetModuleInstance ();
#else
	wc .hInstance = _Module .GetModuleInstance ();
#endif
	
	wc .hIcon = NULL;
	wc .hCursor = LoadCursor (NULL, IDC_ARROW);
	wc .hbrBackground = (HBRUSH) (COLOR_MENU + 1);
	wc .lpszMenuName = NULL;
	wc .lpszClassName = _T ("ColorPicker");
	wc .hIconSm = NULL;
#if (_WIN32_WINNT >= 0x0501)
	if (fIsXP)
	{
		BOOL fDropShadow;
		::SystemParametersInfo (SPI_GETDROPSHADOW, 0, &fDropShadow, FALSE);
		if (fDropShadow)
			wc .style |= CS_DROPSHADOW;
	}
#endif
	ATOM atom = ::RegisterClassEx (&wc);

	//
	// Create the window
	//

	CRect rcButton;
	GetWindowRect (&rcButton);
#if (_ATL_VER >= 0x0700)
	ATL::_AtlWinModule.AddCreateWndData (&m_wndPicker .m_thunk .cd, &m_wndPicker);
	m_wndPicker .m_hWnd = ::CreateWindowEx (0, (LPCTSTR) MAKELONG (atom, 0), 
		_T (""),  WS_POPUP, rcButton .left, rcButton .bottom, 100, 100,
		GetParent (), NULL, ATL::_AtlBaseModule .GetModuleInstance(), NULL);
#else
	_Module .AddCreateWndData (&m_wndPicker .m_thunk .cd, &m_wndPicker);
	m_wndPicker .m_hWnd = ::CreateWindowEx (0, (LPCTSTR) MAKELONG (atom, 0), 
		_T (""),  WS_POPUP, rcButton .left, rcButton .bottom, 100, 100,
		GetParent (), NULL, _Module .GetModuleInstance(), NULL);
#endif
	

	//
	// If we created the window
	//

	if (m_wndPicker .m_hWnd != NULL)
	{
        
		//
		// Set the window size
		//

	    SetPickerWindowSize ();

		//
	    // Create the tooltips
		//

		CToolTipCtrl sToolTip;
		CreatePickerToolTips (sToolTip);

		//
		// Find which cell (if any) corresponds to the initial color
		//

		FindPickerCellFromColor (m_clrCurrent);

		//
		// Make visible
		//
	
		m_wndPicker .ShowWindow (SW_SHOWNA);

		//
		// Purge the message queue of paints
		//

		MSG msg;
		while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_NOREMOVE))
		{
			if (!GetMessage(&msg, NULL, WM_PAINT, WM_PAINT))
				return FALSE;
			DispatchMessage(&msg);
		}

		// 
		// Set capture to the window which received this message
		//

		m_wndPicker .SetCapture ();
		_ASSERTE (m_wndPicker .m_hWnd == ::GetCapture ());

		//
		// Get messages until capture lost or cancelled/accepted
		//

		while (m_wndPicker .m_hWnd == ::GetCapture ())
		{
			MSG msg;
			if (!::GetMessage(&msg, NULL, 0, 0))
			{
				::PostQuitMessage ((int)msg .wParam);
				break;
			}

			sToolTip .RelayEvent (&msg);

			switch (msg.message)
			{
				case WM_LBUTTONUP:
					{
						BOOL bHandled = TRUE;
						OnPickerLButtonUp (msg .message, 
							msg .wParam, msg .lParam, bHandled);
					}
					break;

				case WM_MOUSEMOVE:
					{
						BOOL bHandled = TRUE;
						OnPickerMouseMove (msg .message, 
							msg .wParam, msg .lParam, bHandled);
					}
					break;

				case WM_KEYUP:
					break;

				case WM_KEYDOWN:
					{
						BOOL bHandled = TRUE;
						OnPickerKeyDown (msg .message, 
							msg .wParam, msg .lParam, bHandled);
					}
					break;

				case WM_RBUTTONDOWN:
					::ReleaseCapture ();
					m_fOked = FALSE;
					break;

				// just dispatch rest of the messages
				default:
					DispatchMessage (&msg);
					break;
			}
		}
		::ReleaseCapture ();
		fOked = m_fOked;

		//
		// Destroy the window
		//

		sToolTip .DestroyWindow ();
		m_wndPicker .DestroyWindow ();

		//
		// If needed, show custom
		//

		if (fOked)
		{
			if (fOked && m_nCurrentSel == CUSTOM_BOX_VALUE)
			{
				CColorDialog dlg (m_clrCurrent, 
					CC_FULLOPEN | CC_ANYCOLOR, m_hWnd);

				if (dlg .DoModal() == IDOK)
					m_clrCurrent = dlg.GetColor();
				else
					fOked = FALSE;
			}
			else
				m_clrCurrent = m_clrPicker;
		}

		//
		// Clean up GDI objects
		//

		m_font .DeleteObject ();
		m_palette .DeleteObject ();
	}

	//
	// Unregister our class
	//
#if (_ATL_VER >= 0x0700)
	::UnregisterClass ((LPCTSTR) MAKELONG (atom, 0),
		ATL::_AtlBaseModule .GetModuleInstance());
#else
	::UnregisterClass ((LPCTSTR) MAKELONG (atom, 0),
		_Module .GetModuleInstance());
#endif
	return fOked;
}

//-----------------------------------------------------------------------------
//
// @mfunc Set the window size of the picker control
//
// @rdesc None.
//
//-----------------------------------------------------------------------------

void CColorButton::SetPickerWindowSize ()
{
	SIZE szText = { 0, 0 };

	//
    // If we are showing a custom or default text area, get the font and text size.
	//

    if (HasCustomText () || HasDefaultText ())
    {
		CClientDC dc (m_wndPicker);
		HFONT hfontOld = dc .SelectFont (m_font);

		//
		// Get the size of the custom text (if there IS custom text)
		//

		if (HasCustomText ())
		{
			dc .GetTextExtent (m_pszCustomText, 
				(int)_tcslen (m_pszCustomText), &szText);
		}

		//
        // Get the size of the default text (if there IS default text)
		//

        if (HasDefaultText ())
        {
			SIZE szDefault;
			dc .GetTextExtent (m_pszDefaultText, 
				(int)_tcslen (m_pszDefaultText), &szDefault);
			if (szDefault .cx > szText .cx)
				szText .cx = szDefault .cx;
			if (szDefault .cy > szText .cy)
				szText .cy = szDefault .cy;
        }
		dc .SelectFont (hfontOld);

		//
		// Commpute the final size
		//

		szText .cx += 2 * (s_sizeTextMargin .cx + s_sizeTextHiBorder .cx);
		szText .cy += 2 * (s_sizeTextMargin .cy + s_sizeTextHiBorder .cy);
    }

	//
	// Initiailize our box size
	//

	_ASSERTE (s_sizeBoxHiBorder .cx == s_sizeBoxHiBorder .cy);
	_ASSERTE (s_sizeBoxMargin .cx == s_sizeBoxMargin .cy);
	m_sizeBox .cx = s_sizeBoxCore .cx + (s_sizeBoxHiBorder .cx + s_sizeBoxMargin .cx) * 2;
	m_sizeBox .cy = s_sizeBoxCore .cy + (s_sizeBoxHiBorder .cy + s_sizeBoxMargin .cy) * 2;

	//
    // Get the number of columns and rows
	//

    m_nNumColumns = 8;
    m_nNumRows = m_nNumColors / m_nNumColumns;
    if ((m_nNumColors % m_nNumColumns) != 0) 
		m_nNumRows++;

	//
	// Compute the min width
	//

	int nBoxTotalWidth = m_nNumColumns * m_sizeBox .cx;
	int nMinWidth = nBoxTotalWidth;
	if (nMinWidth < szText .cx)
		nMinWidth = szText .cx;

	//
	// Create the rectangle for the default text
	//

	m_rectDefaultText = CRect (
		CPoint (0, 0), 
		CSize (nMinWidth, HasDefaultText () ? szText .cy : 0)
		);
		
	//
	// Initialize the color box rectangle
	//

	m_rectBoxes = CRect (
		CPoint ((nMinWidth - nBoxTotalWidth) / 2, m_rectDefaultText .bottom), 
		CSize (nBoxTotalWidth, m_nNumRows * m_sizeBox .cy)
		);

	//
	// Create the rectangle for the custom text
	//

 	m_rectCustomText = CRect (
		CPoint (0, m_rectBoxes .bottom), 
		CSize (nMinWidth, HasCustomText () ? szText .cy : 0)
		);

	//
    // Get the current window position, and set the new size
	//

	CRect rectWindow (
		m_rectDefaultText .TopLeft (), 
		m_rectCustomText .BottomRight ());
    CRect rect;
	m_wndPicker .GetWindowRect (&rect);
	rectWindow .OffsetRect (rect .TopLeft ());

	//
	// Adjust the rects for the border
	//

	rectWindow .right += m_rectMargins .left + m_rectMargins .right;
	rectWindow .bottom += m_rectMargins .top + m_rectMargins .bottom;
	::OffsetRect (&m_rectDefaultText, m_rectMargins .left, m_rectMargins .top);
	::OffsetRect (&m_rectBoxes, m_rectMargins .left, m_rectMargins .top);
	::OffsetRect (&m_rectCustomText, m_rectMargins .left, m_rectMargins .top);

	//
	// Get the screen rectangle
	//

	CRect rectScreen (CPoint (0, 0), CSize (
		::GetSystemMetrics (SM_CXSCREEN),
        ::GetSystemMetrics (SM_CYSCREEN)));
#if (WINVER >= 0x0500)
	HMODULE hUser32 = ::GetModuleHandleA ("USER32.DLL");
	if (hUser32 != NULL)
	{
		typedef HMONITOR (WINAPI *FN_MonitorFromWindow) (HWND hWnd, DWORD dwFlags);
		typedef BOOL (WINAPI *FN_GetMonitorInfo) (HMONITOR hMonitor, LPMONITORINFO lpmi);
		FN_MonitorFromWindow pfnMonitorFromWindow = (FN_MonitorFromWindow)
			::GetProcAddress (hUser32, "MonitorFromWindow");
		FN_GetMonitorInfo pfnGetMonitorInfo = (FN_GetMonitorInfo)
			::GetProcAddress (hUser32, "GetMonitorInfoA");
		if (pfnMonitorFromWindow != NULL && pfnGetMonitorInfo != NULL)
		{
			MONITORINFO mi;
			HMONITOR hMonitor = pfnMonitorFromWindow (m_hWnd, 
				MONITOR_DEFAULTTONEAREST);
			mi .cbSize = sizeof (mi);
			pfnGetMonitorInfo (hMonitor, &mi);
			rectScreen = mi .rcWork;
		}
	}
#endif

	//
    // Need to check it'll fit on screen: Too far right?
	//

    if (rectWindow .right > rectScreen .right)
		::OffsetRect (&rectWindow, rectScreen .right - rectWindow .right, 0);

	//
    // Too far left?
	//

    if (rectWindow .left < rectScreen .left)
        ::OffsetRect (&rectWindow, rectScreen .left - rectWindow .left, 0);

	//
    // Bottom falling out of screen?  If so, the move
	// the whole popup above the parents window
	//

    if (rectWindow .bottom > rectScreen .bottom)
    {
        CRect rcParent;
		GetWindowRect (&rcParent);
        ::OffsetRect (&rectWindow, 0,
			- ((rcParent .bottom - rcParent .top) + 
			(rectWindow .bottom - rectWindow .top)));
    }

	//
    // Set the window size and position
	//

	m_wndPicker .MoveWindow (&rectWindow, TRUE);
}

//-----------------------------------------------------------------------------
//
// @mfunc Create the tooltips for the picker
//
// @parm CToolTipCtrl & | sToolTip | Tool tip control
//
// @rdesc None.
//
//-----------------------------------------------------------------------------

void CColorButton::CreatePickerToolTips (CToolTipCtrl &sToolTip)
{
	//
    // Create the tool tip
	//

    if (!sToolTip .Create (m_wndPicker .m_hWnd)) 
		return;

	//
    // Add a tool for each cell
	// 
    for (int i = 0; i < m_nNumColors; i++)
    {
        CRect rect;
        if (!GetPickerCellRect (i, &rect)) 
			continue;
		sToolTip .AddTool (m_wndPicker .m_hWnd, 
			gm_sColors [i] .pszName, &rect, 1);
    }
}

//-----------------------------------------------------------------------------
//
// @mfunc Gets the dimensions of the colour cell given by (row,col)
//
// @parm int | nIndex | Index of the cell
//
// @parm RECT * | pRect | Rectangle of the cell
//
// @rdesc Return value.
//
//		@flag TRUE | If the index is valid
//		@flag FALSE | If the index is not valid
//
//-----------------------------------------------------------------------------

BOOL CColorButton::GetPickerCellRect (int nIndex, RECT *pRect) const
{

	//
	// If the custom box
	//

    if (nIndex == CUSTOM_BOX_VALUE)
    {
		*pRect = m_rectCustomText;
		return TRUE;
    }

	//
	// If the default box
	//

    else if (nIndex == DEFAULT_BOX_VALUE)
    {
		*pRect = m_rectDefaultText;
		return TRUE;
    }

	//
	// Validate the range
	//

    if (nIndex < 0 || nIndex >= m_nNumColors)
        return FALSE;

	//
	// Compute the value of the boxes
	//

    pRect ->left = (nIndex % m_nNumColumns) * m_sizeBox .cx + m_rectBoxes .left;
    pRect ->top  = (nIndex / m_nNumColumns) * m_sizeBox .cy + m_rectBoxes .top;
    pRect ->right = pRect ->left + m_sizeBox .cx;
    pRect ->bottom = pRect ->top + m_sizeBox .cy;
    return TRUE;
}

//-----------------------------------------------------------------------------
//
// @mfunc Set the chosen color from the given color
//
// @parm COLORREF | clr | Color
//
// @rdesc None
//
//-----------------------------------------------------------------------------

void CColorButton::FindPickerCellFromColor (COLORREF clr)
{
    if (clr == CLR_DEFAULT && HasDefaultText ())
    {
        m_nChosenColorSel = DEFAULT_BOX_VALUE;
        return;
    }

    for (int i = 0; i < m_nNumColors; i++)
    {
        if (gm_sColors [i] .clrColor == clr)
        {
            m_nChosenColorSel = i;
            return;
        }
    }

    if (HasCustomText ())
        m_nChosenColorSel = CUSTOM_BOX_VALUE;
    else
        m_nChosenColorSel = INVALID_COLOR;
}

//-----------------------------------------------------------------------------
//
// @mfunc Change the current selection
//
// @parm int | nIndex | New selection
//
// @rdesc None
//
//-----------------------------------------------------------------------------

void CColorButton::ChangePickerSelection (int nIndex)
{
	CClientDC dc (m_wndPicker);

	//
	// Clamp the index
	//

    if (nIndex > m_nNumColors)
        nIndex = CUSTOM_BOX_VALUE; 

	//
	// If the current selection is valid, redraw old selection with out
	// it being selected
	//

    if ((m_nCurrentSel >= 0 && m_nCurrentSel < m_nNumColors) ||
        m_nCurrentSel == CUSTOM_BOX_VALUE || m_nCurrentSel == DEFAULT_BOX_VALUE)
    {
        int nOldSel = m_nCurrentSel;
        m_nCurrentSel = INVALID_COLOR;
        DrawPickerCell (dc, nOldSel);
    }

	//
    // Set the current selection as row/col and draw (it will be drawn selected)
	//

    m_nCurrentSel = nIndex;
    DrawPickerCell (dc, m_nCurrentSel);

	//
    // Store the current colour
	//

	BOOL fValid = TRUE;
	COLORREF clr;
    if (m_nCurrentSel == CUSTOM_BOX_VALUE)
		clr = m_clrDefault;
    else if (m_nCurrentSel == DEFAULT_BOX_VALUE)
        clr = m_clrPicker = CLR_DEFAULT;
    else if (m_nCurrentSel == INVALID_COLOR)
	{
		clr = RGB (0, 0, 0);
		fValid = FALSE;
	}
	else
        clr = m_clrPicker = gm_sColors [m_nCurrentSel] .clrColor;

	//
	// Send the message
	//

	if (m_fTrackSelection)
	{
		if (fValid)
			m_clrCurrent = clr;
		InvalidateRect (NULL);
		SendNotification (CPN_SELCHANGE, m_clrCurrent, fValid); 
	}
}

//-----------------------------------------------------------------------------
//
// @mfunc End the selection
//
// @parm BOOL | fOked | If TRUE, the user has selected a new color.
//
// @rdesc None.
//
//-----------------------------------------------------------------------------

void CColorButton::EndPickerSelection (BOOL fOked)
{
    ::ReleaseCapture ();
	m_fOked = fOked;
}

//-----------------------------------------------------------------------------
//
// @mfunc Draw the given cell
//
// @parm CDC & | dc | Destination cell
//
// @parm int | nIndex | Index of the cell
//
// @rdesc None.
//
//-----------------------------------------------------------------------------

void CColorButton::DrawPickerCell (CDC &dc, int nIndex)
{

	//
	// Get the drawing rect
	//

	CRect rect;
	if (!GetPickerCellRect (nIndex, &rect)) 
		return;

	//
	// Get the text pointer and colors
	//

	LPCTSTR pszText;
	COLORREF clrBox;
	SIZE sizeMargin;
	SIZE sizeHiBorder;
	if (nIndex == CUSTOM_BOX_VALUE)
	{
		pszText = m_pszCustomText;
		sizeMargin = s_sizeTextMargin;
		sizeHiBorder = s_sizeTextHiBorder;
	}
	else if (nIndex == DEFAULT_BOX_VALUE)
	{
		pszText = m_pszDefaultText;
		sizeMargin = s_sizeTextMargin;
		sizeHiBorder = s_sizeTextHiBorder;
	}
	else
	{
		pszText = NULL;
		clrBox = gm_sColors [nIndex] .clrColor;
		sizeMargin = s_sizeBoxMargin;
		sizeHiBorder = s_sizeBoxHiBorder;
	}

	//
	// Based on the selectons, get our colors
	//

	COLORREF clrHiLight;
	COLORREF clrText;
	bool fSelected;
	if (m_nCurrentSel == nIndex)
	{
		fSelected = true;
		clrHiLight = m_clrHiLight;
		clrText = m_clrHiLightText;
	}
	else if (m_nChosenColorSel == nIndex)
	{
		fSelected = true;
		clrHiLight = m_clrLoLight;
		clrText = m_clrText;
	}
	else
	{
		fSelected = false;
		clrHiLight = m_clrLoLight;
		clrText = m_clrText;
	}

	//
	// Select and realize the palette
	//

	HPALETTE hpalOld = NULL;
	if (pszText == NULL)
	{
		if (m_palette .m_hPalette != NULL && 
			(dc .GetDeviceCaps (RASTERCAPS) & RC_PALETTE) != 0)
		{
			hpalOld = dc .SelectPalette (m_palette, FALSE);
			dc .RealizePalette ();
		}
	}

	//
	// If we are currently selected
	//

	if (fSelected)
	{

		//
		// If we have a background margin, then draw that
		//

		if (sizeMargin .cx > 0 || sizeMargin .cy > 0)
		{
			dc .SetBkColor (m_clrBackground);
			dc .ExtTextOut (0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
			rect .InflateRect (- sizeMargin .cx, - sizeMargin .cy);
		}

		//
		// Draw the selection rectagle
		//

		dc .SetBkColor (m_clrHiLightBorder);
		dc .ExtTextOut (0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
		rect .InflateRect (-1, -1);

		//
		// Draw the inner coloring
		//

		dc .SetBkColor (clrHiLight);
		dc .ExtTextOut (0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
		rect .InflateRect (- (sizeHiBorder .cx - 1), - (sizeHiBorder .cy - 1));
	}

	//
	// Otherwise, we are not selected
	//

	else
	{
		
		//
		// Draw the background
		//

		dc .SetBkColor (m_clrBackground);
		dc .ExtTextOut (0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
		rect .InflateRect ( 
			- (sizeMargin .cx + sizeHiBorder .cx), 
			- (sizeMargin .cy + sizeHiBorder .cy));
	}

	//
	// Draw custom text
	//

	if (pszText)
	{
		HFONT hfontOld = dc .SelectFont (m_font);
		dc .SetTextColor (clrText);
		dc .SetBkMode (TRANSPARENT);
		dc .DrawText (pszText, (int)_tcslen (pszText), 
			&rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
		dc .SelectFont (hfontOld);
    }        

	//
	// Otherwise, draw color
	//

	else
	{

		//
		// Draw color (ok, this code is bit sleeeeeezy.  But the
		// area's that are being drawn are SO small, that nobody
		// will notice.)
		//

		dc .SetBkColor (::GetSysColor (COLOR_3DSHADOW));
		dc .ExtTextOut (0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
		rect .InflateRect (-1, -1);
		dc .SetBkColor (gm_sColors [nIndex] .clrColor);
		dc .ExtTextOut (0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
	}

	//
	// Restore the pallete
	//

	if (hpalOld && (dc .GetDeviceCaps (RASTERCAPS) & RC_PALETTE) != 0)
		dc .SelectPalette (hpalOld, FALSE);
}

//-----------------------------------------------------------------------------
//
// @mfunc Set the chosen color from the given color
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnPickerKeyDown (UINT uMsg, 
	WPARAM wParam, LPARAM lParam, BOOL &bHandled) 
{

	//
	// Get the key data
	//
	
	UINT nChar = (UINT)wParam;

	//
	// Get the offset for movement
	//

	int nOffset = 0;
	switch (nChar)
	{
		case VK_DOWN:
			nOffset = m_nNumColumns;
			break;

		case VK_UP:
			nOffset = -m_nNumColumns;
			break;

		case VK_RIGHT:
			nOffset = 1;
			break;

		case VK_LEFT:
			nOffset = -1;
			break;

		case VK_ESCAPE:
			m_clrPicker = m_clrCurrent;
			EndPickerSelection (FALSE);
			break;

		case VK_RETURN:
		case VK_SPACE:
			if (m_nCurrentSel == INVALID_COLOR)
				m_clrPicker = m_clrCurrent;
	        EndPickerSelection (m_nCurrentSel != INVALID_COLOR);
			break;
	}

	//
	// If we have an offset
	//

	if (nOffset != 0)
	{

		//
		// Based on our current position, compute a new position
		//

		int nNewSel;
		if (m_nCurrentSel == INVALID_COLOR)
			nNewSel = nOffset > 0 ? DEFAULT_BOX_VALUE : CUSTOM_BOX_VALUE;
		else if (m_nCurrentSel == DEFAULT_BOX_VALUE)
			nNewSel = nOffset > 0 ? 0 : CUSTOM_BOX_VALUE;
		else if (m_nCurrentSel == CUSTOM_BOX_VALUE)
			nNewSel = nOffset > 0 ? DEFAULT_BOX_VALUE : m_nNumColors - 1;
		else
		{
			nNewSel = m_nCurrentSel + nOffset;
			if (nNewSel < 0)
				nNewSel = DEFAULT_BOX_VALUE;
			else if (nNewSel >= m_nNumColors)
				nNewSel = CUSTOM_BOX_VALUE;
		}

		//
		// Now, for simplicity, the previous code blindly set new 
		// DEFAUT/CUSTOM indexes without caring if we really have those boxes.
		// The following code makes sure we actually map those values into
		// their proper locations.  This loop will run AT the most, twice.
		//

		while (true)
		{
			if (nNewSel == DEFAULT_BOX_VALUE && !HasDefaultText ())
				nNewSel = nOffset > 0 ? 0 : CUSTOM_BOX_VALUE;
			else if (nNewSel == CUSTOM_BOX_VALUE && !HasCustomText ())
				nNewSel = nOffset > 0 ? DEFAULT_BOX_VALUE : m_nNumColors - 1;
			else
				break;
		}

		//
		// Set the new location
		//

        ChangePickerSelection (nNewSel);
	}
	bHandled = FALSE;
	return FALSE;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle a button up event
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnPickerLButtonUp (UINT uMsg, 
	WPARAM wParam, LPARAM lParam, BOOL &bHandled) 
{

	//
	// Where did the button come up at?
	//

	CPoint pt (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
    int nNewSelection = PickerHitTest (pt);

	//
	// If valid, then change selection and end
	//

	if (nNewSelection != m_nCurrentSel)
		ChangePickerSelection (nNewSelection);
	EndPickerSelection (nNewSelection != INVALID_COLOR);
	return 0;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle mouse move
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnPickerMouseMove (UINT uMsg, WPARAM wParam, 
	LPARAM lParam, BOOL &bHandled) 
{

	//
	// Do a hit test
	//

	CPoint pt (GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam));
    int nNewSelection = PickerHitTest (pt);

	//
    // OK - we have the row and column of the current selection 
	// (may be CUSTOM_BOX_VALUE) Has the row/col selection changed? 
	// If yes, then redraw old and new cells.
	//

    if (nNewSelection != m_nCurrentSel)
        ChangePickerSelection (nNewSelection);
	return 0;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle a paint event
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnPickerPaint (UINT uMsg, 
	WPARAM wParam, LPARAM lParam, BOOL &bHandled) 
{
	CPaintDC dc (m_wndPicker);

	//
    // Draw raised window edge (ex-window style WS_EX_WINDOWEDGE is sposed to do this,
    // but for some reason isn't
	//

	CRect rect;
	m_wndPicker .GetClientRect (&rect);
	if (m_fPickerFlat)
	{
		CPen pen;
		pen .CreatePen (PS_SOLID, 0, ::GetSysColor (COLOR_GRAYTEXT));
		HPEN hpenOld = dc .SelectPen (pen);
		dc .Rectangle (rect .left, rect .top, 
			rect .Width (), rect .Height ());
		dc .SelectPen (hpenOld);
	}
	else
	{
		dc .DrawEdge (&rect, EDGE_RAISED, BF_RECT);
	}

	//
    // Draw the Default Area text
	// 
    if (HasDefaultText ())
        DrawPickerCell (dc, DEFAULT_BOX_VALUE);
 
	//
    // Draw colour cells
	// 

    for (int i = 0; i < m_nNumColors; i++)
        DrawPickerCell (dc, i);
    
	//
    // Draw custom text
	//

    if (HasCustomText ())
        DrawPickerCell (dc, CUSTOM_BOX_VALUE);
	return 0;
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle palette query for picker
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnPickerQueryNewPalette (UINT uMsg, 
	WPARAM wParam, LPARAM lParam, BOOL &bHandled) 
{
    Invalidate ();
    return DefWindowProc (uMsg, wParam, lParam);
}

//-----------------------------------------------------------------------------
//
// @mfunc Handle palette change for picker
//
// @parm UINT | uMsg | Message
//
// @parm WPARAM | wParam | Message w-parameter
//
// @parm LPARAM | lParam | Message l-parameter
//
// @parm BOOL & | bHandled | If handled, set to true
//
// @rdesc Routine results
//
//-----------------------------------------------------------------------------

LRESULT CColorButton::OnPickerPaletteChanged (UINT uMsg, 
	WPARAM wParam, LPARAM lParam, BOOL &bHandled) 
{
    LRESULT lResult = DefWindowProc (uMsg, wParam, lParam);
	if ((HWND) wParam != m_hWnd)
		Invalidate ();
	return lResult;
}

//-----------------------------------------------------------------------------
//
// @mfunc Send notification message
//
// @parm UINT | nCode | Notification code
//
// @parm COLORREF | clr | Color to be sent
//
// @parm BOOL | fColorValid | If true, the color is a valid color.
//
// @rdesc None
//
//-----------------------------------------------------------------------------

void CColorButton::SendNotification (UINT nCode, COLORREF clr, BOOL fColorValid)
{
	NMCOLORBUTTON nmclr;

	nmclr .hdr .code = nCode;
	nmclr .hdr .hwndFrom = m_hWnd;
	nmclr .hdr .idFrom = GetDlgCtrlID ();
	nmclr .fColorValid = fColorValid;
	nmclr .clr = clr;

	::SendMessage (GetParent (), WM_NOTIFY, 
		(WPARAM) GetDlgCtrlID (), (LPARAM) &nmclr);
}

//-----------------------------------------------------------------------------
//
// @mfunc Do a hit test
//
// @parm const POINT & | pt | Point inside the window
//
// @rdesc Index/Item over or INVALID_COLOR
//
//-----------------------------------------------------------------------------

int CColorButton::PickerHitTest (const POINT &pt)
{

	//
    // If we are in the custom text
	//

    if (m_rectCustomText .PtInRect (pt))
        return CUSTOM_BOX_VALUE;

	//
	// If we are in the default text
	//

    if (m_rectDefaultText .PtInRect (pt))
        return DEFAULT_BOX_VALUE;

	//
	// If the point isn't in the boxes, return invalid color
	//

	if (!m_rectBoxes .PtInRect (pt))
		return INVALID_COLOR;

	//
    // Convert the point to an index
	//

	int nRow = (pt .y - m_rectBoxes .top) / m_sizeBox .cy;
	int nCol = (pt .x - m_rectBoxes .left) / m_sizeBox .cx;
	if (nRow < 0 || nRow >= m_nNumRows || nCol < 0 || nCol >= m_nNumColumns)
		return INVALID_COLOR;
	int nIndex = nRow * m_nNumColumns + nCol;
	if (nIndex >= m_nNumColors)
		return INVALID_COLOR;
	return nIndex;
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Belarus Belarus
I am a software developer for 3 years.

Comments and Discussions