ColorSpace ATL Control

Environment: Written with VC6 and ATL3, tested under NT4 SP3+4+5, Windows95 and 98

This is a follow-up to Rajiv Ramachandran’s excellent
Corel PhotoHouse-like Color Chooser
(btw: great work, Rajiv!). Rajiv’s
Color Chooser is based on MFC dialogs.  My version is based on the ATL and
WTL, and is in fact an ActiveX Control.

I’ve used Rajiv’s bitmap and underlying maths code for the graphics itself, and
I’ve restricted my control to an RGB and luminosity selector, cutting out the
full HSV part of Rajiv’s dialog (mainly to reduce the size of the control).

To
use this control, download the demo zip, which contains the built OCX and two
built test apps, one built using the MFC, the other with the ATL window and
dialog classes.  Then register the control with:   REGSVR32 
COLORSPACE.OCX.  The sourcecode zip includes the source for all three
projects.

The control is an ATL composite control.  It exposes two
properties via four get/put methods: get_RGB, put_RGB, get_HSV and put_HSV;
fires one event when the color is changed; and responds to ambient background
color changes.  The control does support ErrorInfo, and throws an Error if
the user attempts to put an invalid RGB value.  Most of the rest of the
code is basically rewritten from Rajiv’s original.

Some interesting ATL bits
include:

– The parent class CComCompositeControl<>::OnDraw in atlctl.h
has been replaced with a custom one – this is only used for drawing the
metafile, and therefore gives the user (the client project developer) a
recognisable image of the control while they are working with it in say the
dialog editor.

– The parent class CComCompositeControl<>::CalcExtent has
been similarly replaced in order to prepare the child control template sizes
converted to draw units.

– The use of the undocumented Platform SDK WTL,
specifically: atlapp.h for the WTL generically; atlmisc.h for the WTL CString
class (very similar to MFC CString); atlgdi.h for CBitmap, CDC, CPen and CBrush;
and atlctrls.h for CUpDownCtrl (== CSpinButtonCtrl).  

The primary
control class declaration is listed here, to give you an idea of its scope, but
for full details see the sourcecode download:


class ATL_NO_VTABLE CColorSpace :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<IColorSpace,
&IID_IColorSpace,
&LIBID_COLORSPACESERVERLib>,
public CComCompositeControl<CColorSpace>,
public IPersistStreamInitImpl<CColorSpace>,
public IOleControlImpl<CColorSpace>,
public IOleObjectImpl<CColorSpace>,
public IOleInPlaceActiveObjectImpl<CColorSpace>,
public IViewObjectExImpl<CColorSpace>,
public IOleInPlaceObjectWindowlessImpl<CColorSpace>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CColorSpace>,
public IPersistStorageImpl<CColorSpace>,
public ISpecifyPropertyPagesImpl<CColorSpace>,
public IQuickActivateImpl<CColorSpace>,
public IDataObjectImpl<CColorSpace>,
public IProvideClassInfo2Impl<&CLSID_ColorSpace,
&DIID__IColorSpaceEvents,
&LIBID_COLORSPACESERVERLib>,
public IPropertyNotifySinkCP<CColorSpace>,
public CComCoClass<CColorSpace, &CLSID_ColorSpace>,
public CProxy_IColorSpaceEvents< CColorSpace >
{
public:
CColorSpace();
CColorSpace(COLORREF c);
~CColorSpace();
COLORREF GetColor() { return m_RGB.GetColorref(); }

// overrides
HRESULT OnDraw(ATL_DRAWINFO &di);
BOOL CalcExtent(SIZE& size);

DECLARE_REGISTRY_RESOURCEID(IDR_COLORSPACE)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CColorSpace)
COM_INTERFACE_ENTRY(IColorSpace)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IViewObjectEx)
COM_INTERFACE_ENTRY(IViewObject2)
COM_INTERFACE_ENTRY(IViewObject)
COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceObject)
COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY(IOleControl)
COM_INTERFACE_ENTRY(IOleObject)
COM_INTERFACE_ENTRY(IPersistStreamInit)
COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(ISpecifyPropertyPages)
COM_INTERFACE_ENTRY(IQuickActivate)
COM_INTERFACE_ENTRY(IPersistStorage)
COM_INTERFACE_ENTRY(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
END_COM_MAP()

BEGIN_PROP_MAP(CColorSpace)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
END_PROP_MAP()

BEGIN_CONNECTION_POINT_MAP(CColorSpace)
CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
CONNECTION_POINT_ENTRY(DIID__IColorSpaceEvents)
END_CONNECTION_POINT_MAP()

BEGIN_MSG_MAP(CColorSpace)
CHAIN_MSG_MAP(CComCompositeControl<CColorSpace>)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUP)
MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSysColorChange)
COMMAND_HANDLER(IDC_EDIT_BLUE, EN_CHANGE, OnChangeEditBlue)
COMMAND_HANDLER(IDC_EDIT_GREEN, EN_CHANGE, OnChangeEditGreen)
COMMAND_HANDLER(IDC_EDIT_RED, EN_CHANGE, OnChangeEditRed)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
END_MSG_MAP()

BEGIN_SINK_MAP(CColorSpace)
//Make sure the Event Handlers have __stdcall calling convention
END_SINK_MAP()

STDMETHOD(OnAmbientPropertyChange)(DISPID dispid);

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IViewObjectEx
DECLARE_VIEW_STATUS(0)

// IColorSpace
public:
STDMETHOD(get_HSV)(/*[out, retval]*/ LONG *pVal);
STDMETHOD(put_HSV)(/*[in]*/ LONG newVal);
STDMETHOD(get_RGB)(/*[out, retval]*/ OLE_COLOR *pVal);
STDMETHOD(put_RGB)(/*[in]*/ OLE_COLOR newVal);
enum { IDD = IDD_COLORSPACE };

protected:
void DrawFilledColor(CDC *pDC, CRect cr, COLORREF c);
void DrawLines(CDC *pDC);
void DrawXorRect(CDC *pDC, CRect& cr);
void CalcSlopes();
void CalcCuboid();
void DrawMarkers(CDC *pDC);
void SetSpinVals();
void SetEditVals();
void DrawAll();
void DrawRGB(CDC *pDC);
void LoadMappedBitmap(CBitmap& bitmap, UINT nIdResource, CSize& size);

CBitmap m_RGBbitmap;
CDC m_memDC;
int m_nRGBwidth, m_nRGBheight;
RGBType m_RGB;
RGBType m_OldRGB;
CPoint m_ptVertex, m_ptTop, m_ptLeft, m_ptRight;
CRect m_rects[3];
CPoint m_Cuboid[8];
BOOL m_bInMouse;
int m_nIndex;
int m_nRedLen, m_nGreenLen, m_nBlueLen;
LineDesc m_lines[3];
CRect m_rectRGB;
CRect m_rectOldColor, m_rectNewColor;
BOOL m_bInitOver;
BOOL m_bInDrawAll;

HSVType m_HSV;
CRect m_rectBright;
CRect m_rectBrightMark;
CRect m_rectHSV;
CDIB m_BrightDIB;
int m_nMouseIn;
BOOL InBright(CPoint pt);
void TrackPoint(CPoint pt);
void CreateBrightDIB();
void SetDIBPalette();
void CalcRects();
void DrawHSV(CDC *pDC);

// additional rects for drawing the metafile
CRect m_rectRed, m_rectGreen, m_rectBlue;
CRect m_rectSmallControls;
CPoint m_ptRed, m_ptGreen, m_ptBlue, m_ptOld, m_ptNew;

protected:
void UpdateColor(BYTE r, BYTE g, BYTE b);
// Windows message handlers
LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnChangeEditBlue(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
LRESULT OnChangeEditGreen(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
LRESULT OnChangeEditRed(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
};

For further info on the WTL, see Richard Grimes’s excellent article in Visual
C++ Developers Journal Vol3 No3 April 2000, or from http://www.vcdj.com
  If you haven’t got the
Platform SDK, you can download it (including the WTL and an ATL/WTL AppWizard)
from Microsoft’s website:
http://msdn.microsoft.com/downloads/c-frame.htm?007#/downloads/sdks/ 

 

Downloads

Download demo project – 113 Kb

Download source – 88 Kb

More by Author

Must Read