// XColorDialog.cpp Version 1.0 - see article at CodeProject.com
//
// Author: Hans Dietrich
// hdietrich@gmail.com
//
// Description:
// XColorDialog implements CXColorDialog, a dialog that mimics the color
// picker dialog in MS Office.
//
// History
// Version 1.0 - 2008 April 5
// - 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 "XColorDialogRes.h"
#include "XColorDialog.h"
#include "TabStandard.h"
#include "TabCustom.h"
#include "uxtheme.h" // from platform sdk
#include "tmschema.h" // from platform sdk
#include "rgbhsl.h"
#include "XColorSpectrumCtrl.h"
#include "XBalloonMsg.h"
#ifdef _DEBUG
#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"
//=============================================================================
BEGIN_MESSAGE_MAP(CXColorDialog, CDialog)
//=============================================================================
//{{AFX_MSG_MAP(CXColorDialog)
ON_NOTIFY(TCN_SELCHANGE, IDC_TAB, OnSelchangeTab)
ON_NOTIFY(TCN_SELCHANGING, IDC_TAB, OnSelchangingTab)
ON_WM_CTLCOLOR()
ON_WM_PAINT()
ON_WM_HELPINFO()
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE(WM_XCOLORPICKER_SELCHANGE, OnSelChange)
ON_REGISTERED_MESSAGE(WM_XCOLORPICKER_SELENDOK, OnSelendOk)
END_MESSAGE_MAP()
//=============================================================================
static BOOL IsVista()
//=============================================================================
{
BOOL rc = FALSE;
OSVERSIONINFO osvi = { 0 };
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (GetVersionEx(&osvi))
{
if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
(osvi.dwMajorVersion >= 6))
{
rc = TRUE;
}
}
return rc;
}
//=============================================================================
CXColorDialog::CXColorDialog(
//=============================================================================
COLORREF crInitial /*= 0*/,
DWORD dwFlags /*= XCD_TOOLTIP_NONE | XCD_OPEN_HEXAGON*/,
CWnd* pParent /*= NULL*/)
: CDialog(_T("IDD_XCOLOR_DIALOG"), pParent),
m_crNew(crInitial),
m_crCurrent(crInitial),
m_dwFlags(dwFlags),
m_nCurrentTab(0),
m_strTitle(_T("")),
m_nColorModel(-1)
{
//{{AFX_DATA_INIT(CXColorDialog)
//}}AFX_DATA_INIT
m_nTooltipFormat = m_dwFlags & 0xF;
m_brushNew.CreateSolidBrush(m_crNew);
m_brushCurrent.CreateSolidBrush(m_crCurrent);
m_hHelpIcon = (HICON) ::LoadImage(AfxGetInstanceHandle(),
IsVista() ? _T("IDI_HELP_VISTA") : _T("IDI_HELP_XP"),
IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
if (!m_hHelpIcon)
{
TRACE(_T("ERROR - failed to load help icon\n"));
}
}
//=============================================================================
CXColorDialog::~CXColorDialog()
//=============================================================================
{
TRACE(_T("in CXColorDialog::~CXColorDialog\n"));
delete m_tabPages[0];
m_tabPages[0] = 0;
delete m_tabPages[1];
m_tabPages[1] = 0;
if (m_brushNew.GetSafeHandle())
m_brushNew.DeleteObject();
if (m_brushCurrent.GetSafeHandle())
m_brushCurrent.DeleteObject();
if (m_hHelpIcon)
::DestroyIcon(m_hHelpIcon);
m_hHelpIcon = 0;
}
//=============================================================================
void CXColorDialog::DoDataExchange(CDataExchange* pDX)
//=============================================================================
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CXColorDialog)
DDX_Control(pDX, IDC_TAB, m_TabCtrl);
//}}AFX_DATA_MAP
}
//=============================================================================
BOOL CXColorDialog::OnInitDialog()
//=============================================================================
{
TRACE(_T("in CXColorDialog::OnInitDialog\n"));
CDialog::OnInitDialog();
//=========================================================================
// get colors for gradient fill
COLORREF crStartColor = GetTabBackgroundColor();
double H, S, L;
RGBtoHSL(crStartColor, &H, &S, &L);
L -= 10.0;
if (L < 0.0)
L = 0.0;
COLORREF crEndColor = HSLtoRGB(H, S, L);
//=========================================================================
// create tab pages
m_tabPages[0] = new CTabStandard(m_crCurrent, crStartColor, crEndColor,
(CXColorHexagonCtrl::TOOLTIP_FORMAT) m_nTooltipFormat, this,
m_hHelpIcon);
ASSERT(m_tabPages[0]);
VERIFY(m_tabPages[0]->Create(_T("IDD_TAB_STANDARD"), this));
m_tabPages[0]->ShowWindow(SW_HIDE);
m_tabPages[1] = new CTabCustom(m_crCurrent, crStartColor, crEndColor,
(CXColorSpectrumCtrl::TOOLTIP_FORMAT) m_nTooltipFormat, this,
m_hHelpIcon);
ASSERT(m_tabPages[1]);
VERIFY(m_tabPages[1]->Create(_T("IDD_TAB_CUSTOM"), this));
m_tabPages[1]->ShowWindow(SW_HIDE);
//=========================================================================
// set color model
if (m_nColorModel == 0 || m_nColorModel == 1)
{
CTabCustom *pCustom = (CTabCustom *) m_tabPages[1];
pCustom->SetColorModel(m_nColorModel);
}
//=========================================================================
// set initial tab
if (m_dwFlags & XCD_OPEN_SPECTRUM)
m_nCurrentTab = 1;
m_tabPages[m_nCurrentTab]->ShowWindow(SW_SHOW);
//=========================================================================
// set up tab control
m_TabCtrl.InsertItem(TCIF_TEXT|TCIF_PARAM, 0, _T("Standard"), 0,
(LPARAM)m_tabPages[0]);
m_TabCtrl.InsertItem(TCIF_TEXT|TCIF_PARAM, 1, _T("Custom"), 0,
(LPARAM)m_tabPages[1]);
m_TabCtrl.SetCurSel(m_nCurrentTab);
//=========================================================================
// position tabs
CRect rectTab;
m_TabCtrl.GetWindowRect(&rectTab);
ScreenToClient(&rectTab);
TRACERECT(rectTab);
CRect rectItem;
m_TabCtrl.GetItemRect(0, &rectItem);
TRACERECT(rectItem);
CRect rectClient;
m_TabCtrl.GetClientRect(&rectClient);
TRACERECT(rectClient);
rectTab.top += rectItem.bottom + GetSystemMetrics(SM_CYEDGE);
rectTab.bottom -= 2 * GetSystemMetrics(SM_CYEDGE);
rectTab.left += GetSystemMetrics(SM_CXEDGE);
rectTab.right = rectTab.left + rectClient.Width() -
2 * GetSystemMetrics(SM_CXEDGE) - 1;
m_tabPages[0]->MoveWindow(rectTab);
m_tabPages[1]->MoveWindow(rectTab);
//=========================================================================
// position new & current colors
CRect rect;
GetDlgItem(IDC_NEW_COLOR)->GetWindowRect(&rect);
ScreenToClient(&rect);
int h = rect.Height();
rect.top += h;
rect.bottom += h;
GetDlgItem(IDC_CURRENT_COLOR)->MoveWindow(&rect);
if (!m_strTitle.IsEmpty())
SetWindowText(m_strTitle);
#ifdef _DEBUG
//Test();
#endif
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
#ifdef _DEBUG
//=============================================================================
// This function tests basic accuracy of algorithms.
void CXColorDialog::Test()
//=============================================================================
{
struct RGBHSL
{
COLORREF cr;
BYTE h, s, l;
};
static struct RGBHSL ColorData[] =
{
RGB(255,0,0), 0,240,120,
RGB(128,0,0), 0,240,60,
RGB(255,255,0), 40,240,120,
RGB(0,255,0), 80,240,120,
RGB(0,128,0), 80,240,60,
RGB(0,255,255), 120,240,120,
RGB(0,0,255), 160,240,120,
RGB(0,0,128), 160,240,60,
RGB(255,0,255), 200,240,120,
RGB(128,0,128), 200,240,60,
RGB(255,255,255), 0,0,240,
RGB(128,128,128), 0,0,120,
RGB(0,0,0), 0,0,0
};
const int nColorDataSize = sizeof(ColorData) / sizeof(ColorData[0]);
CTabCustom *pCustom = (CTabCustom *) m_tabPages[1];
ASSERT(pCustom);
for (int i = 0; i < nColorDataSize; i++)
{
COLORREF cr = ColorData[i].cr;
pCustom->SetRGB(cr);
BYTE h, s, l;
pCustom->GetHSL(&h, &s, &l);
ASSERT(h == ColorData[i].h &&
s == ColorData[i].s &&
l == ColorData[i].l);
TRACE(_T(">>>>> CXColorDialog::Test: RGB(%d,%d,%d) %d,%d,%d\n"),
GetRValue(cr), GetGValue(cr), GetBValue(cr),
h, s, l);
}
}
#endif
//=============================================================================
COLORREF CXColorDialog::GetTabBackgroundColor()
//=============================================================================
{
COLORREF cr = GetSysColor(COLOR_BTNFACE);
typedef HTHEME (__stdcall * OPENTHEMEDATA)(HWND hwnd, LPCWSTR pszClassList);
typedef HRESULT (__stdcall * CLOSETHEMEDATA)(HTHEME hTheme);
typedef HRESULT (__stdcall * GETTHEMECOLOR)(HTHEME hTheme,
int iPartId, int iStateId, int iPropId,
COLORREF *pColor);
HMODULE hUxDll = ::LoadLibrary(_T("UxTheme.dll"));
if (hUxDll)
{
OPENTHEMEDATA pfOpenThemeData =
(OPENTHEMEDATA) ::GetProcAddress(hUxDll, "OpenThemeData");
CLOSETHEMEDATA pfCloseThemeData =
(CLOSETHEMEDATA)::GetProcAddress(hUxDll, "CloseThemeData");
GETTHEMECOLOR pfGetThemeColor =
(GETTHEMECOLOR) ::GetProcAddress(hUxDll, "GetThemeColor");
if (pfOpenThemeData && pfCloseThemeData && pfGetThemeColor)
{
HTHEME hThemeTabCtrl =
(*pfOpenThemeData)(AfxGetMainWnd()->GetSafeHwnd(), L"TAB");
if (hThemeTabCtrl)
{
(*pfGetThemeColor)(hThemeTabCtrl, TABP_BODY, TIS_NORMAL,
TMT_FILLCOLORHINT, &cr);
(*pfCloseThemeData)(hThemeTabCtrl);
TRACE(_T("cr=0x%06X\n"), cr);
}
}
::FreeLibrary(hUxDll);
}
return cr;
}
//=============================================================================
void CXColorDialog::OnSelchangeTab(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
int nNewTab = m_TabCtrl.GetCurSel();
// hide the current tab
if (IsWindow(m_tabPages[m_nCurrentTab]->m_hWnd))
m_tabPages[m_nCurrentTab]->ShowWindow(SW_HIDE);
// show the new tab
if (IsWindow(m_tabPages[nNewTab]->m_hWnd))
{
if (nNewTab == 0)
{
CTabStandard *pStandard = (CTabStandard *) m_tabPages[0];
ASSERT(pStandard && IsWindow(pStandard->m_hWnd));
if (pStandard && IsWindow(pStandard->m_hWnd))
{
pStandard->SetRGB(m_crNew);
}
}
else
{
CTabCustom *pCustom = (CTabCustom *) m_tabPages[1];
ASSERT(pCustom && IsWindow(pCustom->m_hWnd));
if (pCustom && IsWindow(pCustom->m_hWnd))
{
pCustom->SetRGB(m_crNew);
}
}
m_tabPages[nNewTab]->ShowWindow(SW_SHOW);
}
m_nCurrentTab = nNewTab;
*pResult = 0;
}
//=============================================================================
void CXColorDialog::OnSelchangingTab(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
m_nCurrentTab = m_TabCtrl.GetCurSel();
*pResult = 0;
}
//=============================================================================
HBRUSH CXColorDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
//=============================================================================
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
if (nCtlColor == CTLCOLOR_STATIC)
{
if (GetDlgItem(IDC_NEW_COLOR)->m_hWnd == pWnd->m_hWnd)
{
if (m_brushNew.GetSafeHandle())
hbr = m_brushNew;
}
else if (GetDlgItem(IDC_CURRENT_COLOR)->m_hWnd == pWnd->m_hWnd)
{
if (m_brushCurrent.GetSafeHandle())
hbr = m_brushCurrent;
}
}
return hbr;
}
//=============================================================================
void CXColorDialog::OnPaint()
//=============================================================================
{
CPaintDC dc(this); // device context for painting
// draw frame around color boxes
CRect rect;
GetDlgItem(IDC_NEW_COLOR)->GetWindowRect(&rect);
ScreenToClient(&rect);
rect.bottom += rect.Height();
rect.InflateRect(1, 1);
dc.Rectangle(&rect);
// Do not call CDialog::OnPaint() for painting messages
}
//=============================================================================
// handler for WM_XCOLORPICKER_SELCHANGE
LRESULT CXColorDialog::OnSelChange(WPARAM wParam, LPARAM /*lParam*/)
//=============================================================================
{
if (m_crNew != wParam)
{
m_crNew = wParam;
if (m_brushNew.GetSafeHandle())
m_brushNew.DeleteObject();
m_brushNew.CreateSolidBrush(m_crNew);
CRect rect;
GetDlgItem(IDC_NEW_COLOR)->GetWindowRect(&rect);
ScreenToClient(&rect);
InvalidateRect(&rect, FALSE);
}
return 0;
}
//=============================================================================
// handler for WM_XCOLORPICKER_SELENDOK
LRESULT CXColorDialog::OnSelendOk(WPARAM /*wParam*/, LPARAM /*lParam*/)
//=============================================================================
{
// dialog is about to close, no need to update display
OnOK();
return 0;
}
//=============================================================================
void CXColorDialog::GetHSL(BYTE *h, BYTE *s, BYTE *l)
//=============================================================================
{
BOOL bHSL = FALSE;
int nTab = m_TabCtrl.GetCurSel();
if (nTab == 0)
{
CTabStandard *pStandard = (CTabStandard *) m_tabPages[0];
ASSERT(pStandard && IsWindow(pStandard->m_hWnd));
if (pStandard && IsWindow(pStandard->m_hWnd))
{
pStandard->m_ColorHexagon.GetHSL(h, s, l);
bHSL = TRUE;
}
}
else
{
CTabCustom *pCustom = (CTabCustom *) m_tabPages[1];
ASSERT(pCustom && IsWindow(pCustom->m_hWnd));
if (pCustom && IsWindow(pCustom->m_hWnd))
{
pCustom->m_ColorSpectrum.GetHSL(h, s, l);
bHSL = TRUE;
}
}
if (!bHSL)
{
// tabs not created yet, just use algorithm
RGBtoHSL(m_crNew, h, s, l);
}
}
//=============================================================================
int CXColorDialog::GetColorModel()
//=============================================================================
{
int rc = 0;
CTabCustom *pCustom = (CTabCustom *) m_tabPages[1];
ASSERT(pCustom);
if (pCustom)
{
rc = pCustom->GetColorModel();
}
return rc;
}
//=============================================================================
int CXColorDialog::GetCurTab()
//=============================================================================
{
return m_nCurrentTab;
}
//=============================================================================
CXColorDialog& CXColorDialog::SetTooltipFormat(int nFormat)
//=============================================================================
{
m_dwFlags &= ~0xF;
m_dwFlags |= nFormat & 0xF;
return *this;
}
//=============================================================================
CXColorDialog& CXColorDialog::SetStartTab(int nStartTab)
//=============================================================================
{
m_dwFlags &= ~(XCD_OPEN_HEXAGON | XCD_OPEN_SPECTRUM);
if (nStartTab != XCD_OPEN_HEXAGON && nStartTab != XCD_OPEN_SPECTRUM)
nStartTab = XCD_OPEN_HEXAGON;
m_dwFlags |= nStartTab;
return *this;
}
//=============================================================================
CXColorDialog& CXColorDialog::SetCurrentColor(COLORREF cr)
//=============================================================================
{
SetRGB(cr);
return *this;
}
//=============================================================================
CXColorDialog& CXColorDialog::SetRGB(COLORREF cr)
//=============================================================================
{
m_crNew = m_crCurrent = cr;
return *this;
}
//=============================================================================
CXColorDialog& CXColorDialog::SetHSL(BYTE h, BYTE s, BYTE l)
//=============================================================================
{
SetRGB(HSLtoRGB(h, s, l));
return *this;
}
//=============================================================================
BOOL CXColorDialog::OnColorOK()
//=============================================================================
{
TRACE(_T("in CXColorDialog::OnColorOK\n"));
return 0;
}
//=============================================================================
void CXColorDialog::OnOK()
//=============================================================================
{
if (OnColorOK())
return;
CDialog::OnOK();
}
//=============================================================================
BOOL CXColorDialog::OnHelpInfo(HELPINFO* pHelpInfo)
//=============================================================================
{
CString s = _T("");
RECT rect;
rect.left = pHelpInfo->MousePos.x;
rect.top = pHelpInfo->MousePos.y;
int nCtrlId = pHelpInfo->iCtrlId;
if (nCtrlId == IDC_STATIC_NEW_COLOR)
nCtrlId = IDC_NEW_COLOR;
else if (nCtrlId == IDC_STATIC_CURRENT_COLOR)
nCtrlId = IDC_CURRENT_COLOR;
CString strTitle = _T("");
GetWindowText(strTitle);
// the string resource id is the same as control id
if (s.LoadString(nCtrlId))
{
CXBalloonMsg::Show(strTitle,
s,
::GetDlgItem(m_hWnd, pHelpInfo->iCtrlId),
m_hWnd,
AfxGetInstanceHandle(),
(UINT) m_hHelpIcon,
TRUE,
30,
&rect);
}
return TRUE;
}