1636 lines
41 KiB
C++
1636 lines
41 KiB
C++
// XColorSpectrumCtrl.cpp Version 1.1 - see article at CodeProject.com
|
|
//
|
|
// Author: Hans Dietrich
|
|
// hdietrich@gmail.com
|
|
//
|
|
// Description:
|
|
// XColorSpectrumCtrl implements CXColorSpectrumCtrl, a control that mimics the
|
|
// functionality of the "Standard Colors" color picker in MS Office
|
|
//
|
|
// History
|
|
// Version 1.1 - 2008 April 4
|
|
// - Bug fixes
|
|
//
|
|
// Version 1.0 - 2008 March 12
|
|
// - Initial public release
|
|
//
|
|
// License:
|
|
// This software is released under the Code Project Open License (CPOL),
|
|
// which may be found here: http://www.codeproject.com/info/eula.aspx
|
|
// You are free to use this software in any way you like, except that you
|
|
// may not sell this source code.
|
|
//
|
|
// This software is provided "as is" with no expressed or implied warranty.
|
|
// I accept no liability for any damage or loss of business that this
|
|
// software may cause.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "stdafx.h"
|
|
#include "windows.h"
|
|
#include "windowsx.h"
|
|
#include <stdio.h>
|
|
#include <tchar.h>
|
|
#include <crtdbg.h>
|
|
#include "XColorSpectrumCtrl.h"
|
|
//#include "rgbhsl.h"
|
|
|
|
#pragma warning(disable : 4127) // for _ASSERTE: conditional expression is constant
|
|
#pragma warning(disable : 4996) // disable bogus deprecation warning
|
|
#pragma warning(disable : 4244)
|
|
#pragma warning(disable : 4312)
|
|
#pragma warning(disable : 4267)
|
|
#ifndef UNUSED
|
|
#define UNUSED(x) x
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
// extracted from mfc\src\afxmem.cpp
|
|
static void * operator new(size_t nSize, LPCSTR lpszFileName, int nLine)
|
|
{
|
|
return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
|
|
}
|
|
static void operator delete(void *pData, LPCSTR /*lpszFileName*/, int /*nLine*/)
|
|
{
|
|
::operator delete(pData);
|
|
}
|
|
#define DEBUG_NEW new(THIS_FILE, __LINE__)
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#ifndef __noop
|
|
#if _MSC_VER < 1300
|
|
#define __noop ((void)0)
|
|
#endif
|
|
#endif
|
|
|
|
#undef TRACE
|
|
#define TRACE __noop
|
|
#undef TRACERECT
|
|
#define TRACERECT __noop
|
|
|
|
//=============================================================================
|
|
// if you want to see the TRACE output, uncomment this line:
|
|
//#include "XTrace.h"
|
|
|
|
|
|
//=============================================================================
|
|
// Messages sent to parent window -
|
|
// wParam = RGB color value
|
|
// lParam = control id
|
|
UINT WM_XCOLORPICKER_SELCHANGE = ::RegisterWindowMessage(_T("WM_XCOLORPICKER_SELCHANGE"));
|
|
UINT WM_XCOLORPICKER_SELENDOK = ::RegisterWindowMessage(_T("WM_XCOLORPICKER_SELENDOK"));
|
|
|
|
|
|
//=============================================================================
|
|
// constants used in drawing
|
|
static const int ARROW_WIDTH = 10; // slider arrow
|
|
static const int ARROW_HEIGHT = 17; // slider arrow
|
|
static const int HORIZONTAL_MARGIN = 2; // right margin only
|
|
static const int VERTICAL_MARGIN = ARROW_HEIGHT/2 + 3;
|
|
// this is necessary to
|
|
// make room for arrow on
|
|
// slider bar
|
|
static const int SLIDER_WIDTH = 10;
|
|
static const int ARROW_DELTA = 5; // delta for arrow keys
|
|
static const int CROSSHAIR_SIZE = 19; // spectrum crosshair:
|
|
// height = width
|
|
static const int LUMINOSITY_BAR_WIDTH = 14;
|
|
// cx - SLIDER_OFFSET = left side of slider area
|
|
static const int SLIDER_OFFSET = SLIDER_WIDTH + HORIZONTAL_MARGIN;
|
|
// cx - LUMINOSITY_BAR_OFFSET = left side of luminosity bar
|
|
static const int LUMINOSITY_BAR_OFFSET = SLIDER_OFFSET + LUMINOSITY_BAR_WIDTH + 3;
|
|
// cx - SPECTRUM_OFFSET = right side of spectrum
|
|
static const int SPECTRUM_OFFSET = LUMINOSITY_BAR_OFFSET + 6;
|
|
|
|
|
|
//=============================================================================
|
|
static double HuetoRGB(double m1, double m2, double h)
|
|
//=============================================================================
|
|
{
|
|
if (h < 0) h += 1.0;
|
|
if (h > 1) h -= 1.0;
|
|
if (6.0 * h < 1 )
|
|
return (m1 + (m2 - m1) * h * 6.0);
|
|
if (2.0 * h < 1 )
|
|
return m2;
|
|
if (3.0 * h < 2.0 )
|
|
return (m1 + (m2 - m1) * ((2.0 / 3.0) - h) * 6.0);
|
|
return m1;
|
|
}
|
|
|
|
//=============================================================================
|
|
void RGBtoHSL(COLORREF cr, double *H, double *S, double *L)
|
|
//=============================================================================
|
|
{
|
|
double delta;
|
|
double r = (double) GetRValue(cr) / 255;
|
|
double g = (double) GetGValue(cr) / 255;
|
|
double b = (double) GetBValue(cr) / 255;
|
|
double cmax = max(r, max(g,b));
|
|
double cmin = min(r, min(g,b));
|
|
|
|
*L = (cmax + cmin) / 2.0;
|
|
|
|
if (cmax == cmin)
|
|
{
|
|
*S = 0;
|
|
*H = 0; // it's really undefined
|
|
}
|
|
else
|
|
{
|
|
if (*L < 0.5)
|
|
*S = (cmax - cmin) / (cmax + cmin);
|
|
else
|
|
*S = (cmax - cmin) / (2.0 - cmax - cmin);
|
|
|
|
delta = cmax - cmin;
|
|
|
|
if (r == cmax)
|
|
*H = (g - b) / delta;
|
|
else if (g == cmax)
|
|
*H = 2.0 + (b - r) / delta;
|
|
else
|
|
*H = 4.0 + (r - g) / delta;
|
|
|
|
*H /= 6.0;
|
|
|
|
if (*H < 0.0)
|
|
*H += 1;
|
|
}
|
|
*H *= 240.0;
|
|
*S *= 240.0;
|
|
*L *= 240.0;
|
|
}
|
|
|
|
//=============================================================================
|
|
void RGBtoHSL(COLORREF cr, BYTE *h, BYTE *s, BYTE *l)
|
|
//=============================================================================
|
|
{
|
|
double H, S, L;
|
|
RGBtoHSL(cr, &H, &S, &L);
|
|
*h = (BYTE) (H + 0.5);
|
|
*s = (BYTE) (S + 0.5);
|
|
*l = (BYTE) (L + 0.5);
|
|
if (*h > 239)
|
|
*h = 239;
|
|
if (*s > 240)
|
|
*s = 240;
|
|
if (*l > 240)
|
|
*l = 240;
|
|
}
|
|
|
|
//=============================================================================
|
|
COLORREF HSLtoRGB(double H, double S, double L)
|
|
//=============================================================================
|
|
{
|
|
double r, g, b;
|
|
double m1, m2;
|
|
|
|
H /= 240.0;
|
|
S /= 240.0;
|
|
L /= 240.0;
|
|
|
|
if (S == 0)
|
|
{
|
|
r = g = b = L;
|
|
}
|
|
else
|
|
{
|
|
if (L <= 0.5)
|
|
m2 = L * (1.0 + S);
|
|
else
|
|
m2 = L + S - L * S;
|
|
|
|
m1 = 2.0 * L - m2;
|
|
r = HuetoRGB(m1, m2, H + 1.0/3.0);
|
|
g = HuetoRGB(m1, m2, H);
|
|
b = HuetoRGB(m1, m2, H - 1.0/3.0);
|
|
}
|
|
|
|
return RGB((BYTE)(r*255), (BYTE)(g*255), (BYTE)(b*255));
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// CXColorSpectrumCtrl()
|
|
//
|
|
// Purpose: Construct CXColorSpectrumCtrl object.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Notes: Construction is a two-step process. First, construct the
|
|
// CXColorSpectrumCtrl object. Second, call CXColorSpectrumCtrl::Create()
|
|
// to create the CXColorSpectrumCtrl window.
|
|
//
|
|
CXColorSpectrumCtrl::CXColorSpectrumCtrl()
|
|
: m_hWnd(0),
|
|
m_OldBitmap(NULL),
|
|
m_nLuminosity(0),
|
|
m_Hue(0),
|
|
m_Sat(0),
|
|
m_Lum(0),
|
|
m_crLastSent(NO_COLOR),
|
|
m_crCurrent(NO_COLOR),
|
|
m_crBackground(NO_COLOR),
|
|
m_bSliderDrag(FALSE),
|
|
m_bCrosshairDrag(FALSE),
|
|
m_nDlgCode(DLGC_WANTARROWS|DLGC_WANTTAB),
|
|
m_bIsSpectrumFocused(FALSE),
|
|
m_bIsSliderFocused(FALSE)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::CXColorSpectrumCtrl\n"));
|
|
m_ptCurrent.x = 0;
|
|
m_ptCurrent.y = VERTICAL_MARGIN;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// ~CXColorSpectrumCtrl()
|
|
//
|
|
// Purpose: Destroy CXColorSpectrumCtrl object.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
CXColorSpectrumCtrl::~CXColorSpectrumCtrl()
|
|
{
|
|
DeleteAll();
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// DefWindowProcX()
|
|
//
|
|
// Purpose: Initial window proc to dispatch messages to
|
|
// CXColorSpectrumCtrl::WindowProc(). This allows us to set up
|
|
// the 'this' pointer to CXColorSpectrumCtrl instance.
|
|
//
|
|
// Parameters: Standard windows message parameters.
|
|
//
|
|
// Returns: LRESULT - The return value is the result of the message
|
|
// processing and depends on the message.
|
|
//
|
|
static
|
|
LRESULT __stdcall DefWindowProcX(HWND hWnd, // handle to window
|
|
UINT message, // message identifier
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam) // second message parameter
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
// save 'this' pointer in windows extra memory - the lParam
|
|
// is set when ::CreateWindowEx() is called
|
|
CREATESTRUCT* pcs = (CREATESTRUCT *) lParam;
|
|
if (!pcs)
|
|
{
|
|
TRACE(_T("ERROR - CREATESTRUCT lParam is zero\n"));
|
|
_ASSERTE(pcs);
|
|
return -1; // abort creation
|
|
}
|
|
::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
// dispatch via saved 'this' pointer
|
|
LONG_PTR lData = ::GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
|
if (lData)
|
|
{
|
|
CXColorSpectrumCtrl *pCtrl = (CXColorSpectrumCtrl *) lData;
|
|
return pCtrl->WindowProc(message, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
// probably some WM_NCxxxx message
|
|
TRACE(_T("GWLP_USERDATA = 0 for message = 0x%04X\n"), message);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ::DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Create()
|
|
//
|
|
// Purpose: This virtual function creates the CXColorSpectrumCtrl window.
|
|
//
|
|
// Parameters: hInstance - handle to the instance that contains
|
|
// the window procedure
|
|
// dwStyle - specifies the window style attributes
|
|
// rect - the size and position of the window
|
|
// hParent - the parent window HWND
|
|
// nID - the ID of the child window
|
|
// crInitialColor - initial color selection
|
|
//
|
|
// Returns: BOOL - TRUE = window created successfully
|
|
//
|
|
BOOL CXColorSpectrumCtrl::Create(HINSTANCE hInstance,
|
|
DWORD dwStyle,
|
|
const RECT& rect,
|
|
HWND hParent,
|
|
UINT nID,
|
|
COLORREF crInitialColor /*= RGB(0,0,0)*/)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::Create\n"));
|
|
|
|
m_hWnd = 0;
|
|
|
|
m_hParent = hParent;
|
|
_ASSERTE(IsWindow(m_hParent));
|
|
if (!IsWindow(m_hParent))
|
|
return FALSE;
|
|
|
|
m_rectCtrl = rect;
|
|
TRACERECT(m_rectCtrl);
|
|
TRACE(_T("width=%d height=%d\n"), m_rectCtrl.Width(), m_rectCtrl.Height());
|
|
|
|
m_rectSlider = m_rectCtrl;
|
|
TRACERECT(m_rectSlider);
|
|
|
|
TCHAR * pszClassName = _T("XColorSpectrumCtrl");
|
|
|
|
WNDCLASS wc =
|
|
{
|
|
CS_DBLCLKS, // class style - want WM_LBUTTONDBLCLK messages
|
|
DefWindowProcX, // window proc
|
|
0, // class extra bytes
|
|
0, // window extra bytes
|
|
hInstance, // instance handle
|
|
0, // icon
|
|
::LoadCursor(0, IDC_ARROW), // cursor
|
|
0, // background brush
|
|
0, // menu name
|
|
pszClassName // class name
|
|
};
|
|
|
|
if (!::RegisterClass(&wc))
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
if (dwLastError != ERROR_CLASS_ALREADY_EXISTS)
|
|
{
|
|
TRACE(_T("ERROR - RegisterClass failed, GetLastError() returned %u\n"),
|
|
dwLastError);
|
|
_ASSERTE(FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// we pass 'this' pointer as lpParam, so DefWindowProcX will see it
|
|
// in WM_CREATE message
|
|
m_hWnd = ::CreateWindowEx(0, pszClassName, _T(""), dwStyle,
|
|
m_rectCtrl.left, m_rectCtrl.top, m_rectCtrl.Width(), m_rectCtrl.Height(),
|
|
hParent, (HMENU)nID, hInstance, this);
|
|
|
|
if (m_hWnd == 0)
|
|
{
|
|
#ifdef _DEBUG
|
|
DWORD dwLastError = GetLastError();
|
|
UNUSED(dwLastError);
|
|
TRACE(_T("ERROR - CreateWindowEx failed, GetLastError() returned %u\n"),
|
|
dwLastError);
|
|
_ASSERTE(m_hWnd);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
// set up rect for spectrum
|
|
::GetClientRect(m_hWnd, &m_rectSpectrumClient);
|
|
m_rectSliderClient = m_rectSpectrumClient;
|
|
m_rectSpectrumClient.right -= SPECTRUM_OFFSET;
|
|
m_rectSpectrumClient.top += VERTICAL_MARGIN;
|
|
m_rectSpectrumClient.bottom -= VERTICAL_MARGIN;
|
|
|
|
// set up rect for slider
|
|
m_rectSliderClient.left = m_rectSliderClient.right - LUMINOSITY_BAR_OFFSET;
|
|
m_rectSliderClient.right -= HORIZONTAL_MARGIN;
|
|
m_rectSliderClient.top += VERTICAL_MARGIN - ARROW_HEIGHT/2;
|
|
m_rectSliderClient.bottom -= VERTICAL_MARGIN - ARROW_HEIGHT/2;
|
|
|
|
m_crLastSent = m_crCurrent = crInitialColor;
|
|
RGBtoHSL(m_crLastSent, &m_Hue, &m_Sat, &m_Lum);
|
|
TRACE(_T("m_crCurrent=%06X h=%d s=%d l=%d\n"),
|
|
m_crCurrent, m_Hue, m_Sat, m_Lum);
|
|
|
|
m_ptCurrent = GetPointFromHsl();
|
|
m_nLuminosity = GetLuminosity();
|
|
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, FALSE, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, FALSE, FALSE);
|
|
|
|
return m_hWnd != 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GetRGB()
|
|
//
|
|
// Purpose: This function retrieves the current RGB value.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: COLORREF - the RGB value
|
|
//
|
|
COLORREF CXColorSpectrumCtrl::GetRGB()
|
|
{
|
|
TRACE(_T("GetRGB: %06X\n"), m_crCurrent);
|
|
|
|
return m_crCurrent;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// SetRGB()
|
|
//
|
|
// Purpose: This function sets the RGB value.
|
|
//
|
|
// Parameters: cr - RGB value to set
|
|
//
|
|
// Returns: CXColorSpectrumCtrl& - reference to 'this'
|
|
//
|
|
CXColorSpectrumCtrl& CXColorSpectrumCtrl::SetRGB(COLORREF cr)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::SetRGB\n"));
|
|
|
|
m_crLastSent = m_crCurrent = cr;
|
|
RGBtoHSL(m_crCurrent, &m_Hue, &m_Sat, &m_Lum);
|
|
|
|
m_ptCurrent = GetPointFromHsl();
|
|
m_nLuminosity = GetLuminosity();
|
|
|
|
//TRACE(_T("SetRGB: m_ptCurrent.y=%d m_Hue=%d m_Sat=%d m_Lum=%d =====\n"), m_ptCurrent.y, m_Hue, m_Sat, m_Lum);
|
|
|
|
// update color locators, don't tell parent
|
|
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y,
|
|
IsFocused() && m_bIsSpectrumFocused, FALSE);
|
|
|
|
DrawArrow(NULL, m_nLuminosity, IsFocused() && m_bIsSliderFocused, FALSE);
|
|
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GetHSL()
|
|
//
|
|
// Purpose: This function retrieves the current HSL values.
|
|
//
|
|
// Parameters: h - BYTE pointer to retrieved h value
|
|
// s - BYTE pointer to retrieved s value
|
|
// l - BYTE pointer to retrieved l value
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::GetHSL(BYTE* h, BYTE* s, BYTE* l)
|
|
{
|
|
*h = m_Hue;
|
|
*s = m_Sat;
|
|
*l = m_Lum;
|
|
//TRACE(_T("GetHSL: m_ptCurrent.y=%d h=%d s=%d l=%d =====\n"), m_ptCurrent.y, *h, *s, *l);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// SetHslFromPoint()
|
|
//
|
|
// Purpose: This function sets the hue and sat values from point coordinates.
|
|
//
|
|
// Parameters: point - point for color coordinates
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::SetHslFromPoint(POINT point)
|
|
{
|
|
int y = point.y - VERTICAL_MARGIN;
|
|
m_Hue = (BYTE)((point.x * 239) / (m_rectSpectrumClient.Width() - 1));
|
|
m_Sat = (BYTE)(((m_rectSpectrumClient.Height() - y - 1) * 240) /
|
|
(m_rectSpectrumClient.Height() - 1));
|
|
if (m_Hue > 239)
|
|
m_Hue = 239;
|
|
if (m_Sat > 240)
|
|
m_Sat = 240;
|
|
//TRACE(_T("SetHslFromPoint: hue=%d sat=%d height=%d y = %d =====\n"), m_Hue, m_Sat, m_rectSpectrumClient.Height(), y);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// Internal_SetHSL()
|
|
//
|
|
// Purpose: Internal function to set HSL values
|
|
//
|
|
// Parameters: h - BYTE value of h value
|
|
// s - BYTE value of s value
|
|
// l - BYTE value of l value
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::Internal_SetHSL(BYTE h, BYTE s, BYTE l)
|
|
{
|
|
if (h > 239)
|
|
h = 239;
|
|
if (s > 240)
|
|
s = 240;
|
|
if (l > 240)
|
|
l = 240;
|
|
|
|
m_Hue = h;
|
|
m_Sat = s;
|
|
m_Lum = l;
|
|
|
|
m_crCurrent = HSLtoRGB(h, s, l);
|
|
//TRACE(_T("Internal_SetHSL: m_crCurrent=%06X\n"), m_crCurrent);
|
|
m_ptCurrent = GetPointFromHsl();
|
|
m_nLuminosity = GetLuminosity();
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// SetHSL()
|
|
//
|
|
// Purpose: This function sets the HSL value. Called *only* by parent.
|
|
//
|
|
// Parameters: h - BYTE h value
|
|
// s - BYTE s value
|
|
// l - BYTE l value
|
|
//
|
|
// Returns: CXColorSpectrumCtrl& - reference to 'this'
|
|
//
|
|
CXColorSpectrumCtrl& CXColorSpectrumCtrl::SetHSL(BYTE h, BYTE s, BYTE l)
|
|
{
|
|
TRACE(_T("SetHSL: y=%d %d,%d,%d =====\n"), m_ptCurrent.y, h, s, l);
|
|
|
|
Internal_SetHSL(h, s, l);
|
|
m_crLastSent = m_crCurrent;
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, m_bIsSpectrumFocused, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, m_bIsSliderFocused, FALSE);
|
|
|
|
//TRACE(_T("SetHSL: m_ptCurrent.x=%d m_ptCurrent.y=%d =====\n"), m_ptCurrent.x, m_ptCurrent.y);
|
|
return *this;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GoLeft()
|
|
//
|
|
// Purpose: This function processes left arrow keystrokes.
|
|
//
|
|
// Parameters: nDelta - number of pixels to move
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::GoLeft(int nDelta)
|
|
{
|
|
if (m_bIsSpectrumFocused)
|
|
{
|
|
int nHue = m_Hue - nDelta;
|
|
if (nHue < 0)
|
|
nHue = 0;
|
|
if (nHue > 239)
|
|
nHue = 239;
|
|
|
|
Internal_SetHSL((BYTE)nHue, m_Sat, m_Lum);
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GoRight()
|
|
//
|
|
// Purpose: This function processes right arrow keystrokes.
|
|
//
|
|
// Parameters: nDelta - number of pixels to move
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::GoRight(int nDelta)
|
|
{
|
|
if (m_bIsSpectrumFocused)
|
|
{
|
|
int nHue = m_Hue + nDelta;
|
|
if (nHue < 0)
|
|
nHue = 0;
|
|
if (nHue > 239)
|
|
nHue = 239;
|
|
|
|
Internal_SetHSL((BYTE)nHue, m_Sat, m_Lum);
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GoUp()
|
|
//
|
|
// Purpose: This function processes up arrow keystrokes.
|
|
//
|
|
// Parameters: nDelta - number of pixels to move
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::GoUp(int nDelta)
|
|
{
|
|
if (m_bIsSliderFocused)
|
|
{
|
|
int nLum = m_Lum + nDelta;
|
|
if (nLum < 0)
|
|
nLum = 0;
|
|
if (nLum > 240)
|
|
nLum = 240;
|
|
Internal_SetHSL(m_Hue, m_Sat, (BYTE)nLum);
|
|
DrawArrow(NULL, m_nLuminosity, TRUE, TRUE);
|
|
}
|
|
else
|
|
{
|
|
int nSat = m_Sat + nDelta;
|
|
if (nSat < 0)
|
|
nSat = 0;
|
|
if (nSat > 240)
|
|
nSat = 240;
|
|
|
|
Internal_SetHSL(m_Hue, (BYTE)nSat, m_Lum);
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GoDown()
|
|
//
|
|
// Purpose: This function processes down arrow keystrokes.
|
|
//
|
|
// Parameters: nDelta - number of pixels to move
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::GoDown(int nDelta)
|
|
{
|
|
if (m_bIsSliderFocused)
|
|
{
|
|
int nLum = m_Lum - nDelta;
|
|
if (nLum < 0)
|
|
nLum = 0;
|
|
if (nLum > 240)
|
|
nLum = 240;
|
|
Internal_SetHSL(m_Hue, m_Sat, (BYTE)nLum);
|
|
DrawArrow(NULL, m_nLuminosity, TRUE, TRUE);
|
|
}
|
|
else
|
|
{
|
|
int nSat = m_Sat - nDelta;
|
|
if (nSat < 0)
|
|
nSat = 0;
|
|
if (nSat > 240)
|
|
nSat = 240;
|
|
|
|
Internal_SetHSL(m_Hue, (BYTE)nSat, m_Lum);
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, TRUE, TRUE);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// KeyDown()
|
|
//
|
|
// Purpose: This function processes WM_KEYDOWN messages.
|
|
//
|
|
// Parameters: wParam - virtual-key code
|
|
// lParam - not used
|
|
//
|
|
// Returns: LRESULT - zero = message was processed
|
|
//
|
|
LRESULT CXColorSpectrumCtrl::KeyDown(WPARAM wParam, LPARAM /*lParam*/)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::KeyDown\n"));
|
|
|
|
// for the arrow keys:
|
|
// if the Ctrl key is down, advance by 1
|
|
// if the shift key is down, advance by twice the normal delta
|
|
int nDelta = ARROW_DELTA; // standard advance for arrow keys
|
|
if (IsCtrlDown()) // Ctrl key: advance by 1
|
|
nDelta = 1;
|
|
else if (IsShiftDown()) // Shift key: advance by 2x
|
|
nDelta *= 2;
|
|
|
|
if (wParam == VK_TAB)
|
|
{
|
|
// ignore tab if we don't have focus
|
|
if (::GetFocus() != m_hWnd)
|
|
return 1;
|
|
|
|
if (IsShiftDown())
|
|
{
|
|
// shift is down, tab backwards
|
|
|
|
if (m_bIsSliderFocused)
|
|
{
|
|
// slider has focus, switch to spectrum
|
|
m_bIsSpectrumFocused = TRUE;
|
|
m_bIsSliderFocused = FALSE;
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, TRUE, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, FALSE, FALSE);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// spectrum has focus, give focus to previous control
|
|
::PostMessage(m_hParent, WM_NEXTDLGCTL, 1, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// shift is up, tab forwards
|
|
if (m_bIsSliderFocused)
|
|
{
|
|
// slider has focus, give focus to next control
|
|
::PostMessage(m_hParent, WM_NEXTDLGCTL, 0, 0);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// spectrum has focus, switch to slider
|
|
m_bIsSpectrumFocused = FALSE;
|
|
m_bIsSliderFocused = TRUE;
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, FALSE, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, TRUE, FALSE);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
else if (wParam == VK_END)
|
|
{
|
|
if (m_bIsSliderFocused)
|
|
GoDown(20000); // position to bottom
|
|
else
|
|
GoRight(20000); // position to right
|
|
}
|
|
else if (wParam == VK_HOME)
|
|
{
|
|
if (m_bIsSliderFocused)
|
|
GoUp(20000); // position to top
|
|
else
|
|
GoLeft(20000); // position to left
|
|
}
|
|
else if (wParam == VK_NEXT)
|
|
{
|
|
GoDown(20000); // position to bottom
|
|
}
|
|
else if (wParam == VK_PRIOR)
|
|
{
|
|
GoUp(20000); // position to top
|
|
}
|
|
else if (wParam == VK_DOWN)
|
|
{
|
|
GoDown(nDelta);
|
|
}
|
|
else if (wParam == VK_UP)
|
|
{
|
|
GoUp(nDelta);
|
|
}
|
|
else if (wParam == VK_RIGHT)
|
|
{
|
|
GoRight(nDelta);
|
|
}
|
|
else if (wParam == VK_LEFT)
|
|
{
|
|
GoLeft(nDelta);
|
|
}
|
|
|
|
return 1; // let default processing continue
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// IsShiftDown()
|
|
//
|
|
// Purpose: This function returns TRUE if either shift key is down.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: BOOL - TRUE = shift key is down
|
|
//
|
|
BOOL CXColorSpectrumCtrl::IsShiftDown()
|
|
{
|
|
return GetAsyncKeyState(VK_SHIFT) & 0x8000;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// IsCtrlDown()
|
|
//
|
|
// Purpose: This function returns TRUE if either Ctrl key is down.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: BOOL - TRUE = Ctrl key is down
|
|
//
|
|
BOOL CXColorSpectrumCtrl::IsCtrlDown()
|
|
{
|
|
return GetAsyncKeyState(VK_CONTROL) & 0x8000;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// IsLeftButtonDown()
|
|
//
|
|
// Purpose: This function returns TRUE if left mouse button is down.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: BOOL - TRUE = left mouse button is down
|
|
//
|
|
BOOL CXColorSpectrumCtrl::IsLeftButtonDown()
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
SHORT state = 0;
|
|
if (GetSystemMetrics(SM_SWAPBUTTON)) // check if buttons have been swapped
|
|
state = GetAsyncKeyState(VK_RBUTTON); // buttons swapped, get right button state
|
|
else
|
|
state = GetAsyncKeyState(VK_LBUTTON);
|
|
|
|
// if the most significant bit is set, the button is down
|
|
if (state < 0)
|
|
rc = TRUE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// IsFocused()
|
|
//
|
|
// Purpose: This function returns TRUE if control has focus.
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: BOOL - TRUE = control has focus
|
|
//
|
|
BOOL CXColorSpectrumCtrl::IsFocused()
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
HWND hFocus = ::GetFocus();
|
|
if (hFocus == m_hWnd)
|
|
rc = TRUE;
|
|
|
|
return rc;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GetClientCursorPos()
|
|
//
|
|
// Purpose: This function retrieves the cursor location relative to the
|
|
// control's client rect.
|
|
//
|
|
// Parameters: point - reference to the variable that receives the location
|
|
//
|
|
// Returns: BOOL - TRUE = location is valid
|
|
//
|
|
BOOL CXColorSpectrumCtrl::GetClientCursorPos(POINT& point)
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
if (::GetCursorPos(&point)) // check if screensaver kicked in
|
|
{
|
|
::ScreenToClient(m_hWnd, &point);
|
|
rc = TRUE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// IsPointInSpectrum()
|
|
//
|
|
// Purpose: This function determines if point is in spectrum.
|
|
//
|
|
// Parameters: point = location to check
|
|
//
|
|
// Returns: BOOL - TRUE = point is in spectrum
|
|
//
|
|
BOOL CXColorSpectrumCtrl::IsPointInSpectrum(POINT point)
|
|
{
|
|
BOOL rc = m_rectSpectrumClient.PtInRect(point);
|
|
return rc;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// IsPointInSlider()
|
|
//
|
|
// Purpose: This function determines if point is in slider.
|
|
//
|
|
// Parameters: point = location to check
|
|
//
|
|
// Returns: BOOL - TRUE = point is in slider
|
|
//
|
|
BOOL CXColorSpectrumCtrl::IsPointInSlider(POINT point)
|
|
{
|
|
BOOL rc = m_rectSliderClient.PtInRect(point);
|
|
return rc;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// DeleteAll()
|
|
//
|
|
// Purpose: This function deletes the saved bitmap
|
|
//
|
|
// Parameters: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::DeleteAll()
|
|
{
|
|
if (m_OldBitmap)
|
|
m_dcSpectrum.SelectObject(m_OldBitmap);
|
|
m_OldBitmap = NULL;
|
|
|
|
if (m_bmpSpectrum)
|
|
::DeleteObject(m_bmpSpectrum);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// SetLuminosity()
|
|
//
|
|
// Purpose: This function sets the luminosity value for the specified
|
|
// luminosity slider position.
|
|
//
|
|
// Parameters: nLuminosity - luminosity slider position
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::SetLuminosity(int nLuminosity)
|
|
{
|
|
if (nLuminosity < 0)
|
|
nLuminosity = 0;
|
|
if (nLuminosity > (m_rectSpectrumClient.Height() -1))
|
|
nLuminosity = m_rectSpectrumClient.Height() -1;
|
|
m_nLuminosity = nLuminosity;
|
|
m_Lum = (BYTE)(((m_rectSpectrumClient.Height() - m_nLuminosity - 1) * 240) / (m_rectSpectrumClient.Height() - 1));
|
|
if (m_Lum > 240)
|
|
m_Lum = 240;
|
|
m_crCurrent = HSLtoRGB(m_Hue, m_Sat, m_Lum);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GetLuminosity()
|
|
//
|
|
// Purpose: This function retrieves the luminosity value for the
|
|
// specified RGB value.
|
|
//
|
|
// Parameters: cr - RGB color value
|
|
//
|
|
// Returns: int - luminosity valie
|
|
//
|
|
int CXColorSpectrumCtrl::GetLuminosity()
|
|
{
|
|
UINT l = m_Lum;
|
|
|
|
int lum = m_rectSpectrumClient.Height() - ((m_rectSpectrumClient.Height() * l) / 240);
|
|
|
|
if (lum < 0)
|
|
lum = 0;
|
|
|
|
if (lum > m_rectSpectrumClient.Height() - 1)
|
|
lum = m_rectSpectrumClient.Height() - 1;
|
|
|
|
return lum;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// GetPointFromHsl()
|
|
//
|
|
// Purpose: This function retrieves the POINT location (in spectrum) for
|
|
// the specified RGB value.
|
|
//
|
|
// Parameters: cr - RGB color value
|
|
//
|
|
// Returns: POINT - location of color
|
|
//
|
|
POINT CXColorSpectrumCtrl::GetPointFromHsl()
|
|
{
|
|
POINT point = { 0 };
|
|
|
|
UINT hue = m_Hue;
|
|
UINT sat = m_Sat;
|
|
|
|
point.x = (hue * m_rectSpectrumClient.Width()) / 239;
|
|
if (point.x > (m_rectSpectrumClient.right-1))
|
|
point.x = m_rectSpectrumClient.right-1;
|
|
|
|
point.y = m_rectSpectrumClient.Height() - ((sat * m_rectSpectrumClient.Height()) / 240);
|
|
point.y += VERTICAL_MARGIN;
|
|
if (point.y > (m_rectSpectrumClient.bottom-1))
|
|
point.y = m_rectSpectrumClient.bottom-1;
|
|
|
|
return point;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// DrawSpectrum()
|
|
//
|
|
// Purpose: This function draws the spectrum and initializes the
|
|
// spectrum bitmap and memory dc.
|
|
//
|
|
// Parameters: pDC - pointer to device context used for drawing
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::DrawSpectrum(CXDC *pDC)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::DrawSpectrum\n"));
|
|
|
|
_ASSERTE(pDC);
|
|
if (!pDC)
|
|
return;
|
|
|
|
TRACERECT(m_rectCtrl);
|
|
|
|
if (m_OldBitmap == NULL)
|
|
{
|
|
// spectrum bitmap hasn't been drawn yet
|
|
|
|
if (!m_dcSpectrum.CreateCompatibleDC(pDC))
|
|
{
|
|
TRACE(_T("ERROR - CreateCompatibleDC failed\n"));
|
|
_ASSERTE(FALSE);
|
|
return;
|
|
}
|
|
m_bmpSpectrum = pDC->CreateCompatibleBitmap(m_rectCtrl.Width(), m_rectCtrl.Height());
|
|
m_OldBitmap = (HBITMAP) m_dcSpectrum.SelectObject(m_bmpSpectrum);
|
|
if (m_crBackground == NO_COLOR)
|
|
{
|
|
// no background color specified, just copy current background
|
|
m_dcSpectrum.BitBlt(0, 0, m_rectCtrl.Width(), m_rectCtrl.Height(),
|
|
pDC, 0, 0, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
m_dcSpectrum.FillSolidRect(0, 0, m_rectCtrl.Width(), m_rectCtrl.Height(),
|
|
m_crBackground);
|
|
}
|
|
|
|
UINT cx = m_rectCtrl.Width() - SPECTRUM_OFFSET;
|
|
UINT cy = m_rectCtrl.Height() - 2*VERTICAL_MARGIN;
|
|
|
|
// The saturation and luminosity values must be in the range 0
|
|
// through 240, and the hue value must be in the range 0 through 239.
|
|
|
|
double cxd = cx;
|
|
double cyd = cy;
|
|
for (UINT y = 0; y < cy; y++)
|
|
{
|
|
for (UINT x = 0; x < cx; x++)
|
|
{
|
|
double hue = x;
|
|
hue *= 240.0;
|
|
hue /= cxd - 1.0;
|
|
double sat = y;
|
|
sat *= 240.0;
|
|
sat /= cyd - 1.0;
|
|
COLORREF cr = HSLtoRGB(hue, sat, 120.0);
|
|
m_dcSpectrum.SetPixelV(x, cy - y - 1 + VERTICAL_MARGIN, cr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// write spectrum bitmap to control's dc
|
|
pDC->BitBlt(0, 0, m_rectCtrl.Width(), m_rectCtrl.Height(),
|
|
&m_dcSpectrum, 0, 0, SRCCOPY);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// SendColorToParent()
|
|
//
|
|
// Purpose: This function sends the specified color to the parent.
|
|
//
|
|
// Parameters: nMessage - message ID
|
|
// cr - color to send
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::SendColorToParent(UINT nMessage, COLORREF cr)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::SendColorToParent: cr=%06X m_crLastSent=%06X\n"), cr, m_crLastSent);
|
|
|
|
// don't send duplicate messages
|
|
//if ((nMessage == WM_XCOLORPICKER_SELENDOK) || (cr != m_crLastSent))
|
|
{
|
|
m_crLastSent = cr;
|
|
if (IsWindow(m_hParent) && (cr != NO_COLOR))
|
|
{
|
|
TRACE(_T(">>>>> sending %06X\n"), m_crLastSent);
|
|
::SendMessage(m_hParent, nMessage,
|
|
m_crLastSent, ::GetDlgCtrlID(m_hWnd));
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// DrawCrosshair()
|
|
//
|
|
// Purpose: This function draws spectrum crosshair.
|
|
//
|
|
// Parameters: pDC - pointer to device context to use for drawing
|
|
// startx - x coord of center point
|
|
// starty - y coord of center point
|
|
// bHasFocus - TRUE = control has focus; this determines how the
|
|
// crosshair is drawn
|
|
// bSendColor - TRUE = send WM_XCOLORPICKER_SELCHANGE to parent
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::DrawCrosshair(CXDC *pDC,
|
|
int startx,
|
|
int starty,
|
|
BOOL bHasFocus,
|
|
BOOL bSendColor)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::DrawCrosshair\n"));
|
|
BOOL bRelease = FALSE;
|
|
|
|
if (pDC == NULL)
|
|
{
|
|
pDC = new CXDC(m_hWnd);
|
|
bRelease = TRUE;
|
|
}
|
|
|
|
// restore bitmap - get rid of previous selection
|
|
if (m_OldBitmap)
|
|
pDC->BitBlt(0, 0, m_rectCtrl.Width()-SPECTRUM_OFFSET, m_rectCtrl.Height(),
|
|
&m_dcSpectrum, 0, 0, SRCCOPY);
|
|
|
|
// for each byte in this array:
|
|
// 0 = skip
|
|
// 1 = COLOR_WINDOWTEXT (COLOR_WINDOW if bHasFocus == FALSE)
|
|
static BYTE pixels[CROSSHAIR_SIZE][CROSSHAIR_SIZE] =
|
|
{
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 1
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 2
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 3
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 4
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 5
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 6
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 7
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 8
|
|
1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1, // 9
|
|
1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1, // 10
|
|
1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1, // 11
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 12
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 13
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 14
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 15
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 16
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 17
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0, // 18
|
|
0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0 // 19
|
|
};
|
|
|
|
COLORREF crFill = GetSysColor(COLOR_WINDOWTEXT);
|
|
if (!bHasFocus)
|
|
crFill = GetSysColor(COLOR_WINDOW);
|
|
|
|
POINT pt;
|
|
for (int row = 0; row < CROSSHAIR_SIZE; row++)
|
|
{
|
|
for (int col = 0; col < CROSSHAIR_SIZE; col++)
|
|
{
|
|
if (pixels[row][col] != 1)
|
|
continue;
|
|
pt.x = startx - CROSSHAIR_SIZE/2 + col;
|
|
pt.y = starty - CROSSHAIR_SIZE/2 + row;
|
|
if (m_rectSpectrumClient.PtInRect(pt))
|
|
{
|
|
pDC->SetPixelV(pt.x, pt.y, crFill);
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawLuminosityBar(pDC);
|
|
|
|
if (bRelease)
|
|
delete pDC;
|
|
|
|
// let parent know color selection has changed
|
|
if (bSendColor)
|
|
SendColorToParent(WM_XCOLORPICKER_SELCHANGE, m_crCurrent);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// DrawLuminosityBar()
|
|
//
|
|
// Purpose: This function draws luminosity bar based on the color under
|
|
// the crosshair.
|
|
//
|
|
// Parameters: pDC - pointer to device context used for drawing
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::DrawLuminosityBar(CXDC *pDC)
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::DrawLuminosityBar\n"));
|
|
|
|
BOOL bRelease = FALSE;
|
|
|
|
if (pDC == NULL)
|
|
{
|
|
pDC = new CXDC(m_hWnd);
|
|
_ASSERTE(pDC);
|
|
bRelease = TRUE;
|
|
}
|
|
|
|
// set up for double buffering
|
|
CXDC memDC;
|
|
if (!memDC.CreateCompatibleDC(pDC))
|
|
{
|
|
TRACE(_T("ERROR - CreateCompatibleDC failed\n"));
|
|
_ASSERTE(FALSE);
|
|
return;
|
|
}
|
|
HBITMAP bmp = pDC->CreateCompatibleBitmap(m_rectSlider.Width(), m_rectSlider.Height());
|
|
HBITMAP hOldBitmap = (HBITMAP) memDC.SelectObject(bmp);
|
|
memDC.BitBlt(0, 0, m_rectSlider.Width(), m_rectSlider.Height(),
|
|
pDC, 0, 0, SRCCOPY);
|
|
|
|
COLORREF cr = m_crCurrent;//GetColorUnderCrosshair(); //RGB(255,255,0);
|
|
if (cr == NO_COLOR)
|
|
return;
|
|
|
|
DOUBLE h, s, l;
|
|
RGBtoHSL(cr, &h, &s, &l);
|
|
//if (h > 239.0)
|
|
// h = 239.0;
|
|
if (h > 240.0)
|
|
h = 240.0;
|
|
if (s > 240.0)
|
|
s = 240.0;
|
|
|
|
UINT cx = m_rectSlider.Width();
|
|
UINT cy = m_rectSlider.Height() - 2*VERTICAL_MARGIN;
|
|
double cyd = cy;
|
|
|
|
for (UINT y = 0; y < cy; y++)
|
|
{
|
|
l = y;
|
|
l /= cyd - 1.0;
|
|
l *= 240.0;
|
|
COLORREF rgb = HSLtoRGB(h, s, l);
|
|
for (UINT x = cx-LUMINOSITY_BAR_OFFSET; x < cx-LUMINOSITY_BAR_OFFSET+LUMINOSITY_BAR_WIDTH; x++)
|
|
memDC.SetPixelV(x, cy - y - 1 + VERTICAL_MARGIN, rgb);
|
|
}
|
|
|
|
// end double buffering
|
|
pDC->BitBlt(0, 0, m_rectSlider.Width(), m_rectSlider.Height(),
|
|
&memDC, 0, 0, SRCCOPY);
|
|
|
|
// swap back the original bitmap
|
|
if (hOldBitmap)
|
|
memDC.SelectObject(hOldBitmap);
|
|
if (bmp)
|
|
::DeleteObject(bmp);
|
|
|
|
if (bRelease)
|
|
delete pDC;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
// DrawArrow()
|
|
//
|
|
// Purpose: This function draws slider arrow.
|
|
//
|
|
// Parameters: pDC - pointer to device context to use for drawing
|
|
// starty - y coord of arror tip
|
|
// bHasFocus - TRUE = control has focus; this determines how the
|
|
// arrow is drawn
|
|
// bSendColor - TRUE = send WM_XCOLORPICKER_SELCHANGE to parent
|
|
//
|
|
// Returns: None
|
|
//
|
|
void CXColorSpectrumCtrl::DrawArrow(CXDC *pDC,
|
|
int starty,
|
|
BOOL bHasFocus,
|
|
BOOL bSendColor)
|
|
{
|
|
BOOL bRelease = FALSE;
|
|
|
|
if (pDC == NULL)
|
|
{
|
|
pDC = new CXDC(m_hWnd);
|
|
bRelease = TRUE;
|
|
}
|
|
|
|
// restore bitmap - get rid of previous selection
|
|
if (m_OldBitmap)
|
|
pDC->BitBlt(m_rectCtrl.Width()-SLIDER_OFFSET, 0, SLIDER_OFFSET,
|
|
m_rectCtrl.Height(), &m_dcSpectrum, m_rectCtrl.Width()-SLIDER_OFFSET,
|
|
0, SRCCOPY);
|
|
|
|
// for each byte in this array:
|
|
// 0 = skip
|
|
// 1 = COLOR_WINDOWTEXT
|
|
// 2 = COLOR_WINDOWTEXT (COLOR_BTNSHADOW if bHasFocus == FALSE)
|
|
static BYTE pixels[ARROW_HEIGHT][ARROW_WIDTH] =
|
|
{
|
|
0,0,0,0,0,0,0,0,1,0, // 1
|
|
0,0,0,0,0,0,0,1,1,0, // 2
|
|
0,0,0,0,0,0,1,2,1,0, // 3
|
|
0,0,0,0,0,1,2,2,1,0, // 4
|
|
0,0,0,0,1,2,2,2,1,0, // 5
|
|
0,0,0,1,2,2,2,2,1,0, // 6
|
|
0,0,1,2,2,2,2,2,1,0, // 7
|
|
0,1,2,2,2,2,2,2,1,0, // 8
|
|
1,2,2,2,2,2,2,2,1,0, // 9
|
|
0,1,2,2,2,2,2,2,1,0, // 10
|
|
0,0,1,2,2,2,2,2,1,0, // 11
|
|
0,0,0,1,2,2,2,2,1,0, // 12
|
|
0,0,0,0,1,2,2,2,1,0, // 13
|
|
0,0,0,0,0,1,2,2,1,0, // 14
|
|
0,0,0,0,0,0,1,2,1,0, // 15
|
|
0,0,0,0,0,0,0,1,1,0, // 16
|
|
0,0,0,0,0,0,0,0,1,0 // 17
|
|
};
|
|
|
|
COLORREF crWindowText = GetSysColor(COLOR_WINDOWTEXT);
|
|
COLORREF crBtnShadow = GetSysColor(COLOR_BTNSHADOW);
|
|
COLORREF crFill = crWindowText;
|
|
if (!bHasFocus)
|
|
crFill = crBtnShadow;
|
|
|
|
int startx = m_rectCtrl.Width() - SLIDER_OFFSET;
|
|
if (starty < 0)
|
|
starty = 0;
|
|
if (starty > m_rectSliderClient.Height() - 2*(ARROW_HEIGHT/2) - 1)
|
|
starty = m_rectSliderClient.Height() - 2*(ARROW_HEIGHT/2) - 1;
|
|
|
|
for (int row = 0; row < ARROW_HEIGHT; row++)
|
|
{
|
|
for (int col = 0; col < ARROW_WIDTH; col++)
|
|
{
|
|
COLORREF crFill = crWindowText;
|
|
if (pixels[row][col] == 1)
|
|
crFill = crWindowText;
|
|
else if (pixels[row][col] == 2)
|
|
if (bHasFocus)
|
|
crFill = crWindowText;
|
|
else
|
|
crFill = crBtnShadow;
|
|
else
|
|
continue;
|
|
pDC->SetPixelV(startx + col,
|
|
starty + row - ARROW_HEIGHT/2 + VERTICAL_MARGIN,
|
|
crFill);
|
|
}
|
|
}
|
|
if (bRelease)
|
|
delete pDC;
|
|
|
|
// let parent know color selection has changed
|
|
if (bSendColor)
|
|
SendColorToParent(WM_XCOLORPICKER_SELCHANGE, m_crCurrent);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Helper functions extracted from mfc\src\strcore.cpp
|
|
#ifndef _UNICODE
|
|
|
|
static int _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
|
|
{
|
|
if (count == 0 && wcstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
|
|
wcstr, count);
|
|
_ASSERTE(wcstr == NULL || result <= (int)count);
|
|
if (result > 0)
|
|
wcstr[result-1] = 0;
|
|
return result;
|
|
}
|
|
|
|
#else
|
|
|
|
static int _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
|
|
{
|
|
if (count == 0 && mbstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
|
|
mbstr, count, NULL, NULL);
|
|
_ASSERTE(mbstr == NULL || result <= (int)count);
|
|
if (result > 0)
|
|
mbstr[result-1] = 0;
|
|
return result;
|
|
}
|
|
|
|
#endif //_UNICODE
|
|
|
|
//=============================================================================
|
|
//
|
|
// WindowProc()
|
|
//
|
|
// Purpose: This function is the window proc for CXColorSpectrumCtrl object.
|
|
// Messages are forwarded to this function from DefWindowProcX().
|
|
//
|
|
// Parameters: message - message identifier
|
|
// wParam - first message parameter
|
|
// lParam - second message parameter
|
|
//
|
|
// Returns: LRESULT - The return value is the result of the message
|
|
// processing and depends on the message.
|
|
//
|
|
LRESULT CXColorSpectrumCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_CREATE:
|
|
// will never see this message - it's handled by DefWindowProcX
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
// nothing to erase, since we draw the entire client area
|
|
return TRUE;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
TRACE(_T("in WM_PAINT\n"));
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = (wParam != NULL) ? (HDC) wParam : ::BeginPaint(m_hWnd, &ps);
|
|
if (hdc == 0)
|
|
return 0;
|
|
CXDC dc(hdc);
|
|
DrawSpectrum(&dc);
|
|
DrawCrosshair(&dc, m_ptCurrent.x, m_ptCurrent.y,
|
|
IsFocused() && m_bIsSpectrumFocused, FALSE);
|
|
DrawArrow(&dc, m_nLuminosity, IsFocused() && m_bIsSliderFocused, FALSE);
|
|
if (wParam == NULL)
|
|
::EndPaint(m_hWnd, &ps);
|
|
return 0;
|
|
}
|
|
|
|
case WM_KEYDOWN:
|
|
if (!KeyDown(wParam, lParam))
|
|
return 0;
|
|
//::SendMessage(m_hParent, message, wParam, lParam);
|
|
break;
|
|
|
|
case WM_GETDLGCODE:
|
|
return m_nDlgCode;
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
POINT point;
|
|
point.x = GET_X_LPARAM(lParam);
|
|
point.y = GET_Y_LPARAM(lParam);
|
|
TRACE(_T("in WM_LBUTTONDOWN: point.x=%d point.y=%d\n"), point.x, point.y);
|
|
|
|
if (IsPointInSpectrum(point))
|
|
{
|
|
SetHslFromPoint(point);
|
|
//TRACE(_T("m_Hue=%d m_Sat=%d m_Lum=%d =====\n"), m_Hue, m_Sat, m_Lum);
|
|
m_crCurrent = HSLtoRGB(m_Hue, m_Sat, m_Lum);
|
|
//TRACE(_T("m_crCurrent=%06X\n"), m_crCurrent);
|
|
m_ptCurrent = point;
|
|
}
|
|
else if (IsPointInSlider(point))
|
|
{
|
|
SetLuminosity(point.y - VERTICAL_MARGIN);
|
|
m_crCurrent = HSLtoRGB(m_Hue, m_Sat, m_Lum);
|
|
}
|
|
|
|
HWND hFocus = ::GetFocus();
|
|
if (hFocus != m_hWnd)
|
|
{
|
|
::SetFocus(m_hWnd); // WM_SETFOCUS draws crosshair
|
|
}
|
|
|
|
if (IsPointInSpectrum(point))
|
|
{
|
|
m_bIsSpectrumFocused = TRUE;
|
|
m_bIsSliderFocused = FALSE;
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, TRUE, TRUE);
|
|
DrawArrow(NULL, m_nLuminosity, FALSE, FALSE);
|
|
m_bCrosshairDrag = TRUE;
|
|
}
|
|
else if (IsPointInSlider(point))
|
|
{
|
|
m_bIsSpectrumFocused = FALSE;
|
|
m_bIsSliderFocused = TRUE;
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, FALSE, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, TRUE, TRUE);
|
|
m_bSliderDrag = TRUE;
|
|
}
|
|
TRACE(_T("WM_LBUTTONDOWN: m_crCurrent=%06X\n"), m_crCurrent);
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
TRACE(_T("in WM_LBUTTONUP\n"));
|
|
m_bSliderDrag = FALSE;
|
|
m_bCrosshairDrag = FALSE;
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
{
|
|
TRACE(_T("in CXColorSpectrumCtrl::OnLButtonDblClk\n"));
|
|
|
|
POINT point;
|
|
point.x = GET_X_LPARAM(lParam);
|
|
point.y = GET_Y_LPARAM(lParam);
|
|
// let parent know a color was selected
|
|
if (IsPointInSlider(point) || IsPointInSpectrum(point))
|
|
SendColorToParent(WM_XCOLORPICKER_SELENDOK, m_crCurrent);
|
|
break;
|
|
}
|
|
|
|
case WM_SETFOCUS:
|
|
{
|
|
TRACE(_T("in WM_SETFOCUS\n"));
|
|
LRESULT lResult = ::DefWindowProc(m_hWnd, message, wParam, lParam);
|
|
|
|
if (IsShiftDown())
|
|
m_bIsSliderFocused = TRUE;
|
|
else
|
|
m_bIsSpectrumFocused = TRUE;
|
|
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y,
|
|
IsFocused() && m_bIsSpectrumFocused, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, IsFocused() && m_bIsSliderFocused, FALSE);
|
|
return lResult;
|
|
}
|
|
|
|
case WM_KILLFOCUS:
|
|
{
|
|
TRACE(_T("in WM_KILLFOCUS\n"));
|
|
LRESULT lResult = ::DefWindowProc(m_hWnd, message, wParam, lParam);
|
|
DrawCrosshair(NULL, m_ptCurrent.x, m_ptCurrent.y, FALSE, FALSE);
|
|
DrawArrow(NULL, m_nLuminosity, FALSE, FALSE);
|
|
m_bIsSpectrumFocused = FALSE;
|
|
m_bIsSliderFocused = FALSE;
|
|
return lResult;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
POINT point;
|
|
point.x = GET_X_LPARAM(lParam);
|
|
point.y = GET_Y_LPARAM(lParam);
|
|
|
|
if (!IsLeftButtonDown())
|
|
{
|
|
m_bSliderDrag = FALSE;
|
|
m_bCrosshairDrag = FALSE;
|
|
}
|
|
|
|
if (m_bSliderDrag)
|
|
{
|
|
if (IsPointInSlider(point))
|
|
{
|
|
SetLuminosity(point.y - VERTICAL_MARGIN);
|
|
DrawArrow(NULL, m_nLuminosity, TRUE, TRUE);
|
|
}
|
|
}
|
|
else if (m_bCrosshairDrag)
|
|
{
|
|
if (IsPointInSpectrum(point))
|
|
{
|
|
m_ptCurrent = point;
|
|
SetHslFromPoint(m_ptCurrent);
|
|
m_crCurrent = HSLtoRGB(m_Hue, m_Sat, m_Lum);
|
|
DrawCrosshair(NULL, point.x, point.y, TRUE, TRUE);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ::DefWindowProc(m_hWnd, message, wParam, lParam);
|
|
}
|