/*
 * $Id: ColourPopup.cpp 4483 2008-01-02 09:19:06Z soarchin $
 * 
 * this file is part of easyMule
 * Copyright (C)2002-2008 VeryCD Dev Team ( strEmail.Format("%s@%s", "emuledev", "verycd.com") / http: * www.easymule.org )
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
// ColourPopup.cpp : implementation file
//
// 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.
#include "stdafx.h"
#include <math.h>
#include "ColourPopup.h"
#include "UserMsgs.h"

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


#define DEFAULT_BOX_VALUE -3
#define CUSTOM_BOX_VALUE  -2
#define INVALID_COLOUR    -1

#define MAX_COLOURS      100


ColourTableEntry CColourPopup::m_crColours[] =
    {
        { 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")             }
    };

/////////////////////////////////////////////////////////////////////////////
// CColourPopup

CColourPopup::CColourPopup()
{
	Initialise();
}

CColourPopup::CColourPopup(CPoint p, COLORREF crColour, CWnd* pParentWnd,
                           LPCTSTR szDefaultText /* = NULL */,
                           LPCTSTR szCustomText  /* = NULL */,
                           COLORREF* colourArray /* = NULL*/,
                           int NumberOfColours /* = 0*/ )
{
	colourArrayPassed = colourArray;//copy the pointer to the array of colours we will be using
	if(colourArray && NumberOfColours)
		m_nNumColours = NumberOfColours;
	else
		colourArrayPassed = NULL; //if an array is passed without a size parameter ignore it and use the defaults

	Initialise();//If not a custom palette intialise as NORMAL!

	m_crColour       = m_crInitialColour = crColour;
	m_pParent        = pParentWnd;
	m_strDefaultText = (szDefaultText)? szDefaultText : _T("");
	m_strCustomText  = (szCustomText)?  szCustomText  : _T("");

	CColourPopup::Create(p, crColour, pParentWnd, szDefaultText, szCustomText);
}

void CColourPopup::Initialise()
{
	//set size if it has not been set already
	if(colourArrayPassed==NULL)
		m_nNumColours = sizeof(m_crColours)/sizeof(ColourTableEntry);

	ASSERT(m_nNumColours <= MAX_COLOURS);
	if (m_nNumColours > MAX_COLOURS)
		m_nNumColours = MAX_COLOURS;

	m_nNumColumns       = 0;
	m_nNumRows          = 0;
	m_nBoxSize          = 18;
	m_nMargin           = ::GetSystemMetrics(SM_CXEDGE);
	m_nCurrentSel       = INVALID_COLOUR;
	m_nChosenColourSel  = INVALID_COLOUR;
	m_pParent           = NULL;
	m_crColour          = m_crInitialColour = RGB(0,0,0);

	m_bChildWindowVisible = FALSE;

	// Idiot check: Make sure the colour square is at least 5 x 5;
	if (m_nBoxSize - 2*m_nMargin - 2 < 5)
		m_nBoxSize = 5 + 2*m_nMargin + 2;

	// Create the font
	NONCLIENTMETRICS ncm;
	ncm.cbSize = sizeof(NONCLIENTMETRICS);
	VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
	m_Font.CreateFontIndirect(&(ncm.lfMessageFont));

	// Create the palette
	struct
	{
		LOGPALETTE    LogPalette;
		PALETTEENTRY  PalEntry[MAX_COLOURS];
	}
	pal;

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

	if(colourArrayPassed==NULL)
	{//use default array
		for (int i = 0; i < m_nNumColours; i++)
		{
			pLogPalette->palPalEntry[i].peRed   = GetRValue(m_crColours[i].crColour);
			pLogPalette->palPalEntry[i].peGreen = GetGValue(m_crColours[i].crColour);
			pLogPalette->palPalEntry[i].peBlue  = GetBValue(m_crColours[i].crColour);
			pLogPalette->palPalEntry[i].peFlags = 0;
		}
	}
	else
	{//if an array has been passed use it
		for(int i=0;i<m_nNumColours;i++)
		{
			pLogPalette->palPalEntry[i].peRed   = GetRValue(colourArrayPassed[i]);
			pLogPalette->palPalEntry[i].peGreen = GetGValue(colourArrayPassed[i]);
			pLogPalette->palPalEntry[i].peBlue  = GetBValue(colourArrayPassed[i]);
			pLogPalette->palPalEntry[i].peFlags = 0;
		}
	}

	m_Palette.CreatePalette(pLogPalette);
}

CColourPopup::~CColourPopup()
{
	m_Font.DeleteObject();
	m_Palette.DeleteObject();
}

BOOL CColourPopup::Create(CPoint p, COLORREF crColour, CWnd* pParentWnd,
                          LPCTSTR szDefaultText /* = NULL */,
                          LPCTSTR szCustomText  /* = NULL */)
{
	ASSERT(pParentWnd && ::IsWindow(pParentWnd->GetSafeHwnd()));

	m_pParent  = pParentWnd;
	m_crColour = m_crInitialColour = crColour;

	// Get the class name and create the window
	CString szClassName = AfxRegisterWndClass(CS_CLASSDC|CS_SAVEBITS|CS_HREDRAW|CS_VREDRAW,
	                      AfxGetApp()->LoadStandardCursor(IDC_ARROW),
	                      (HBRUSH) (COLOR_BTNFACE+1),
	                      0);

	if (!CWnd::CreateEx(0, szClassName, _T(""), WS_VISIBLE|WS_POPUP,
	                    p.x, p.y, 100, 100, // size updated soon
	                    pParentWnd->GetSafeHwnd(), 0, NULL))
		return FALSE;

	// Store the Custom text
	if (szCustomText != NULL)
		m_strCustomText = szCustomText;

	// Store the Default Area text
	if (szDefaultText != NULL)
		m_strDefaultText = szDefaultText;

	// Set the window size
	SetWindowSize();

	// Create the tooltips
	CreateToolTips();

	// Find which cell (if any) corresponds to the initial colour
	FindCellFromColour(crColour);

	// Capture all mouse events for the life of this window
	SetCapture();

	return TRUE;
}

BEGIN_MESSAGE_MAP(CColourPopup, CWnd)
//{{AFX_MSG_MAP(CColourPopup)
ON_WM_NCDESTROY()
ON_WM_LBUTTONUP()
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_KEYDOWN()
ON_WM_QUERYNEWPALETTE()
ON_WM_PALETTECHANGED()
ON_WM_KILLFOCUS()
ON_WM_ACTIVATEAPP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CColourPopup message handlers

// For tooltips
BOOL CColourPopup::PreTranslateMessage(MSG* pMsg)
{
	m_ToolTip.RelayEvent(pMsg);

	// Fix (Adrian Roman): Sometimes if the picker loses focus it is never destroyed
	if (GetCapture()->GetSafeHwnd() != m_hWnd)
		SetCapture();

	return CWnd::PreTranslateMessage(pMsg);
}

// If an arrow key is pressed, then move the selection
void CColourPopup::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	int row = GetRow(m_nCurrentSel),
	          col = GetColumn(m_nCurrentSel);

	if (nChar == VK_DOWN)
	{
		if (row == DEFAULT_BOX_VALUE)
			row = col = 0;
		else if (row == CUSTOM_BOX_VALUE)
		{
			if (m_strDefaultText.GetLength())
				row = col = DEFAULT_BOX_VALUE;
			else
				row = col = 0;
		}
		else
		{
			row++;
			if (GetIndex(row,col) < 0)
			{
				if (m_strCustomText.GetLength())
					row = col = CUSTOM_BOX_VALUE;
				else if (m_strDefaultText.GetLength())
					row = col = DEFAULT_BOX_VALUE;
				else
					row = col = 0;
			}
		}
		ChangeSelection(GetIndex(row, col));
	}

	if (nChar == VK_UP)
	{
		if (row == DEFAULT_BOX_VALUE)
		{
			if (m_strCustomText.GetLength())
				row = col = CUSTOM_BOX_VALUE;
			else
			{
				row = GetRow(m_nNumColours-1);
				col = GetColumn(m_nNumColours-1);
			}
		}
		else if (row == CUSTOM_BOX_VALUE)
		{
			row = GetRow(m_nNumColours-1);
			col = GetColumn(m_nNumColours-1);
		}
		else if (row > 0)
			row--;
		else /* row == 0 */
		{
			if (m_strDefaultText.GetLength())
				row = col = DEFAULT_BOX_VALUE;
			else if (m_strCustomText.GetLength())
				row = col = CUSTOM_BOX_VALUE;
			else
			{
				row = GetRow(m_nNumColours-1);
				col = GetColumn(m_nNumColours-1);
			}
		}
		ChangeSelection(GetIndex(row, col));
	}

	if (nChar == VK_RIGHT)
	{
		if (row == DEFAULT_BOX_VALUE)
			row = col = 0;
		else if (row == CUSTOM_BOX_VALUE)
		{
			if (m_strDefaultText.GetLength())
				row = col = DEFAULT_BOX_VALUE;
			else
				row = col = 0;
		}
		else if (col < m_nNumColumns-1)
			col++;
		else
		{
			col = 0;
			row++;
		}

		if (GetIndex(row,col) == INVALID_COLOUR)
		{
			if (m_strCustomText.GetLength())
				row = col = CUSTOM_BOX_VALUE;
			else if (m_strDefaultText.GetLength())
				row = col = DEFAULT_BOX_VALUE;
			else
				row = col = 0;
		}

		ChangeSelection(GetIndex(row, col));
	}

	if (nChar == VK_LEFT)
	{
		if (row == DEFAULT_BOX_VALUE)
		{
			if (m_strCustomText.GetLength())
				row = col = CUSTOM_BOX_VALUE;
			else
			{
				row = GetRow(m_nNumColours-1);
				col = GetColumn(m_nNumColours-1);
			}
		}
		else if (row == CUSTOM_BOX_VALUE)
		{
			row = GetRow(m_nNumColours-1);
			col = GetColumn(m_nNumColours-1);
		}
		else if (col > 0)
			col--;
		else /* col == 0 */
		{
			if (row > 0)
			{
				row--;
				col = m_nNumColumns-1;
			}
			else
			{
				if (m_strDefaultText.GetLength())
					row = col = DEFAULT_BOX_VALUE;
				else if (m_strCustomText.GetLength())
					row = col = CUSTOM_BOX_VALUE;
				else
				{
					row = GetRow(m_nNumColours-1);
					col = GetColumn(m_nNumColours-1);
				}
			}
		}
		ChangeSelection(GetIndex(row, col));
	}

	if (nChar == VK_ESCAPE)
	{
		m_crColour = m_crInitialColour;
		EndSelection(UM_CPN_SELENDCANCEL);
		return;
	}

	if (nChar == VK_RETURN || nChar == VK_SPACE)
	{
		EndSelection(UM_CPN_SELENDOK);
		return;
	}

	CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}

// auto-deletion
void CColourPopup::OnNcDestroy()
{
	CWnd::OnNcDestroy();
	delete this;
}

void CColourPopup::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	// Draw the Default Area text
	if (m_strDefaultText.GetLength())
		DrawCell(&dc, DEFAULT_BOX_VALUE);

	// Draw colour cells
	for (int i = 0; i < m_nNumColours; i++)
		DrawCell(&dc, i);

	// Draw custom text
	if (m_strCustomText.GetLength())
		DrawCell(&dc, CUSTOM_BOX_VALUE);

	// Draw raised window edge (ex-window style WS_EX_WINDOWEDGE is sposed to do this,
	// but for some reason isn't
	CRect rect;
	GetClientRect(rect);
	dc.DrawEdge(rect, EDGE_RAISED, BF_RECT);
}

void CColourPopup::OnMouseMove(UINT nFlags, CPoint point)
{
	int nNewSelection = INVALID_COLOUR;

	// Translate points to be relative raised window edge
	point.x -= m_nMargin;
	point.y -= m_nMargin;

	// First check we aren't in text box
	if (m_strCustomText.GetLength() && m_CustomTextRect.PtInRect(point))
		nNewSelection = CUSTOM_BOX_VALUE;
	else if (m_strDefaultText.GetLength() && m_DefaultTextRect.PtInRect(point))
		nNewSelection = DEFAULT_BOX_VALUE;
	else
	{
		// Take into account text box
		if (m_strDefaultText.GetLength())
			point.y -= m_DefaultTextRect.Height();

		// Get the row and column
		nNewSelection = GetIndex(point.y / m_nBoxSize, point.x / m_nBoxSize);

		// In range? If not, default and exit
		if (nNewSelection < 0 || nNewSelection >= m_nNumColours)
		{
			CWnd::OnMouseMove(nFlags, point);
			return;
		}
	}

	// 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)
		ChangeSelection(nNewSelection);

	CWnd::OnMouseMove(nFlags, point);
}

// End selection on LButtonUp
void CColourPopup::OnLButtonUp(UINT nFlags, CPoint point)
{
	CWnd::OnLButtonUp(nFlags, point);

	DWORD pos = GetMessagePos();
	point = CPoint(LOWORD(pos), HIWORD(pos));

	if (m_WindowRect.PtInRect(point))
		EndSelection(UM_CPN_SELENDOK);
	else
		EndSelection(UM_CPN_SELENDCANCEL);
}

/////////////////////////////////////////////////////////////////////////////
// CColourPopup implementation

int CColourPopup::GetIndex(int row, int col) const
{
	if ((row == CUSTOM_BOX_VALUE || col == CUSTOM_BOX_VALUE) && m_strCustomText.GetLength())
		return CUSTOM_BOX_VALUE;
	else if ((row == DEFAULT_BOX_VALUE || col == DEFAULT_BOX_VALUE) && m_strDefaultText.GetLength())
		return DEFAULT_BOX_VALUE;
	else if (row < 0 || col < 0 || row >= m_nNumRows || col >= m_nNumColumns)
		return INVALID_COLOUR;
	else
	{
		if (row*m_nNumColumns + col >= m_nNumColours)
			return INVALID_COLOUR;
		else
			return row*m_nNumColumns + col;
	}
}

int CColourPopup::GetRow(int nIndex) const
{
	if (nIndex == CUSTOM_BOX_VALUE && m_strCustomText.GetLength())
		return CUSTOM_BOX_VALUE;
	else if (nIndex == DEFAULT_BOX_VALUE && m_strDefaultText.GetLength())
		return DEFAULT_BOX_VALUE;
	else if (nIndex < 0 || nIndex >= m_nNumColours)
		return INVALID_COLOUR;
	else
		return nIndex / m_nNumColumns;
}

int CColourPopup::GetColumn(int nIndex) const
{
	if (nIndex == CUSTOM_BOX_VALUE && m_strCustomText.GetLength())
		return CUSTOM_BOX_VALUE;
	else if (nIndex == DEFAULT_BOX_VALUE && m_strDefaultText.GetLength())
		return DEFAULT_BOX_VALUE;
	else if (nIndex < 0 || nIndex >= m_nNumColours)
		return INVALID_COLOUR;
	else
		return nIndex % m_nNumColumns;
}

void CColourPopup::FindCellFromColour(COLORREF crColour)
{
	if (crColour == CLR_DEFAULT && m_strDefaultText.GetLength())
	{
		m_nChosenColourSel = DEFAULT_BOX_VALUE;
		return;
	}

	for (int i = 0; i < m_nNumColours; i++)
	{
		if (GetColour(i) == crColour)
		{
			m_nChosenColourSel = i;
			return;
		}
	}

	if (m_strCustomText.GetLength())
		m_nChosenColourSel = CUSTOM_BOX_VALUE;
	else
		m_nChosenColourSel = INVALID_COLOUR;
}

// Gets the dimensions of the colour cell given by (row,col)
BOOL CColourPopup::GetCellRect(int nIndex, const LPRECT& rect)
{
	if (nIndex == CUSTOM_BOX_VALUE)
	{
		::SetRect(rect,
		          m_CustomTextRect.left,  m_CustomTextRect.top,
		          m_CustomTextRect.right, m_CustomTextRect.bottom);
		return TRUE;
	}
	else if (nIndex == DEFAULT_BOX_VALUE)
	{
		::SetRect(rect,
		          m_DefaultTextRect.left,  m_DefaultTextRect.top,
		          m_DefaultTextRect.right, m_DefaultTextRect.bottom);
		return TRUE;
	}

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

	rect->left = GetColumn(nIndex) * m_nBoxSize + m_nMargin;
	rect->top  = GetRow(nIndex) * m_nBoxSize + m_nMargin;

	// Move everything down if we are displaying a default text area
	if (m_strDefaultText.GetLength())
		rect->top += (m_nMargin + m_DefaultTextRect.Height());

	rect->right = rect->left + m_nBoxSize;
	rect->bottom = rect->top + m_nBoxSize;

	return TRUE;
}

// Works out an appropriate size and position of this window
void CColourPopup::SetWindowSize()
{
	CSize TextSize;

	// If we are showing a custom or default text area, get the font and text size.
	if (m_strCustomText.GetLength() || m_strDefaultText.GetLength())
	{
		CClientDC dc(this);
		CFont* pOldFont = (CFont*) dc.SelectObject(&m_Font);

		// Get the size of the custom text (if there IS custom text)
		TextSize = CSize(0,0);
		if (m_strCustomText.GetLength())
			TextSize = dc.GetTextExtent(m_strCustomText);

		// Get the size of the default text (if there IS default text)
		if (m_strDefaultText.GetLength())
		{
			CSize DefaultSize = dc.GetTextExtent(m_strDefaultText);
			if (DefaultSize.cx > TextSize.cx)
				TextSize.cx = DefaultSize.cx;
			if (DefaultSize.cy > TextSize.cy)
				TextSize.cy = DefaultSize.cy;
		}

		dc.SelectObject(pOldFont);
		TextSize += CSize(2*m_nMargin,2*m_nMargin);

		// Add even more space to draw the horizontal line
		TextSize.cy += 2*m_nMargin + 2;
	}

	// Get the number of columns and rows
	//m_nNumColumns = (int) sqrt((double)m_nNumColours);    // for a square window (yuk)
	m_nNumColumns = 8;
	m_nNumRows = m_nNumColours / m_nNumColumns;
	if (m_nNumColours % m_nNumColumns)
		m_nNumRows++;

	// Get the current window position, and set the new size
	CRect rect;
	GetWindowRect(rect);

	m_WindowRect.SetRect(rect.left, rect.top,
	                     rect.left + m_nNumColumns*m_nBoxSize + 2*m_nMargin,
	                     rect.top  + m_nNumRows*m_nBoxSize + 2*m_nMargin);

	// if custom text, then expand window if necessary, and set text width as
	// window width
	if (m_strDefaultText.GetLength())
	{
		if (TextSize.cx > m_WindowRect.Width())
			m_WindowRect.right = m_WindowRect.left + TextSize.cx;
		TextSize.cx = m_WindowRect.Width()-2*m_nMargin;

		// Work out the text area
		m_DefaultTextRect.SetRect(m_nMargin, m_nMargin,
		                          m_nMargin+TextSize.cx, 2*m_nMargin+TextSize.cy);
		m_WindowRect.bottom += m_DefaultTextRect.Height() + 2*m_nMargin;
	}

	// if custom text, then expand window if necessary, and set text width as
	// window width
	if (m_strCustomText.GetLength())
	{
		if (TextSize.cx > m_WindowRect.Width())
			m_WindowRect.right = m_WindowRect.left + TextSize.cx;
		TextSize.cx = m_WindowRect.Width()-2*m_nMargin;

		// Work out the text area
		m_CustomTextRect.SetRect(m_nMargin, m_WindowRect.Height(),
		                         m_nMargin+TextSize.cx,
		                         m_WindowRect.Height()+m_nMargin+TextSize.cy);
		m_WindowRect.bottom += m_CustomTextRect.Height() + 2*m_nMargin;
	}

	// Need to check it'll fit on screen: Too far right?
	CSize ScreenSize(::GetSystemMetrics(SM_CXSCREEN), ::GetSystemMetrics(SM_CYSCREEN));
	if (m_WindowRect.right > ScreenSize.cx)
		m_WindowRect.OffsetRect(-(m_WindowRect.right - ScreenSize.cx), 0);

	// Too far left?
	if (m_WindowRect.left < 0)
		m_WindowRect.OffsetRect( -m_WindowRect.left, 0);

	// Bottom falling out of screen?
	if (m_WindowRect.bottom > ScreenSize.cy)
	{
		CRect ParentRect;
		m_pParent->GetWindowRect(ParentRect);
		m_WindowRect.OffsetRect(0, -(ParentRect.Height() + m_WindowRect.Height()));
	}

	// Set the window size and position
	MoveWindow(m_WindowRect, TRUE);
}

void CColourPopup::CreateToolTips()
{
	// Create the tool tip
	if (!m_ToolTip.Create(this))
		return;

	// Add a tool for each cell
	for (int i = 0; i < m_nNumColours; i++)
	{
		CRect rect;
		if (!GetCellRect(i, rect))
			continue;
		m_ToolTip.AddTool(this,_T(""), rect, 1); //  GetColourName(i)
	}
}

void CColourPopup::ChangeSelection(int nIndex)
{
	CClientDC dc(this);        // device context for drawing

	if (nIndex > m_nNumColours)
		nIndex = CUSTOM_BOX_VALUE;

	if ((m_nCurrentSel >= 0 && m_nCurrentSel < m_nNumColours) ||
	        m_nCurrentSel == CUSTOM_BOX_VALUE || m_nCurrentSel == DEFAULT_BOX_VALUE)
	{
		// Set Current selection as invalid and redraw old selection (this way
		// the old selection will be drawn unselected)
		int OldSel = m_nCurrentSel;
		m_nCurrentSel = INVALID_COLOUR;
		DrawCell(&dc, OldSel);
	}

	// Set the current selection as row/col and draw (it will be drawn selected)
	m_nCurrentSel = nIndex;
	DrawCell(&dc, m_nCurrentSel);

	// Store the current colour
	if (m_nCurrentSel == CUSTOM_BOX_VALUE)
		m_pParent->SendMessage(UM_CPN_SELCHANGE, (WPARAM) m_crInitialColour, 0);
	else if (m_nCurrentSel == DEFAULT_BOX_VALUE)
	{
		m_crColour = CLR_DEFAULT;
		m_pParent->SendMessage(UM_CPN_SELCHANGE, (WPARAM) CLR_DEFAULT, 0);
	}
	else
	{
		m_crColour = GetColour(m_nCurrentSel);
		m_pParent->SendMessage(UM_CPN_SELCHANGE, (WPARAM) m_crColour, 0);
	}
}

void CColourPopup::EndSelection(int nMessage)
{
	ReleaseCapture();

	// If custom text selected, perform a custom colour selection
	if (nMessage != UM_CPN_SELENDCANCEL && m_nCurrentSel == CUSTOM_BOX_VALUE)
	{
		m_bChildWindowVisible = TRUE;

		CColorDialog dlg(m_crInitialColour, CC_FULLOPEN | CC_ANYCOLOR, this);

		if (dlg.DoModal() == IDOK)
			m_crColour = dlg.GetColor();
		else
			nMessage = UM_CPN_SELENDCANCEL;

		m_bChildWindowVisible = FALSE;
	}

	if (nMessage == UM_CPN_SELENDCANCEL)
		m_crColour = m_crInitialColour;

	m_pParent->SendMessage(nMessage, (WPARAM) m_crColour, 0);

	// Kill focus bug fixed by Martin Wawrusch
	if (!m_bChildWindowVisible)
		DestroyWindow();
}

void CColourPopup::DrawCell(CDC* pDC, int nIndex)
{
	// For the Custom Text area
	if (m_strCustomText.GetLength() && nIndex == CUSTOM_BOX_VALUE)
	{
		// The extent of the actual text button
		CRect TextButtonRect = m_CustomTextRect;
		TextButtonRect.top += 2*m_nMargin;

		// Fill background
		pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));

		// Draw horizontal line
		pDC->FillSolidRect(m_CustomTextRect.left+2*m_nMargin, m_CustomTextRect.top,
		                   m_CustomTextRect.Width()-4*m_nMargin, 1, ::GetSysColor(COLOR_3DSHADOW));
		pDC->FillSolidRect(m_CustomTextRect.left+2*m_nMargin, m_CustomTextRect.top+1,
		                   m_CustomTextRect.Width()-4*m_nMargin, 1, ::GetSysColor(COLOR_3DHILIGHT));

		TextButtonRect.DeflateRect(1,1);

		// fill background
		if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
			pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DLIGHT));
		else
			pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));

		// Draw button
		if (m_nCurrentSel == nIndex)
			pDC->DrawEdge(TextButtonRect, BDR_RAISEDINNER, BF_RECT);
		else if (m_nChosenColourSel == nIndex)
			pDC->DrawEdge(TextButtonRect, BDR_SUNKENOUTER, BF_RECT);

		// Draw custom text
		CFont *pOldFont = (CFont*) pDC->SelectObject(&m_Font);
		pDC->SetBkMode(TRANSPARENT);
		pDC->DrawText(m_strCustomText, TextButtonRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
		pDC->SelectObject(pOldFont);

		return;
	}

	// For the Default Text area
	if (m_strDefaultText.GetLength() && nIndex == DEFAULT_BOX_VALUE)
	{
		// Fill background
		pDC->FillSolidRect(m_DefaultTextRect, ::GetSysColor(COLOR_3DFACE));

		// The extent of the actual text button
		CRect TextButtonRect = m_DefaultTextRect;
		TextButtonRect.DeflateRect(1,1);

		// fill background
		if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
			pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DLIGHT));
		else
			pDC->FillSolidRect(TextButtonRect, ::GetSysColor(COLOR_3DFACE));

		// Draw thin line around text
		CRect LineRect = TextButtonRect;
		LineRect.DeflateRect(2*m_nMargin,2*m_nMargin);
		CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
		CPen* pOldPen = pDC->SelectObject(&pen);
		pDC->SelectStockObject(NULL_BRUSH);
		pDC->Rectangle(LineRect);
		pDC->SelectObject(pOldPen);

		// Draw button
		if (m_nCurrentSel == nIndex)
			pDC->DrawEdge(TextButtonRect, BDR_RAISEDINNER, BF_RECT);
		else if (m_nChosenColourSel == nIndex)
			pDC->DrawEdge(TextButtonRect, BDR_SUNKENOUTER, BF_RECT);

		// Draw custom text
		CFont *pOldFont = (CFont*) pDC->SelectObject(&m_Font);
		pDC->SetBkMode(TRANSPARENT);
		pDC->DrawText(m_strDefaultText, TextButtonRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
		pDC->SelectObject(pOldFont);

		return;
	}

	CRect rect;
	if (!GetCellRect(nIndex, rect))
		return;

	// Select and realize the palette
	CPalette* pOldPalette = NULL;
	if (pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
	{
		pOldPalette = pDC->SelectPalette(&m_Palette, FALSE);
		pDC->RealizePalette();
	}

	// fill background
	if (m_nChosenColourSel == nIndex && m_nCurrentSel != nIndex)
		pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DHILIGHT));
	else
		pDC->FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));

	// Draw button
	if (m_nCurrentSel == nIndex)
		pDC->DrawEdge(rect, BDR_RAISEDINNER, BF_RECT);
	else if (m_nChosenColourSel == nIndex)
		pDC->DrawEdge(rect, BDR_SUNKENOUTER, BF_RECT);

	CBrush brush(PALETTERGB(GetRValue(GetColour(nIndex)),
	                        GetGValue(GetColour(nIndex)),
	                        GetBValue(GetColour(nIndex)) ));
	CPen   pen;
	pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));

	CBrush* pOldBrush = (CBrush*) pDC->SelectObject(&brush);
	CPen*   pOldPen   = (CPen*)   pDC->SelectObject(&pen);

	// Draw the cell colour
	rect.DeflateRect(m_nMargin+1, m_nMargin+1);
	pDC->Rectangle(rect);

	// restore DC and cleanup
	pDC->SelectObject(pOldBrush);
	pDC->SelectObject(pOldPen);
	brush.DeleteObject();
	pen.DeleteObject();

	if (pOldPalette && pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE)
		pDC->SelectPalette(pOldPalette, FALSE);
}

BOOL CColourPopup::OnQueryNewPalette()
{
	Invalidate();
	return CWnd::OnQueryNewPalette();
}

void CColourPopup::OnPaletteChanged(CWnd* pFocusWnd)
{
	CWnd::OnPaletteChanged(pFocusWnd);

	if (pFocusWnd->GetSafeHwnd() != GetSafeHwnd())
		Invalidate();
}

void CColourPopup::OnKillFocus(CWnd* pNewWnd)
{
	CWnd::OnKillFocus(pNewWnd);

	ReleaseCapture();
	//DestroyWindow(); - causes crash when Custom colour dialog appears.
}

// KillFocus problem fix suggested by Paul Wilkerson.
void CColourPopup::OnActivateApp(BOOL bActive, DWORD hTask)
{
	CWnd::OnActivateApp(bActive, hTask);

	// If Deactivating App, cancel this selection
	if (!bActive)
		EndSelection(UM_CPN_SELENDCANCEL);
}
