#pragma	title("Colour Wheel DLL  --  Version 1.0 -- (ClrWheel.C)")
#pragma	subtitle("   Resource Editor - Interface Definitions")

#pragma	info(noext)
	
#define	INCL_DOS		   /* Include OS/2 DOS Kernal		*/
#define	INCL_GPI		   /* Include OS/2 PM GPI Interface	*/
#define	INCL_WIN		   /* Include OS/2 PM Windows Interface	*/

#include <math.h>
#include <os2.h>
#include <stdlib.h>
#include <string.h>

#include <pmcx.h>

#include "clrwheel.h"

/* This	module contains	an example installable control that can	be used	*/
/* within applications where additional	facilities are provided	that	*/
/* are not found within	the default controls of	OS/2 PM.		*/
/*									*/
/* For complete	details	regarding the PM Control Extensions (PMCX)	*/
/* consult the User's Guide.                                            */
/*									*/
/* The DLL is created using the	following command line invocation:	*/
/*									*/
/*     Icc -C -G3e -O+ -Sm -W3 ClrWheel.C				*/

/* Filename:   ClrWheel.C						*/

/*  Version:   1.0							*/
/*  Created:   1993-12-21						*/
/*  Revised:   1994-06-26						*/

/* Routines:   LONG lHSBtoRGB(LONG lHue, LONG lSaturation);		*/
/*	       HBITMAP hbmCreateBitmap(PCLRWHEEL pcw);			*/
/*	       VOID DrawClrWheel(HPS hPS, PCLRWHEEL pcw);		*/
/*	       VOID _System BitmapConstructThread(PCLRWHEEL pcw);	*/
/*	       BOOL    EXPENTRY	ClrWheelRegister(HAB hAB);		*/
/*	       BOOL    EXPENTRY	ClrWheelQuery(PUSERINFO	pUserInfo);	*/
/*	       MRESULT EXPENTRY	ClrWheelStyles(HWND hWnd, ULONG	msg ,	*/
/*					       MPARAM mp1, MPARAM mp2);	*/
/*	       MRESULT EXPENTRY	ClrWheelWndProc(HWND hWnd, ULONG msg,	*/
/*						MPARAM mp1, MPARAM mp2);*/


/* Copyright ¸ 1989-1994  Prominare Inc.  All Rights Reserved.		*/

/* --------------------------------------------------------------------	*/

/************************************************************************/
/************************************************************************/
/*		       DISCLAIMER OF WARRANTIES.			*/
/************************************************************************/
/************************************************************************/
/*     The following [enclosed]	code is	library	code created by		*/
/*     Prominare Inc.  This library code is  provided to you solely	*/
/*     for the purpose of assisting you	in the development of your	*/
/*     applications.  The code is provided "AS IS", without		*/
/*     warranty	of any kind.  Prominare	Inc. shall not be liable	*/
/*     for any damages arising out of your use of the library code,	*/
/*     even if they have been advised of the possibility of such	*/
/*     damages.								*/
/************************************************************************/
/************************************************************************/

/* --- Colour Wheel Window Information Structures ---------------------	*/

typedef	struct _CLRWHEEL
   {
   HWND	     hWnd;		   /* Control Window Handle		*/
   HPS	     hpsBitmap;		   /* Bitmap Presentation Space	Handle	*/
   ULONG     id;		   /* ID Value				*/
   TID	     tid;		   /* Thread ID				*/
   ULONG     flStyle;		   /* Style				*/
   BOOL	     fCapture;		   /* Capture Flag			*/
   ARCPARAMS ap;		   /* Arc Parameters			*/
   RECTL     rcl;		   /* Frame Rectangle			*/
   POINTL    aptlXHair[4];	   /* Cross Hair Point Array		*/
   POINTL    aptl[4];		   /* Bitmap Point Array		*/
   LONG	     lRadius;		   /* Radius				*/
   LONG	     lAngle;		   /* Angle				*/
   LONG	     lSaturationInc;	   /* Saturation Increment		*/
   LONG	     cx;		   /* Control Width			*/
   LONG	     cy;		   /* Control Height			*/
   POINTL    ptlOrigin;		   /* Wheel Origin			*/
   HWND	     hwndOwner;		   /* Owner Window Handle		*/
   HWND	     hwndParent;	   /* Parent Window Handle		*/
   HPOINTER  hptrArrow;		   /* Arrow Pointer			*/
   HBITMAP   hbm;		   /* Bitmap Handle			*/
   HBITMAP   hbmConstruct;	   /* Bitmap Handle			*/
   ULONG     ulOptions;		   /* Colour Table Options		*/
   ULONG     cPlanes;		   /* Bitmap Planes Count		*/
   ULONG     cBitCount;		   /* Bitmap Bits per Plane Count	*/
   } CLRWHEEL ;

typedef	CLRWHEEL *PCLRWHEEL;

/* --- Colour Wheel Support -------------------------------------------	*/

typedef	struct _CLRWHLCDATA
   {
   ULONG    cb;			   /* Structure	Size			*/
   LONG	    lAngle;		   /* Angle				*/
   LONG	    lSaturationInc;	   /* Saturation Increment		*/
   LONG	    lRadius;		   /* Radius				*/
   } CLRWHLCDATA ;

typedef	CLRWHLCDATA *PCLRWHLCDATA;

#define	CWN_RGBCLRSELECTED 750L
#define	CWN_HSBCLRSELECTED 751L
#define	CWN_XHAIRSELECTED  752L
#define	CWN_XHAIRRELEASED  753L

#define	CWS_SOLIDCLR	       0x0000001L
#define	CWS_HSB		       0x0000002L
#define	CWS_RGB		       0x0000004L
#define	CWS_BITMAP	       0x0000008L
#define	CWS_AUTOSIZE	       0x0000010L
#define	CWS_THREADED	       0x0000020L

#define	CWM_QUERYXHAIR	       (WM_USER	+ 32)
#define	CWM_SETXHAIR	       (WM_USER	+ 33)
#define	CWM_QUERYRGBCLR	       (WM_USER	+ 34)
#define	CWM_SETRGBCLR	       (WM_USER	+ 35)
#define	CWM_QUERYHSBCLR	       (WM_USER	+ 36)
#define	CWM_SETHSBCLR	       (WM_USER	+ 37)

BOOL	EXPENTRY ClrWheelRegister(HAB hAB);
BOOL	EXPENTRY ClrWheelQuery(PUSERINFO pUserInfo);
MRESULT	EXPENTRY ClrWheelStyles(HWND hWnd, ULONG msg , MPARAM mp1, MPARAM mp2);

/************************************************************************/
/************************************************************************/
/*									*/
/* Styles supported:							*/
/*									*/
/*     CWS_SOLIDCLR    : Uses solid colours when rendering		*/
/*     CWS_HSB	       : Colour	Notification in	HSB			*/
/*			 CWN_HSBCLRSELECTED				*/
/*     CWS_RGB	       : Colour	Notification in	RGB			*/
/*			 CWN_RGBCLRSELECTED				*/
/*     CWS_BITMAP      : Use bitmap for	render image of	wheel		*/
/*     CWS_AUTOSIZE    : Radius	determined by control			*/
/*     CWS_THREADED    : Use threading when creating bitmap		*/
/*									*/
/* Notification	messages:						*/
/*									*/
/*     CWN_RGBCLRSELECTED : mp2	: RGB2 Value				*/
/*	   RGB colour selected (based on style CWS_RGB)			*/
/*     CWN_HSBCLRSELECTED : mp2	: Low Short = Hue	 (Angle)	*/
/*				  Hi  Short = Saturation (Radius)	*/
/*	   HSB colour selected (based on style CWS_HSB)			*/
/*     CWN_XHAIRSELECTED  : mp2	: 0L					*/
/*	   Cross hair selected (button 1 down event)			*/
/*     CWN_XHAIRRELEASED  : mp2	: 0L					*/
/*	   Cross hair released (button 1 up event)			*/
/*									*/
/* Control messages:							*/
/*									*/
/*     CWM_QUERYXHAIR	   : Query cross hair location			*/
/*	   Input:							*/
/*		   mp1 = 0L;						*/
/*		   mp2 = 0L;						*/
/*	   Ouptut:							*/
/*		   x = SHORT1FROMMR(mr);				*/
/*		   y = SHORT2FROMMR(mr);				*/
/*     CWM_SETXHAIR	   : Set cross hair location			*/
/*	   Input:							*/
/*		   mp1 = x;						*/
/*		   mp2 = y;						*/
/*	   Ouptut:							*/
/*		   mr =	TRUE  :	position accepted and cross hair	*/
/*				updated					*/
/*		   mr =	FALSE :	invalid	position specified, cross hair	*/
/*				not updated				*/
/*     CWM_QUERYRGBCLR	   : Query current RGB colour under cross hair	*/
/*	   Input:							*/
/*		   mp1 = 0L;						*/
/*		   mp2 = 0L;						*/
/*	   Ouptut:							*/
/*		   mr =	RGB2 rgb2  : RGB Colour				*/
/*     CWM_SETRGBCLR	   : Set cross hair location using RGB colour	*/
/*	   Input:							*/
/*		   mp1 = RGB2 rgb2;					*/
/*		   mp2 = 0L;						*/
/*	   Ouptut:							*/
/*		   x = SHORT1FROMMR(mr);				*/
/*		   y = SHORT2FROMMR(mr);				*/
/*     CWM_QUERYHSBCLR	   : Query current RGB colour under cross hair	*/
/*	   Input:							*/
/*		   mp1 = 0L;						*/
/*		   mp2 = 0L;						*/
/*	   Ouptut:							*/
/*		   sHue	       = SHORT1FROMMR(mr);			*/
/*		   sSaturation = SHORT2FROMMR(mr);			*/
/*     CWM_SETHSBCLR	   : Set cross hair location using HSB colour	*/
/*	   Input:							*/
/*		   mp1 = lHue;						*/
/*		   mp2 = lSaturation;					*/
/*	   Ouptut:							*/
/*		   x = SHORT1FROMMR(mr);				*/
/*		   y = SHORT2FROMMR(mr);				*/
/*									*/
/************************************************************************/
/************************************************************************/

LONG	lHSBtoRGB(LONG lHue, LONG lSaturation);
HBITMAP	hbmCreateBitmap(PCLRWHEEL pcw);
VOID	DrawClrWheel(HPS hPS, PCLRWHEEL	pcw);
VOID	_System	BitmapConstructThread(PCLRWHEEL	pcw);

MRESULT	EXPENTRY ClrWheelWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2);

#pragma	subtitle("   Colour Wheel DLL - Control Initialization Function")
#pragma	page ( )

/* --- ClrWheelRegister	-------------------------------- [ Public ] ---	*/
/*									*/
/*     This function is	used to	register the installable control class	*/
/*     with OS/2 Presentation Manager.	The registration must use the	*/
/*     USER_CWINDOWWORDS to reserve memory for the control to allow for	*/
/*     proper usage by Resource	Editor and for use by the control	*/
/*     dialog and window procedures.  The information for the control	*/
/*     containing the style, presentation parameters and control data	*/
/*     is pointed to by	a pointer that can be referenced by the		*/
/*     control's dialog and window procedure as required.  The memory   */
/*     for the structure is allocated and controlled through Resource	*/
/*     Editor.	 The control can reserve more memory for its use	*/
/*     by adding the memory required to	that of	the USER_CWINDOWWORDS	*/
/*     constant.							*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     HAB hAB;	= Application Anchor Block Handle			*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     ClrWheelRegister	=  TRUE	: Class	Registration Successful		*/
/*			= FALSE	: Class	Registration Failed		*/
/*									*/
/* --------------------------------------------------------------------	*/

BOOL EXPENTRY ClrWheelRegister(HAB hAB)

{
		       /* Register the control class with OS/2		*/
		       /* Presentation Manager and return registration	*/
		       /* result					*/

return(WinRegisterClass(hAB, "Clr.Wheel", (PFNWP)ClrWheelWndProc,
			CS_SYNCPAINT | CS_PARENTCLIP | CS_SIZEREDRAW,
			USER_CWINDOWWORDS));

}
#pragma	subtitle("   Colour Wheel DLL - Query Control Information Function")
#pragma	page ( )

/* --- ClrWheelQuery ----------------------------------- [ Public ] ---	*/
/*									*/
/*     This function is	used to	return to the caller information	*/
/*     regarding the installable control and its capabilities.	The	*/
/*     function	should return a	true value otherwise Resource Editor	*/
/*     will not	register the control as	being usable.			*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     PUSERINFO pUserInfo; = User Information Pointer			*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     ClrWheelQuery =	TRUE : User Information	Being Returned		*/
/*		     = FALSE : No User Information Available		*/
/*									*/
/* --------------------------------------------------------------------	*/

BOOL EXPENTRY ClrWheelQuery(PUSERINFO pUserInfo)

{
		       /* Complete the User Information	structure	*/
		       /* passed to the	function by Resource Editor	*/

		       /* Complete the version and number of control	*/
		       /* types.  In Version 1.0 of CCRS, only one	*/
		       /* control type is used.				*/
pUserInfo->ulMajor = 3UL;
pUserInfo->ulMinor = 0UL;
pUserInfo->cTypes = 1UL;
		       /* Complete the author and control classname	*/

memcpy(pUserInfo->szAuthor,    "Prominare Inc.", 15);
memcpy(pUserInfo->szClassname, "Clr.Wheel", 10);
memcpy(pUserInfo->szName,      "ClrWheel", 9);

		       /* Complete the default size and	style of the	*/
		       /* first	user control type			*/

pUserInfo->utDefined[0].cx	     = 64L;
pUserInfo->utDefined[0].cy	     = 64L;
pUserInfo->utDefined[0].flStyle	     = WS_VISIBLE | CWS_AUTOSIZE | CWS_BITMAP |	CWS_THREADED;

		       /* Set the maximum amount of text control can	*/
		       /* accept including NULL	termination byte	*/

pUserInfo->utDefined[0].cMaxText     = 0UL;

		       /* Save the style's dialogue ID, type, control   */
		       /* data size and	count of style masks		*/

pUserInfo->utDefined[0].idDlg	     = DLG_CTRLUSER;
pUserInfo->utDefined[0].ulType	     = UTYPE_PRIVATE;
pUserInfo->utDefined[0].cCtlData     = sizeof(CLRWHLCDATA);
pUserInfo->utDefined[0].cMasks	     = 6UL;
pUserInfo->utDefined[0].flOptions    = PMCXOPT_REFRESH;
pUserInfo->utDefined[0].flStyleType  = STYLETYPE_BITFLAGS;
pUserInfo->utDefined[0].stMasks[0].flStyleMask = CWS_SOLIDCLR;
pUserInfo->utDefined[0].stMasks[0].idStyle     = IDS_CWS_SOLIDCLR;
pUserInfo->utDefined[0].stMasks[1].flStyleMask = CWS_HSB;
pUserInfo->utDefined[0].stMasks[1].idStyle     = IDS_CWS_HSB;
pUserInfo->utDefined[0].stMasks[2].flStyleMask = CWS_RGB;
pUserInfo->utDefined[0].stMasks[2].idStyle     = IDS_CWS_RGB;
pUserInfo->utDefined[0].stMasks[3].flStyleMask = CWS_BITMAP;
pUserInfo->utDefined[0].stMasks[3].idStyle     = IDS_CWS_BITMAP;
pUserInfo->utDefined[0].stMasks[4].flStyleMask = CWS_AUTOSIZE;
pUserInfo->utDefined[0].stMasks[4].idStyle     = IDS_CWS_AUTOSIZE;
pUserInfo->utDefined[0].stMasks[5].flStyleMask = CWS_THREADED;
pUserInfo->utDefined[0].stMasks[5].idStyle     = IDS_CWS_THREADED;

		       /* Save the description of the control		*/

memcpy(pUserInfo->utDefined[0].szDescription, "Colour Wheel", 13);

		       /* Return the success flag back to Resource	*/
		       /* Editor					*/
return(TRUE);
}
#pragma	subtitle("   Colour Wheel DLL - HSB to RGB Conversion Function")
#pragma	page( )

/* --- lHSBtoRGB --------------------------------------	[ Private ] ---	*/
/*									*/
/*     This function is	used to	determine the RGB value	for a given	*/
/*     hue and saturation.  The	function is based upon the algorithm	*/
/*     presented in the	Foly and van Dam book "Computer Graphics:       */
/*     Principles and Practice" Second Addition, page 593 Figure        */
/*     13.34.  The routine has been adapted to ignore the brightness	*/
/*     since it	will be	constant within	this implementation.		*/
/*     The Hue value corresponds to the	angle and the Saturation	*/
/*     corresponds to the radius of the	circle.	 The Hue value is	*/
/*     from 0 to 360ø and the Saturation value is from 0 to 100.  The	*/
/*     RGB values are defined by the system to be from 0 to 255.	*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     LONG lHue;	 = Hue Value					*/
/*     LONG lSaturation; = Saturation Value				*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     lHSBtoRGB = Resultant RGB Colour					*/
/*									*/
/* --------------------------------------------------------------------	*/

LONG lHSBtoRGB(LONG lHue, LONG lSaturation)

{
RGB2   rgb2;			   /* RGB Colour Holder			*/
PLONG  plClr = (PLONG)(PVOID)&rgb2;/* Long Pointer to RGB Colour Holder	*/
		
		       /* Initialize the options component of the RGB	*/
		       /* holder since it is a reserved	value		*/
rgb2.fcOptions = 0;
		       /* Check	to see if the saturation level is 0	*/
		       /* in which case	the colour should be white	*/
if ( lSaturation == 0 )
   {
   rgb2.bRed   =
   rgb2.bBlue  =
   rgb2.bGreen = 255;
   }
else
   {
		       /* Check	to see if the hue is at	its maximum	*/
		       /* value	in which case the hue should revert to	*/
		       /* its lower limit				*/
   if (	lHue ==	360L )
       lHue = 0L;
		       /* Break	the hue	into 6 wedges which corresponds	*/
		       /* to red, yellow, green, cyan, blue and	magenta	*/
   switch ( lHue / 60L )
       {
		       /* Red wedge					*/
       case 0 :
	   rgb2.bRed   = (BYTE)255;
	   rgb2.bGreen = (BYTE)((((100 - ((lSaturation * (100 -	((lHue % 60L) *	100L) /	60)) / 100L))) * 255L) / 100L);
	   rgb2.bBlue  = (BYTE)(((100 -	lSaturation) * 255L) / 100L);
	   break;
		       /* Yellow wedge					*/
       case 1 :
	   rgb2.bRed   = (BYTE)((((100 - ((lSaturation * ((lHue	% 60L) * 100L) / 60) / 100L))) * 255L) / 100L);
	   rgb2.bGreen = (BYTE)255;
	   rgb2.bBlue  = (BYTE)(((100 -	lSaturation) * 255L) / 100L);
	   break;
		       /* Green	wedge					*/
       case 2 :
	   rgb2.bRed   = (BYTE)(((100 -	lSaturation) * 255L) / 100L);
	   rgb2.bGreen = (BYTE)255;
	   rgb2.bBlue  = (BYTE)((((100 - ((lSaturation * (100 -	((lHue % 60L) *	100L) /	60)) / 100L))) * 255L) / 100L);
	   break;
		       /* Cyan wedge					*/
       case 3 :
	   rgb2.bRed   = (BYTE)(((100 -	lSaturation) * 255L) / 100L);
	   rgb2.bGreen = (BYTE)((((100 - ((lSaturation * ((lHue	% 60L) * 100L) / 60) / 100L))) * 255L) / 100L);
	   rgb2.bBlue  = (BYTE)255;
	   break;
		       /* Blue wedge					*/
       case 4 :
	   rgb2.bRed   = (BYTE)((((100 - ((lSaturation * (100 -	((lHue % 60L) *	100L) /	60)) / 100L))) * 255L) / 100L);
	   rgb2.bGreen = (BYTE)(((100 -	lSaturation) * 255L) / 100L);
	   rgb2.bBlue  = (BYTE)255;
	   break;
		       /* Magenta wedge					*/
       case 5 :
	   rgb2.bRed   = (BYTE)255;
	   rgb2.bGreen = (BYTE)(((100 -	lSaturation) * 255L) / 100L);
	   rgb2.bBlue  = (BYTE)((((100 - ((lSaturation * ((lHue	% 60L) * 100L) / 60) / 100L))) * 255L) / 100L);
	   break;
       }
   }
return(*plClr);
}
#pragma	subtitle("   Colour Wheel DLL - Control Window Bitmap Image Procedure")
#pragma	page( )

/* --- hbmCreateBitmap --------------------------------	[ Private ] ---	*/
/*									*/
/*     This function is	used to	calculate and create the bitmap		*/
/*     storage for both	the main Client	and Context Windows.		*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     PCLRWHEEL pcw; =	Colour Wheel Data Pointer			*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     hbmCreateBitmap = NULL :	Error, Bitmap Not Created		*/
/*		       =      :	Bitmap Created				*/
/*									*/
/* --------------------------------------------------------------------	*/

HBITMAP	hbmCreateBitmap(PCLRWHEEL pcw)

{
BITMAPINFOHEADER2 bminfo2;	   /* Bitmap Information Header		*/
HDC		  hDC;		   /* Device Context Handle		*/
SIZEL		  sizl;		   /* Sizing Structure			*/
PLONG		  plFormats;	   /* Formats Array			*/
LONG		  cFormats;	   /* Formats Count			*/

if ( pcw->hbmConstruct )
   GpiDeleteBitmap(pcw->hbmConstruct);

		       /* Get bitmap device context handle for the main	*/
		       /* Client Window					*/

if ( !(hDC = DevOpenDC((HAB)NULL, OD_MEMORY, "*", 0L, 0L, 0L)) )
   return((HBITMAP)NULL);
		       /* Create bitmap	presentation space specifying	*/
		       /* entire map Client Window for size required	*/
sizl.cx	= pcw->cx;
sizl.cy	= pcw->cy;

if ( !(pcw->hpsBitmap =	GpiCreatePS((HAB)NULL, hDC, &sizl, PU_PELS | GPIT_NORMAL | GPIA_ASSOC))	)
   {
		       /* Error	occurred during	creation of		*/
		       /* presentation space, close device context	*/
   DevCloseDC(hDC);
   return((HBITMAP)NULL);
   }
		       /* Check	to see if the device capabilities for	*/
		       /* the number of	planes and bitcounts have been	*/
		       /* determined yet on a previous pass through	*/
		       /* this routine and if they haven't, get them    */
if ( pcw->cPlanes == 0 )
   {
		       /* Get the number of bitmap formats that	the	*/
		       /* display driver supports			*/

   DevQueryCaps(hDC, CAPS_BITMAP_FORMATS, 1L, &cFormats);

		       /* Allocate memory for the formats array		*/

   DosAllocMem((PPVOID)(PVOID)&plFormats, 2UL *	cFormats,
	       PAG_READ	| PAG_WRITE | PAG_COMMIT);

		       /* Get the bitmap display formats.  The first	*/
		       /* set within the array will be the one that	*/
		       /* most closely matches the display device.	*/

   GpiQueryDeviceBitmapFormats(pcw->hpsBitmap, cFormats	* 2L, plFormats);

   pcw->cPlanes	  = (ULONG)plFormats[0];
   pcw->cBitCount = (ULONG)plFormats[1];

		       /* Release the memory allocated for the bitmap	*/
		       /* formats array					*/

   DosFreeMem((PVOID)plFormats);
   }
		       /* Create actual	bitmap storage for colour wheel	*/
		       /* having the default plane and bit count	*/

memset(&bminfo2, 0, sizeof(BITMAPINFOHEADER2));
bminfo2.cbFix	  = sizeof(BITMAPINFOHEADER2);
bminfo2.cx	  = (ULONG)pcw->cx;
bminfo2.cy	  = (ULONG)pcw->cy;
bminfo2.cPlanes	  = (USHORT)pcw->cPlanes;
bminfo2.cBitCount = (USHORT)pcw->cBitCount;

if ( !(pcw->hbmConstruct = GpiCreateBitmap(pcw->hpsBitmap, &bminfo2, 0L, 0L, 0L)) )
   {
		       /* Error	occurred during	creation of bitmap	*/
		       /* storage, destroy presentation	space created	*/
		       /* and close device context opened		*/

   GpiDestroyPS(pcw->hpsBitmap);
   DevCloseDC(hDC);
   return((HBITMAP)NULL);
   }
		       /* Set bitmap as	current	bitmap to use		*/

GpiSetBitmap(pcw->hpsBitmap, pcw->hbmConstruct);

		       /* Draw the colour wheel				*/

DrawClrWheel(pcw->hpsBitmap, pcw);

		       /* Set the bitmap to allow completion of	bitmap	*/
		       /* in memory					*/

GpiSetBitmap(pcw->hpsBitmap, (HDC)NULL);

		       /* Destroy the memory device context		*/

GpiAssociate(pcw->hpsBitmap, (HDC)NULL);

		       /* Destroy the presentation spaces used		*/

GpiDestroyPS(pcw->hpsBitmap);
DevCloseDC(hDC);
pcw->hpsBitmap = (HPS)NULL;

		       /* Return the bitmap handle that	will be	used in	*/
		       /* painting the image on	the window		*/
return(pcw->hbmConstruct);

}
#pragma	subtitle("   Colour Wheel DLL - Control Window Procedure")
#pragma	page( )

/* --- DrawClrWheel -----------------------------------	[ Private ] ---	*/
/*									*/
/*     This function is	used to	draw the actual	colour wheel based	*/
/*     on the parameters defined for the wheel.	 It should be noted	*/
/*     that all	angles when used with sin and cosine functions are	*/
/*     are in radians.							*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     HPS	 hPS; =	Presentation Space Handle			*/
/*     PCLRWHEEL pcw; =	Colour Wheel Data Pointer			*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     Nothing								*/
/*									*/
/* --------------------------------------------------------------------	*/

VOID DrawClrWheel(HPS hPS, PCLRWHEEL pcw)

{
ARCPARAMS ap;			   /* Arc Parameters			*/
LONG	  cIncrements;		   /* Increments Count			*/
LONG	  lSector;		   /* Sector				*/
PLONG	  plClr;		   /* RGB2 Colour Pointer		*/
POINTL	  ptl;			   /* Display Point			*/
POINTL	  ptlCurrent;		   /* Display Point			*/
RGB2	  rgb2;			   /* RGB2 Colour Holder		*/
double rdAngle;			   /* Angle				*/
register INT cAngle, cSaturation;  /* Loop Counter			*/
register INT i;			   /* Loop Counter			*/

		       /* Set the background of	the control to the	*/
		       /* dialogue background colour			*/

WinFillRect(hPS, &pcw->rcl, SYSCLR_FIELDBACKGROUND);

		       /* Set the colour table to RGB mode		*/

GpiCreateLogColorTable(hPS, pcw->ulOptions, LCOLF_RGB, 0L, 0L, (PLONG)NULL);

		       /* Point	to the RGB holder since	the colour	*/
		       /* setting function doesn't like the RGB2        */
		       /* structure being specified			*/

plClr =	(PLONG)(PVOID)&rgb2;

		       /* Initialize the reserved field	of the RGB2	*/
		       /* structure					*/
rgb2.fcOptions = 0;
		       /* Calculate the	shift value of the saturation	*/
		       /* increment					*/

cIncrements = 100L / pcw->lSaturationInc;

		       /* Moving through a full	circle,	create the	*/
		       /* colour wheel.	 The method used here is to	*/
		       /* move through each degree and display each	*/
		       /* saturation slice of the degree pie.  It	*/
		       /* should be noted that the saturaction moves	*/
		       /* from the outside of the circle to the	centre	*/
		       /* point	when drawing.				*/

for ( cAngle = 0; cAngle < 360;	cAngle += pcw->lAngle )
   {
		       /* Break	the wheel into 6 wedges	which		*/
		       /* corresponds to red, yellow, green, cyan, blue	*/
		       /* and magenta					*/

   lSector = cAngle / 60L;
   switch ( lSector )
       {
		       /* Red wedge					*/
       case 0 :
	   rgb2.bRed   = (BYTE)255;
	   break;
		       /* Yellow wedge					*/
       case 1 :
	   rgb2.bGreen = (BYTE)255;
	   break;
		       /* Green	wedge					*/
       case 2 :
	   rgb2.bGreen = (BYTE)255;
	   break;
		       /* Cyan wedge					*/
       case 3 :
	   rgb2.bBlue  = (BYTE)255;
	   break;
		       /* Blue wedge					*/
       case 4 :
	   rgb2.bBlue  = (BYTE)255;
	   break;
		       /* Magenta wedge					*/
       case 5 :
	   rgb2.bRed   = (BYTE)255;
	   break;
       }

   for ( i = 1,	cSaturation = pcw->lSaturationInc; i <=	cIncrements; cSaturation += pcw->lSaturationInc, i++ )
       {
		       /* Break	the wheel into 6 wedges	which		*/
		       /* corresponds to red, yellow, green, cyan, blue	*/
		       /* and magenta					*/

       switch (	lSector	)
	   {
		       /* Red wedge					*/
	   case	0 :
	       rgb2.bGreen = (BYTE)((((100 - ((cSaturation * (100 - ((cAngle % 60L) * 100L) / 60)) / 100L))) * 255L) / 100L);
	       rgb2.bBlue  = (BYTE)(((100 - cSaturation) * 255L) / 100L);
	       break;
		       /* Yellow wedge					*/
	   case	1 :
	       rgb2.bRed   = (BYTE)((((100 - ((cSaturation * ((cAngle %	60L) * 100L) / 60) / 100L))) * 255L) / 100L);
	       rgb2.bBlue  = (BYTE)(((100 - cSaturation) * 255L) / 100L);
	       break;
		       /* Green	wedge					*/
	   case	2 :
	       rgb2.bRed   = (BYTE)(((100 - cSaturation) * 255L) / 100L);
	       rgb2.bBlue  = (BYTE)((((100 - ((cSaturation * (100 - ((cAngle % 60L) * 100L) / 60)) / 100L))) * 255L) / 100L);
	       break;
		       /* Cyan wedge					*/
	   case	3 :
	       rgb2.bRed   = (BYTE)(((100 - cSaturation) * 255L) / 100L);
	       rgb2.bGreen = (BYTE)((((100 - ((cSaturation * ((cAngle %	60L) * 100L) / 60) / 100L))) * 255L) / 100L);
	       break;
		       /* Blue wedge					*/
	   case	4 :
	       rgb2.bRed   = (BYTE)((((100 - ((cSaturation * (100 - ((cAngle % 60L) * 100L) / 60)) / 100L))) * 255L) / 100L);
	       rgb2.bGreen = (BYTE)(((100 - cSaturation) * 255L) / 100L);
	       break;
		       /* Magenta wedge					*/
	   case	5 :
	       rgb2.bGreen = (BYTE)(((100 - cSaturation) * 255L) / 100L);
	       rgb2.bBlue  = (BYTE)((((100 - ((cSaturation * ((cAngle %	60L) * 100L) / 60) / 100L))) * 255L) / 100L);
	       break;
	   }
		       /* Set the colour for the wedge saturation being	*/
		       /* drawn						*/

       GpiSetColor(hPS,	*plClr);
			
		       /* The method that the wedge saturation is drawn	*/
		       /* is to	first draw the outer curve with	the	*/
		       /* lower	curve right corner being the line	*/
		       /* start.  The location where the upper curve	*/
		       /* ends is determined to	allow for proper area	*/
		       /* closure when the lower curve is drawn.  This	*/
		       /* lower	curve is drawn withou a	line, namely,	*/
		       /* the line start position is the same point as	*/
		       /* the curve start position.  When the arc has	*/
		       /* been drawn for the lower curve, a line from	*/
		       /* that ending point to upper curve ending point	*/
		       /* is drawn and the area	is closed thereby	*/
		       /* allowing the correct area fill in the	desired	*/
		       /* RGB colour.					*/

		       /* Start	the area braket				*/

       GpiBeginArea(hPS, BA_NOBOUNDARY | BA_ALTERNATE);

		       /* Set the arc radius to	the current saturation	*/
		       /* value						*/

       if ( i == cIncrements )
		       /* Set the arc radius to	the current saturation	*/
		       /* value						*/

	   pcw->ap.lP =	pcw->ap.lQ = pcw->lRadius;
       else
	   pcw->ap.lP =	pcw->ap.lQ = (i	* pcw->lSaturationInc *	pcw->lRadius) /	100L;
       GpiSetArcParams(hPS, &pcw->ap);

       if ( i == 1 )
	   {
	   GpiMove(hPS,	&pcw->ptlOrigin);

		       /* Draw the outside arc				*/

	   GpiPartialArc(hPS, &pcw->ptlOrigin, MAKEFIXED(1, 0),
			 MAKEFIXED(cAngle, 0), MAKEFIXED(pcw->lAngle, 0));

		       /* Draw the closure line	from the ending	point	*/
		       /* of the inner arc to the ending point of the	*/
		       /* outer	arc and	close the area braket forcing	*/
		       /* the colour fill of the area drawn in the	*/
		       /* saturation colour.				*/

	   GpiLine(hPS,	&pcw->ptlOrigin);
	   ap =	pcw->ap;
	   }
       else
	   {
	   ptl.x = (LONG)((double)ap.lQ	* cos(rdAngle =	(double)cAngle / 57.29577951)) + pcw->ptlOrigin.x;
	   ptl.y = (LONG)((double)ap.lQ	* sin(rdAngle))	+ pcw->ptlOrigin.y;

	   GpiMove(hPS,	&ptl);

		       /* Draw the outside arc				*/

	   GpiPartialArc(hPS, &pcw->ptlOrigin, MAKEFIXED(1, 0),
			 MAKEFIXED(cAngle, 0), MAKEFIXED(pcw->lAngle, 0));

		       /* Get the ending location of arc to allow for	*/
		       /* later	closure	of the area			*/

	   GpiQueryCurrentPosition(hPS,	&ptlCurrent);

		       /* Set the inner	arc radius and move back to the	*/
		       /* calculated starting location			*/

	   GpiSetArcParams(hPS,	&ap);
	   GpiMove(hPS,	&ptl);

		       /* Draw the inner arc				*/

	   GpiPartialArc(hPS, &pcw->ptlOrigin, MAKEFIXED(1, 0),
			 MAKEFIXED(cAngle, 0), MAKEFIXED(pcw->lAngle, 0));

		       /* Draw the closure line	from the ending	point	*/
		       /* of the inner arc to the ending point of the	*/
		       /* outer	arc and	close the area braket forcing	*/
		       /* the colour fill of the area drawn in the	*/
		       /* saturation colour.				*/

	   GpiLine(hPS,	&ptlCurrent);
	   ap =	pcw->ap;
	   }
      GpiEndArea(hPS);
      }
   if (	pcw->tid && GpiQueryStopDraw(hPS) )
       break;
   }
}
#pragma	subtitle("   Colour Wheel DLL - Control Window Procedure")
#pragma	page( )

/* --- BitmapConstructThread --------------------------	[ Private ] ---	*/
/*									*/
/*     This function is	used to	process	the messages for the Colour	*/
/*     Wheel control window.  It should	be noted that all angles	*/
/*     when used with sin and cosine functions are in radians.		*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     PCLRWHEEL pcw; =	Thread Parameter: Colour Internal Data Pointer	*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     Nothing								*/
/*									*/
/* --------------------------------------------------------------------	*/

VOID _System BitmapConstructThread(PCLRWHEEL pcw)

{
HAB habThread;			   /* Thread Anchor Block		*/

		       /* Register the thread with OS/2	PM		*/

habThread = WinInitialize(0);

		       /* Create the bitmap using the passed parameter	*/
		       /* to the thread	as the address to the defined	*/
		       /* colour wheel data				*/

if ( (pcw->hbm = hbmCreateBitmap(pcw)) != (HBITMAP)NULL	)
   {
   pcw->hbmConstruct = 0L;

		       /* Clear	the thread ID to allow the allow	*/
		       /* routines to become active.  The thread ID is	*/
		       /* used as a form of semaphore.			*/
   pcw->tid = 0L;

		       /* Bitmap successfully created, force the	*/
		       /* painting of the wheel	within the control	*/
		       /* rectangle					*/

   WinInvalidateRect(pcw->hWnd,	NULL, FALSE);
   }
else
		       /* Clear	the thread ID to allow the allow	*/
		       /* routines to become active.  The thread ID is	*/
		       /* used as a form of semaphore.			*/
   pcw->tid = 0L;
		       /* De-register the thread with OS/2 PM since the	*/
		       /* creation of the bitmap is complete		*/
WinTerminate(habThread);
		       /* Exit the thread				*/
DosExit(EXIT_THREAD, 0L);
}
#pragma	subtitle("   Colour Wheel DLL - Control Window Procedure")
#pragma	page( )

/* --- ClrWheelWndProc ------------------------------------------------	*/
/*									*/
/*     This function is	used to	process	the messages for the Colour	*/
/*     Wheel control window.  It should	be noted that all angles	*/
/*     when used with sin and cosine functions are in radians.		*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     HWND   hWnd; = Window Handle					*/
/*     ULONG  msg;  = PM Message					*/
/*     MPARAM mp1;  = Message Parameter	1				*/
/*     MPARAM mp2;  = Message Parameter	2				*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     ClrWheelWndProc = Message Handling Result			*/
/*									*/
/* --------------------------------------------------------------------	*/

MRESULT	EXPENTRY ClrWheelWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)

{
BOOL	      fReturn;		   /* Message Return Flag		*/
HPS	      hPS;		   /* Presentation Space Handle		*/
LONG	      lMax;		   /* Maximum Value			*/
LONG	      lMin;		   /* Minimum Value			*/
LONG	      lAngle;		   /* Angle Holder			*/
LONG	      lRadius;		   /* Radius Holder			*/
PCLRWHEEL     pcw;		   /* Colour Wheel Internal Data Pointer*/
PCLRWHLCDATA  pcwd;		   /* Colour Wheel Data	Pointer		*/
PCREATESTRUCT pcrst;		   /* Create Structure Pointer		*/
POINTL	      ptl;		   /* Display Point			*/
RECTL	      rcl;		   /* Client Window Rectangle		*/
PRGB2	      prgb2;		   /* RGB2 Structure Pointer		*/
double	      rdAngle;		   /* Angle				*/
register INT x,	y;		   /* Loop Counter			*/

switch ( msg )
   {

/************************************************************************/
/************************************************************************/
/*									*/
/* Part	1: Control creation coding					*/
/*									*/
/************************************************************************/
/************************************************************************/

   /*********************************************************************/
   /*  Control creation							*/
   /*********************************************************************/

   case	WM_CREATE :
		       /* Get the address of the CTLDATA structure that	*/
		       /* may contain the bitmap information that the	*/
		       /* control can use during its creation instead	*/
		       /* of using messages to set the button images	*/

       if ( (pcwd = (PCLRWHLCDATA)PVOIDFROMMP(mp1)) != NULL )

		       /* Check	to see that the	structure passed is	*/
		       /* what is expected and if not, return		*/
		       /* indicating that the control window should	*/
		       /* not be further created			*/

		       /*************************************************/
		       /*  NOTE:   OS/2	2.0 requires the first element	*/
		       /*	   of the CTLDATA structure to be the	*/
		       /*	   size	of the structure and this	*/
		       /*	   value must be less than 64 KB	*/
		       /*************************************************/

	   if (	(pcwd->cb != sizeof(CLRWHLCDATA)) )
	       return(MRFROMLONG(TRUE));

		       /* Allocate memory for internal control data	*/

       DosAllocMem((PPVOID)(PVOID)&pcw,	sizeof(CLRWHEEL),
		   PAG_READ | PAG_WRITE	| PAG_COMMIT);

		       /* Save the address of the internal control data	*/
		       /* in the control's reserved memory to allow it  */
		       /* to be	referenced as required by the control	*/

       WinSetWindowPtr(hWnd, QWW_CDATA,	(PVOID)pcw);

		       /* Get the control's creation structure address  */
		       /* to copy the relevant information such	as the	*/
		       /* size,	position and text of the control into	*/
		       /* the internal control data			*/

       pcrst = (PCREATESTRUCT)PVOIDFROMMP(mp2);

		       /* Save the owner and parent of the control so	*/
		       /* notification messages	can be sent back to	*/
		       /* the proper locations within the owning	*/
		       /* application					*/

       pcw->hWnd       = hWnd;
       pcw->hwndOwner  = pcrst->hwndOwner;
       pcw->hwndParent = pcrst->hwndParent;

		       /* Save the ID of the control, style and	save	*/
		       /* the default style along with the normal	*/
		       /* arrow	pointer	handle which will be used when	*/
		       /* the pointer passes over the control		*/

       pcw->id	       = pcrst->id;
       pcw->flStyle    = pcrst->flStyle;
       pcw->hptrArrow  = WinQuerySysPointer(HWND_DESKTOP, SPTR_ARROW,
					    FALSE);

		       /* Check	to see if the style for	the control	*/
		       /* indicates that pure colours (solid) should	*/
		       /* be used					*/

       if ( pcrst->flStyle & CWS_SOLIDCLR )
	   pcw->ulOptions = LCOL_PURECOLOR;

		       /* Save the initial size	of the control		*/

       pcw->rcl.xRight	= pcrst->cx;
       pcw->rcl.yTop	= pcrst->cy;
       pcw->cx		= pcrst->cx;
       pcw->cy		= pcrst->cy;

		       /* Save the co-ordinates	of the rectangle for	*/
		       /* the bitmap display if	it is required		*/

       pcw->aptl[1].x =	pcw->aptl[3].x = pcrst->cx - 1L;
       pcw->aptl[1].y =	pcw->aptl[3].y = pcrst->cy - 1L;

		       /* Calculate the	origin point for the circle	*/

       pcw->ptlOrigin.x	= (pcrst->cx - 2L) / 2L;
       pcw->ptlOrigin.y	= (pcrst->cy - 2L) / 2L;

       pcw->aptlXHair[0].x = 0L;
       pcw->aptlXHair[0].y = pcw->ptlOrigin.y;
       pcw->aptlXHair[1].x = pcrst->cx - 1L;
       pcw->aptlXHair[1].y = pcw->ptlOrigin.y;
       pcw->aptlXHair[2].x = pcw->ptlOrigin.x;
       pcw->aptlXHair[2].y = 0L;
       pcw->aptlXHair[3].x = pcw->ptlOrigin.x;
       pcw->aptlXHair[3].y = pcw->cy - 1L;

		       /* Determine if CTLDATA was specified for the	*/
		       /* control and if the case, get the data	and	*/
		       /* set up the control internal values		*/
       if ( pcwd )
	   {
		       /* Determine if the angle is within the defined	*/
		       /* range	for the	colour wheel			*/

	   if (	(pcwd->lAngle <	1L) || (pcwd->lAngle > 45L)  )

		       /* Specified angle for the wedges is outside	*/
		       /* the defined limits for the colour wheel, set	*/
		       /* the angle to the default value of 10ø		*/

	       pcw->lAngle = 10L;
	   else
		       /* Valid	value specified, save the angle		*/
		       /* within the internal data structure		*/

	       pcw->lAngle = pcwd->lAngle;

		       /* Determine if the saturation increment	outside	*/
		       /* the defined limits for the colour wheel	*/

	   if (	(pcwd->lSaturationInc <	1L) || (pcwd->lSaturationInc > 25L) )

		       /* Specified saturation increment outside the	*/
		       /* defined limits for the colour	wheel, set the	*/
		       /* saturation increment to 10			*/

	       pcw->lSaturationInc = 10L;
	   else
		       /* Valid	value specified, save the saturation	*/
		       /* within the internal data structure		*/

	       pcw->lSaturationInc = pcwd->lSaturationInc;

		       /* Determine if the radius specified is within	*/
		       /* the limits of	the control size and is	not	*/
		       /* negative					*/

	   if (	((pcwd->lRadius	< 1L) || (pcwd->lRadius	> pcrst->cx) ||
		(pcwd->lRadius > pcrst->cy)) ||	(pcw->flStyle &	CWS_AUTOSIZE) )

		       /* Invalid size given or	autosizing requested,	*/
		       /* form the radius for the wheel			*/

	       if ( pcrst->cx <	pcrst->cy )
		   pcw->lRadius	= (pcrst->cx - 2L) / 2L;
	       else
		   pcw->lRadius	= (pcrst->cy - 2L) / 2L;
	   else
		       /* Valid	radius given, save the value internally	*/

	       pcw->lRadius = pcwd->lRadius;
	   }
       else
	   {
		       /* No CTLDATA specified,	use default angle and	*/
		       /* saturation.  For the radius, use the best	*/
		       /* fit for the width or height			*/

	   pcw->lAngle	       = 10L;
	   pcw->lSaturationInc = 10L;
	   if (	pcrst->cx < pcrst->cy )
	       pcw->lRadius = pcw->ptlOrigin.x;
	   else
	       pcw->lRadius = pcw->ptlOrigin.y;
	   }
       if ( pcw->flStyle & CWS_BITMAP )
	   if (	pcw->flStyle & CWS_THREADED )
	      DosCreateThread(&pcw->tid, (PFNTHREAD)BitmapConstructThread,
			      (ULONG)pcw, 2UL, 8192);
	   else
	       pcw->hbm	= hbmCreateBitmap(pcw);
       break;

/************************************************************************/
/************************************************************************/
/*									*/
/* Part	2: Control sizing						*/
/*									*/
/************************************************************************/
/************************************************************************/

   /*********************************************************************/
   /*  Control changing	size						*/
   /*********************************************************************/

   case	WM_SIZE	:
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Save the new sizes within the	control	info	*/

       pcw->rcl.xRight	= SHORT1FROMMP(mp2);
       pcw->rcl.yTop	= SHORT2FROMMP(mp2);
       pcw->cx		= SHORT1FROMMP(mp2);
       pcw->cy		= SHORT2FROMMP(mp2);
       pcw->ptlOrigin.x	= SHORT1FROMMP(mp2) / 2L;
       pcw->ptlOrigin.y	= SHORT2FROMMP(mp2) / 2L;

       pcw->aptl[1].x =	pcw->aptl[3].x = SHORT1FROMMP(mp2) - 1L;
       pcw->aptl[1].y =	pcw->aptl[3].y = SHORT2FROMMP(mp2) - 1L;
       if ( pcw->flStyle & CWS_AUTOSIZE	)
	   if (	pcw->cx	< pcw->cy )
	       pcw->lRadius = pcw->ptlOrigin.x;
	   else
	       pcw->lRadius = pcw->ptlOrigin.y;

       if ( pcw->flStyle & CWS_BITMAP )
	   if (	pcw->flStyle & CWS_THREADED )
	       {
	       if ( pcw->tid )
		   {
		   if (	pcw->hpsBitmap )
		       GpiSetStopDraw(pcw->hpsBitmap, SDW_ON);
		   DosKillThread(pcw->tid);
		   }
	       DosCreateThread(&pcw->tid, (PFNTHREAD)BitmapConstructThread,
			       (ULONG)pcw, 2UL,	8192UL);
	       }
	   else
	       pcw->hbm	= hbmCreateBitmap(pcw);
       break;

/************************************************************************/
/************************************************************************/
/*									*/
/* Part	3: Mouse input interface					*/
/*									*/
/************************************************************************/
/************************************************************************/

   /*********************************************************************/
   /*  Mouse Button 1 selection						*/
   /*********************************************************************/

   case	WM_BUTTON1DOWN :
		       /* Get the current mouse	pointer	position and	*/
		       /* place	within the test	point structure		*/

       ptl.x = SHORT1FROMMP(mp1);
       ptl.y = SHORT2FROMMP(mp1);

		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Check	to see if the colour wheel is a		*/
		       /* threaded bitmap version, in which case differ	*/
		       /* any mouse selections until after the bitmap	*/
		       /* has been fully created.  The thread ID is	*/
		       /* used as an internal semaphore			*/
       if ( pcw->tid )
	   {
	   WinAlarm(HWND_DESKTOP, WA_NOTE);
	   break;
	   }
		       /* Set up the arc parameters			*/

       pcw->ap.lP = pcw->ap.lQ = pcw->lRadius;
       GpiSetArcParams(hPS = WinGetPS(hWnd), &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

       GpiBeginPath(hPS, 1L);
       GpiMove(hPS, &pcw->ptlOrigin);
       GpiFullArc(hPS, DRO_OUTLINE, MAKEFIXED(1, 0));
       GpiEndPath(hPS);
		       /* Circle complete, convert the normal path to a	*/
		       /* clip path to allow the determination of the	*/
		       /* mouse	button click within the	colour wheel or	*/
		       /* outside the edge of the wheel	but still	*/
		       /* within the limits of the control itself	*/

       GpiSetClipPath(hPS, 1L, SCP_ALTERNATE | SCP_AND);

		       /* Check	to see if the mouse pointer button	*/
		       /* click	within the confines of the colour	*/
		       /* wheel						*/

       if ( GpiPtVisible(hPS, &ptl) == PVIS_VISIBLE )
	   {
		       /* Mouse	click within the colour	wheel, capture	*/
		       /* the mouse for	this control only until	the	*/
		       /* button 1 of the mouse	is released		*/

	   WinSetCapture(HWND_DESKTOP, hWnd);
	   pcw->fCapture = TRUE;

		       /* Send notification message to control owner	*/
		       /* that cross hair has been selected		*/
		       /*						*/
		       /* Notification:	CWN_XHAIRSELECTED		*/
		       /* mp2:		N/A				*/

	   WinSendMsg(pcw->hwndOwner, WM_CONTROL,
		      MPFROM2SHORT(pcw->id, CWN_XHAIRSELECTED),	0L);

		       /* Set the mix mode for line invert		*/

	   GpiSetMix(hPS, FM_INVERT);

		       /* Erase	the current cross hair by drawing it	*/
		       /* again	which will cause the previous colours	*/
		       /* to be	displayed since	it is being drawn in	*/
		       /* invert mix mode				*/

	   GpiMove(hPS,	pcw->aptlXHair);
	   GpiLine(hPS,	&pcw->aptlXHair[1]);

	   GpiMove(hPS,	&pcw->aptlXHair[2]);
	   GpiLine(hPS,	&pcw->aptlXHair[3]);

		       /* Save the new position	of the mouse pointer	*/
		       /* within the cross hair	point array		*/

	   pcw->aptlXHair[0].y =
	   pcw->aptlXHair[1].y = SHORT2FROMMP(mp1);
	   pcw->aptlXHair[2].x =
	   pcw->aptlXHair[3].x = SHORT1FROMMP(mp1);

		       /* Draw the new cross hair within the invert	*/
		       /* mix mode					*/

	   GpiMove(hPS,	pcw->aptlXHair);
	   GpiLine(hPS,	&pcw->aptlXHair[1]);

	   GpiMove(hPS,	&pcw->aptlXHair[2]);
	   GpiLine(hPS,	&pcw->aptlXHair[3]);
	   }
		       /* Release the controls presentation space	*/
       WinReleasePS(hPS);
       break;

   /*********************************************************************/
   /*  Mouse Button 1 release						*/
   /*********************************************************************/

   case	WM_BUTTON1UP :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Determine if the control in capture mode in	*/
		       /* case,	the current position of	the mouse	*/
		       /* pointer needs	to be checked and the colour	*/
		       /* determined					*/

       if ( pcw->fCapture )
	   {
		       /* Release the mouse capture to allow other	*/
		       /* windows the ability of receiving mouse input	*/

	   WinSetCapture(HWND_DESKTOP, (HWND)NULL);
	   pcw->fCapture = FALSE;

		       /* Send notification message to control owner	*/
		       /* that cross hair has been released		*/
		       /*						*/
		       /* Notification:	CWN_XHAIRRELEASED		*/
		       /* mp2:		N/A				*/

	   WinSendMsg(pcw->hwndOwner, WM_CONTROL,
		      MPFROM2SHORT(pcw->id, CWN_XHAIRRELEASED),	0L);

		       /* Check	to see if notification of RGB or HSB	*/
		       /* colour selected required			*/

	   if (	pcw->flStyle & (CWS_RGB	| CWS_HSB) )
	       {
		       /* Check	to see if the mouse pointer at the	*/
		       /* origin of the	colour wheel in	the horizontal	*/
		       /* direction					*/

	       if ( pcw->aptlXHair[2].x	== pcw->ptlOrigin.x )

		       /* Mouse	pointer	aligning horizontally at zero	*/
		       /* in the colour	wheel.	Need to	specify	in	*/
		       /* radians the angle in which the cross is	*/
		       /* resting.  Check to see if the	y position	*/
		       /* positive which indicates the angle is	90ø	*/
		       /* otherwise it is 270ø.				*/

		   if (	(lRadius = pcw->aptlXHair[0].y - pcw->ptlOrigin.y) < 0 )
		       rdAngle = 4.712388980;
		   else
		       rdAngle = 1.570796327;
	       else
		       /* Check	to see if the mouse pointer at the	*/
		       /* origin of the	colour wheel in	the vertical	*/
		       /* direction					*/

		   if (	pcw->aptlXHair[0].y == pcw->ptlOrigin.y	)

		       /* Mouse	pointer	aligning horizontally at zero	*/
		       /* in the colour	wheel.	Need to	specify	in	*/
		       /* radians the angle in which the cross is	*/
		       /* resting.  Check to see if the	y position	*/
		       /* positive which indicates the angle is	0ø	*/
		       /* otherwise it is 180ø.				*/

		       if ( (lRadius = pcw->aptlXHair[2].x - pcw->ptlOrigin.x) < 0 )
			   rdAngle = 3.141592654;
		       else
			   rdAngle = 0.0;
		   else
		       {
		       /* Determine the	absolute x and y co-ordinates	*/
		       /* based	upon cartesian system.	All trig	*/
		       /* functions are	within the 0 to	90ø range.	*/

		       if ( pcw->aptlXHair[0].y	< pcw->ptlOrigin.y )
			   y = pcw->ptlOrigin.y	- pcw->aptlXHair[0].y;
		       else
			   y = pcw->aptlXHair[0].y - pcw->ptlOrigin.y;

		       if ( pcw->aptlXHair[2].x	< pcw->ptlOrigin.x )
			   x = pcw->ptlOrigin.x	- pcw->aptlXHair[2].x;
		       else
			   x = pcw->aptlXHair[2].x - pcw->ptlOrigin.x;

		       /* Calculate the	radius from the	x and y		*/
		       /* position of the mouse	using trig and right	*/
		       /* angle	geometry				*/

		       lRadius = (LONG)((double)y / sin(rdAngle	= atan2((double)y, (double)x)));

		       /* Determine the	quadrant that the mouse	pointer	*/
		       /* was in to be able to form correct angle	*/

		       if ( pcw->aptlXHair[0].y	< pcw->ptlOrigin.y )
			   if (	pcw->aptlXHair[2].x > pcw->ptlOrigin.x )
			       rdAngle = 6.283185307 - rdAngle;
			   else
			       rdAngle += 3.141592654;
		       else
			   if (	pcw->aptlXHair[2].x < pcw->ptlOrigin.x )
			       rdAngle = 3.141592654 - rdAngle;
		       }
		       /* When the style of the	control	requests RGB	*/
		       /* notification,	convert	the hue	and saturation	*/
		       /* (angle and radius) to	RGB and	create a	*/
		       /* notification message package that is sent	*/
		       /* back to the owner.				*/
		       /*						*/
		       /* Notification:	CWN_RGBCLRSELECTED		*/
		       /* mp2:		RGB2 value			*/

	       if ( pcw->flStyle & CWS_RGB )
		   WinSendMsg(pcw->hwndOwner, WM_CONTROL,
			      MPFROM2SHORT(pcw->id, CWN_RGBCLRSELECTED),
			      MPFROMLONG(lHSBtoRGB((LONG)((rdAngle * 360.0) / 6.283185307),
					 lRadius * 100 / pcw->lRadius)));

		       /* When the style of the	control	requests HSB	*/
		       /* notification,	create a notification message	*/
		       /* package that is sent back to the owner.	*/
		       /*						*/
		       /* Notification:	CWN_HSBCLRSELECTED		*/
		       /* mp2:		Low Short = Hue	(Angle)		*/
		       /*		Hi  Short = Saturation (Radius)	*/

	       if ( pcw->flStyle & CWS_HSB )
		   WinSendMsg(pcw->hwndOwner, WM_CONTROL,
			      MPFROM2SHORT(pcw->id, CWN_HSBCLRSELECTED),
			      MPFROMLONG(MAKELONG((LONG)rdAngle, lRadius * 100 / pcw->lRadius)));
	       }
	   }
       break;

   /*********************************************************************/
   /*  Mouse moving							*/
   /*********************************************************************/

   case	WM_MOUSEMOVE :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Check	to see if in capture mode and if the	*/
		       /* case,	need to	move the cross hair that is	*/
		       /* used to denote the current colour selected	*/

       if ( pcw->fCapture )
	   {
		       /* Get the current mouse	pointer	position and	*/
		       /* place	within the test	point structure		*/

	   ptl.x = SHORT1FROMMP(mp1);
	   ptl.y = SHORT2FROMMP(mp1);

		       /* Set up the arc parameters			*/

	   pcw->ap.lP =	pcw->ap.lQ = pcw->lRadius;
	   GpiSetArcParams(hPS = WinGetPS(hWnd), &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

	   GpiBeginPath(hPS, 1L);
	   GpiMove(hPS,	&pcw->ptlOrigin);
	   GpiFullArc(hPS, DRO_OUTLINE,	MAKEFIXED(1, 0));
	   GpiEndPath(hPS);

		       /* Circle complete, convert the normal path to a	*/
		       /* clip path to allow the determination of the	*/
		       /* mouse	button click within the	colour wheel or	*/
		       /* outside the edge of the wheel	but still	*/
		       /* within the limits of the control itself	*/

	   GpiSetClipPath(hPS, 1L, SCP_ALTERNATE | SCP_AND);

		       /* Check	to see if the mouse pointer button	*/
		       /* click	within the confines of the colour	*/
		       /* wheel						*/

	   if (	GpiPtVisible(hPS, &ptl)	== PVIS_VISIBLE	)
	       {
		       /* Set the mix mode for line invert		*/

	       GpiSetMix(hPS, FM_INVERT);

		       /* Erase	the current cross hair by drawing it	*/
		       /* again	which will cause the previous colours	*/
		       /* to be	displayed since	it is being drawn in	*/
		       /* invert mix mode				*/

	       GpiMove(hPS, pcw->aptlXHair);
	       GpiLine(hPS, &pcw->aptlXHair[1]);

	       GpiMove(hPS, &pcw->aptlXHair[2]);
	       GpiLine(hPS, &pcw->aptlXHair[3]);

		       /* Save the new position	of the mouse pointer	*/
		       /* within the cross hair	point array		*/

	       pcw->aptlXHair[0].y =
	       pcw->aptlXHair[1].y = SHORT2FROMMP(mp1);
	       pcw->aptlXHair[2].x =
	       pcw->aptlXHair[3].x = SHORT1FROMMP(mp1);

		       /* Draw the new cross hair within the invert	*/
		       /* mix mode					*/

	       GpiMove(hPS, pcw->aptlXHair);
	       GpiLine(hPS, &pcw->aptlXHair[1]);

	       GpiMove(hPS, &pcw->aptlXHair[2]);
	       GpiLine(hPS, &pcw->aptlXHair[3]);
	       }
		       /* Release the controls presentation space	*/

	   WinReleasePS(hPS);
	   }
		       /* Set the mouse	pointer	to the arrow shape	*/
		       /* while	the mouse within the control		*/

       WinSetPointer(HWND_DESKTOP, pcw->hptrArrow);
       break;

/************************************************************************/
/************************************************************************/
/*									*/
/* Part	4: Painting and	display						*/
/*									*/
/************************************************************************/
/************************************************************************/

   /*********************************************************************/
   /*  Erase control background						*/
   /*********************************************************************/

   case	WM_ERASEBACKGROUND :
       WinQueryWindowRect(hWnd,	&rcl);
       WinFillRect((HPS)mp1, &rcl, SYSCLR_FIELDBACKGROUND);
       break;

   /*********************************************************************/
   /*  Paint control							*/
   /*********************************************************************/

   case	WM_PAINT :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

       hPS = WinBeginPaint(hWnd, (HPS)NULL, (PRECTL)NULL);

		       /* Check	to see if the control is threaded and	*/
		       /* has a	bitmap display				*/

       if ( (pcw->flStyle & (CWS_BITMAP	| CWS_THREADED)) == (CWS_BITMAP	| CWS_THREADED)	)

		       /* Threaded version of the control being	used,	*/
		       /* check	to make	sure that the thread is	active	*/
		       /* which	indicates that the bitmap is still	*/
		       /* being	constructed and	that the outline of the	*/
		       /* should only be shown.	 This acts like	a	*/
		       /* prevent semaphore except that	no blocking on	*/
		       /* the semaphore	occurs as this would block the	*/
		       /* rest of the users PM interface.		*/
	   if (	pcw->tid )
	       {
		       /* Set up the arc parameters			*/

	       pcw->ap.lP = pcw->ap.lQ = pcw->lRadius;
	       GpiSetArcParams(hPS, &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

	       GpiMove(hPS, &pcw->ptlOrigin);
	       GpiFullArc(hPS, DRO_OUTLINE, MAKEFIXED(1, 0));
	       WinEndPaint(hPS);
	       break;
	       }
		       /* Check	to see if the bitmap is	ready.	This	*/
		       /* will be indicated by the handle being		*/
		       /* present somewhat like	the prescence of the	*/
		       /* thread ID when the bitmap is being drawn.	*/
       if ( pcw->hbm )
	   GpiWCBitBlt(hPS, pcw->hbm, 4L, pcw->aptl, ROP_SRCCOPY, BBO_OR);
       else
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

	   DrawClrWheel(hPS, pcw);

		       /* Set up the arc parameters for	the clipping	*/
		       /* path						*/

       pcw->ap.lP = pcw->ap.lQ = pcw->lRadius;
       GpiSetArcParams(hPS, &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

       GpiBeginPath(hPS, 1L);
       GpiMove(hPS, &pcw->ptlOrigin);
       GpiFullArc(hPS, DRO_OUTLINE, MAKEFIXED(1, 0));
       GpiEndPath(hPS);
		       /* Circle complete, convert the normal path to a	*/
		       /* clip path to allow the determination of the	*/
		       /* mouse	button click within the	colour wheel or	*/
		       /* outside the edge of the wheel	but still	*/
		       /* within the limits of the control itself	*/

       GpiSetClipPath(hPS, 1L, SCP_ALTERNATE | SCP_AND);

		       /* Set the mix mode for line invert		*/

       GpiSetMix(hPS, FM_INVERT);

		       /* Draw the new cross hair within the invert	*/
		       /* mix mode					*/

       GpiMove(hPS, pcw->aptlXHair);
       GpiLine(hPS, &pcw->aptlXHair[1]);

       GpiMove(hPS, &pcw->aptlXHair[2]);
       GpiLine(hPS, &pcw->aptlXHair[3]);
       WinEndPaint(hPS);
       break;

/************************************************************************/
/************************************************************************/
/*									*/
/* Part	5: Control defined message handling				*/
/*									*/
/************************************************************************/
/************************************************************************/

   /*********************************************************************/
   /*  Cross-hair position query					*/
   /*********************************************************************/
   /*********************************************************************/
   /*									*/
   /*  Message:								*/
   /*	       CWM_QUERYXHAIR						*/
   /*									*/
   /*  Input:								*/
   /*	       mp1 = 0L;						*/
   /*	       mp2 = 0L;						*/
   /*									*/
   /*  Return:								*/
   /*	       x = SHORT1FROMMR(mr);					*/
   /*	       y = SHORT2FROMMR(mr);					*/
   /*									*/
   /*********************************************************************/

   case	CWM_QUERYXHAIR :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Return the cross position with the x in the	*/
		       /* lower	portion	and the	y in the upper portion	*/
		       /* of the return	value				*/

       return(MRFROMLONG(MAKELONG((USHORT)pcw->aptlXHair[2].x, (USHORT)pcw->aptlXHair[0].y)));

   /*********************************************************************/
   /*  Cross-hair position set						*/
   /*********************************************************************/
   /*********************************************************************/
   /*									*/
   /*  Message:								*/
   /*	       CWM_SETXHAIR						*/
   /*									*/
   /*  Input:								*/
   /*	       mp1 = x;							*/
   /*	       mp2 = y;							*/
   /*									*/
   /*  Return:								*/
   /*	       TRUE  : position	accepted and cross hair	updated		*/
   /*	       FALSE : invalid position	specified, cross hair not	*/
   /*		       updated						*/
   /*									*/
   /*********************************************************************/

   case	CWM_SETXHAIR :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Get the position and place within the	test	*/
		       /* point	structure				*/

       ptl.x = (LONG)LONGFROMMP(mp1);
       ptl.y = (LONG)LONGFROMMP(mp2);

		       /* Set up the arc parameters			*/

       pcw->ap.lP = pcw->ap.lQ = pcw->lRadius;
       GpiSetArcParams(hPS = WinGetPS(hWnd), &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

       GpiBeginPath(hPS, 1L);
       GpiMove(hPS, &pcw->ptlOrigin);
       GpiFullArc(hPS, DRO_OUTLINE, MAKEFIXED(1, 0));
       GpiEndPath(hPS);
		       /* Circle complete, convert the normal path to a	*/
		       /* clip path to allow the determination of the	*/
		       /* mouse	button click within the	colour wheel or	*/
		       /* outside the edge of the wheel	but still	*/
		       /* within the limits of the control itself	*/

       GpiSetClipPath(hPS, 1L, SCP_ALTERNATE | SCP_AND);

		       /* Check	to see if the mouse pointer button	*/
		       /* click	within the confines of the colour	*/
		       /* wheel						*/

       if ( GpiPtVisible(hPS, &ptl) == PVIS_VISIBLE )
	   {
		       /* Set the mix mode for line invert		*/

	   GpiSetMix(hPS, FM_INVERT);

		       /* Erase	the current cross hair by drawing it	*/
		       /* again	which will cause the previous colours	*/
		       /* to be	displayed since	it is being drawn in	*/
		       /* invert mix mode				*/

	   GpiMove(hPS,	pcw->aptlXHair);
	   GpiLine(hPS,	&pcw->aptlXHair[1]);

	   GpiMove(hPS,	&pcw->aptlXHair[2]);
	   GpiLine(hPS,	&pcw->aptlXHair[3]);

		       /* Save the new position	of the mouse pointer	*/
		       /* within the cross hair	point array		*/

	   pcw->aptlXHair[0].y =
	   pcw->aptlXHair[1].y = (LONG)LONGFROMMP(mp2);
	   pcw->aptlXHair[2].x =
	   pcw->aptlXHair[3].x = (LONG)LONGFROMMP(mp1);

		       /* Draw the new cross hair within the invert	*/
		       /* mix mode					*/

	   GpiMove(hPS,	pcw->aptlXHair);
	   GpiLine(hPS,	&pcw->aptlXHair[1]);

	   GpiMove(hPS,	&pcw->aptlXHair[2]);
	   GpiLine(hPS,	&pcw->aptlXHair[3]);
	   fReturn = TRUE;
	   }
       else
	   fReturn = FALSE;

		       /* Release the controls presentation space	*/
       WinReleasePS(hPS);
       return(MRFROMLONG(fReturn));

   /*********************************************************************/
   /*  RGB colour query							*/
   /*********************************************************************/
   /*********************************************************************/
   /*									*/
   /*  Message:								*/
   /*	       CWM_QUERYRGBCLR						*/
   /*									*/
   /*  Input:								*/
   /*	       mp1 = 0L;						*/
   /*	       mp2 = 0L;						*/
   /*									*/
   /*  Return:								*/
   /*	       RGB2 rgb2 = LONGFROMMR(mr);				*/
   /*									*/
   /*********************************************************************/

   case	CWM_QUERYRGBCLR	:
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Check	to see if the mouse pointer at the	*/
		       /* origin of the	colour wheel in	the horizontal	*/
		       /* direction					*/

       if ( pcw->aptlXHair[2].x	== pcw->ptlOrigin.x )

		       /* Mouse	pointer	aligning horizontally at zero	*/
		       /* in the colour	wheel.	Need to	specify	in	*/
		       /* radians the angle in which the cross is	*/
		       /* resting.  Check to see if the	y position	*/
		       /* positive which indicates the angle is	90ø	*/
		       /* otherwise it is 270ø.				*/

	       if ( (lRadius = pcw->aptlXHair[0].y - pcw->ptlOrigin.y) < 0 )
		   rdAngle = 4.712388980;
	       else
		   rdAngle = 1.570796327;
	   else
		       /* Check	to see if the mouse pointer at the	*/
		       /* origin of the	colour wheel in	the vertical	*/
		       /* direction					*/

	       if ( pcw->aptlXHair[0].y	== pcw->ptlOrigin.y )

		       /* Mouse	pointer	aligning horizontally at zero	*/
		       /* in the colour	wheel.	Need to	specify	in	*/
		       /* radians the angle in which the cross is	*/
		       /* resting.  Check to see if the	y position	*/
		       /* positive which indicates the angle is	0ø	*/
		       /* otherwise it is 180ø.				*/

		   if (	(lRadius = pcw->aptlXHair[2].x - pcw->ptlOrigin.x) < 0 )
		       rdAngle = 3.141592654;
		   else
		       rdAngle = 0.0;
	       else
		   {
		       /* Determine the	absolute x and y co-ordinates	*/
		       /* based	upon cartesian system.	All trig	*/
		       /* functions are	within the 0 to	90ø range.	*/

		   if (	pcw->aptlXHair[0].y < pcw->ptlOrigin.y )
		       y = pcw->ptlOrigin.y - pcw->aptlXHair[0].y;
		   else
		       y = pcw->aptlXHair[0].y - pcw->ptlOrigin.y;

		   if (	pcw->aptlXHair[2].x < pcw->ptlOrigin.x )
		       x = pcw->ptlOrigin.x - pcw->aptlXHair[2].x;
		   else
		       x = pcw->aptlXHair[2].x - pcw->ptlOrigin.x;

		       /* Calculate the	radius from the	x and y		*/
		       /* position of the mouse	using trig and right	*/
		       /* angle	geometry				*/

		   lRadius = (LONG)((double)y /	sin(rdAngle = atan2((double)y, (double)x)));

		       /* Determine the	quadrant that the mouse	pointer	*/
		       /* was in to be able to form correct angle	*/

		   if (	pcw->aptlXHair[0].y < pcw->ptlOrigin.y )
		       if ( pcw->aptlXHair[2].x	> pcw->ptlOrigin.x )
			   rdAngle = 6.283185307 - rdAngle;
		       else
			   rdAngle += 3.141592654;
		   else
		       if ( pcw->aptlXHair[2].x	< pcw->ptlOrigin.x )
			   rdAngle = 3.141592654 - rdAngle;
		   }
       return(MRFROMLONG(lHSBtoRGB((LONG)((rdAngle * 360.0) / 6.283185307),
	      lRadius *	100 / pcw->lRadius)));

   /*********************************************************************/
   /*  RGB colour set							*/
   /*********************************************************************/
   /*********************************************************************/
   /*									*/
   /*  Message:								*/
   /*	       CWM_SETRGBCLR						*/
   /*									*/
   /*  Input:								*/
   /*	       mp1 = RGB2 rgb2;						*/
   /*	       mp2 = 0L;						*/
   /*									*/
   /*  Return:								*/
   /*	       x = SHORT1FROMMR(mr);					*/
   /*	       y = SHORT2FROMMR(mr);					*/
   /*									*/
   /*********************************************************************/

   case	CWM_SETRGBCLR :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Get the RGB from the message parameter	*/

       prgb2 = (PRGB2)mp1;

       lMin = min(prgb2->bRed, min(prgb2->bGreen, prgb2->bBlue));

       if ( (lMax = max(prgb2->bRed, max(prgb2->bGreen,	prgb2->bBlue)))	!= 0 )
	   {
	   lRadius = ((lMax - lMin) * pcw->lRadius) / lMax;

	   if (	lMax ==	lMin )
	       lAngle =	0L;
	   else
	       if ( prgb2->bRed	== lMax	)
		   lAngle = ((prgb2->bGreen - prgb2->bBlue) * 60L) / (lMax - lMin);
	       else
		   if (	prgb2->bGreen == lMax )
		       lAngle =	120 + ((prgb2->bBlue - prgb2->bRed) * 60L) / (lMax - lMin);
		   else
		       if ( prgb2->bBlue == lMax )
			   lAngle = 240	+ ((prgb2->bRed	- prgb2->bGreen) * 60L)	/ (lMax	- lMin);

	   if (	lAngle < 0L )
	       lAngle += 360;
	   }
       else
	   lRadius = lAngle = 0L;

		       /* Set up the arc parameters			*/

       pcw->ap.lP = pcw->ap.lQ = pcw->lRadius;
       GpiSetArcParams(hPS = WinGetPS(hWnd), &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

       GpiBeginPath(hPS, 1L);
       GpiMove(hPS, &pcw->ptlOrigin);
       GpiFullArc(hPS, DRO_OUTLINE, MAKEFIXED(1, 0));
       GpiEndPath(hPS);
		       /* Circle complete, convert the normal path to a	*/
		       /* clip path to allow the determination of the	*/
		       /* mouse	button click within the	colour wheel or	*/
		       /* outside the edge of the wheel	but still	*/
		       /* within the limits of the control itself	*/

       GpiSetClipPath(hPS, 1L, SCP_ALTERNATE | SCP_AND);

		       /* Set the mix mode for line invert		*/

       GpiSetMix(hPS, FM_INVERT);

		       /* Erase	the current cross hair by drawing it	*/
		       /* again	which will cause the previous colours	*/
		       /* to be	displayed since	it is being drawn in	*/
		       /* invert mix mode				*/

       GpiMove(hPS, pcw->aptlXHair);
       GpiLine(hPS, &pcw->aptlXHair[1]);

       GpiMove(hPS, &pcw->aptlXHair[2]);
       GpiLine(hPS, &pcw->aptlXHair[3]);

		       /* Save the new position	of the mouse pointer	*/
		       /* within the cross hair	point array		*/

       pcw->aptlXHair[2].x =
       pcw->aptlXHair[3].x = (LONG)((double)lRadius * cos(rdAngle = (double)lAngle / 57.29577951)) +
			     pcw->ptlOrigin.x;
       pcw->aptlXHair[0].y =
       pcw->aptlXHair[1].y = (LONG)((double)lRadius * sin(rdAngle)) + pcw->ptlOrigin.y;

		       /* Draw the new cross hair within the invert	*/
		       /* mix mode					*/

       GpiMove(hPS, pcw->aptlXHair);
       GpiLine(hPS, &pcw->aptlXHair[1]);

       GpiMove(hPS, &pcw->aptlXHair[2]);
       GpiLine(hPS, &pcw->aptlXHair[3]);

		       /* Release the controls presentation space	*/

       WinReleasePS(hPS);
       return(MRFROMLONG(MAKELONG((USHORT)pcw->aptlXHair[2].x, (USHORT)pcw->aptlXHair[0].y)));

   /*********************************************************************/
   /*  HSB colour query							*/
   /*********************************************************************/
   /*********************************************************************/
   /*									*/
   /*  Message:								*/
   /*	       CWM_QUERYHSBCLR						*/
   /*									*/
   /*  Input:								*/
   /*	       mp1 = 0L;						*/
   /*	       mp2 = 0L;						*/
   /*									*/
   /*  Return:								*/
   /*	       sHue	   = SHORT1FROMMR(mr);				*/
   /*	       sSaturation = SHORT2FROMMR(mr);				*/
   /*									*/
   /*********************************************************************/

   case	CWM_QUERYHSBCLR	:
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Check	to see if the mouse pointer at the	*/
		       /* origin of the	colour wheel in	the horizontal	*/
		       /* direction					*/

       if ( pcw->aptlXHair[2].x	== pcw->ptlOrigin.x )

		       /* Mouse	pointer	aligning horizontally at zero	*/
		       /* in the colour	wheel.	Need to	specify	in	*/
		       /* radians the angle in which the cross is	*/
		       /* resting.  Check to see if the	y position	*/
		       /* positive which indicates the angle is	90ø	*/
		       /* otherwise it is 270ø.				*/

	       if ( (lRadius = pcw->aptlXHair[0].y - pcw->ptlOrigin.y) < 0 )
		   rdAngle = 4.712388980;
	       else
		   rdAngle = 1.570796327;
	   else
		       /* Check	to see if the mouse pointer at the	*/
		       /* origin of the	colour wheel in	the vertical	*/
		       /* direction					*/

	       if ( pcw->aptlXHair[0].y	== pcw->ptlOrigin.y )

		       /* Mouse	pointer	aligning horizontally at zero	*/
		       /* in the colour	wheel.	Need to	specify	in	*/
		       /* radians the angle in which the cross is	*/
		       /* resting.  Check to see if the	y position	*/
		       /* positive which indicates the angle is	0ø	*/
		       /* otherwise it is 180ø.				*/

		   if (	(lRadius = pcw->aptlXHair[2].x - pcw->ptlOrigin.x) < 0 )
		       rdAngle = 3.141592654;
		   else
		       rdAngle = 0.0;
	       else
		   {
		       /* Determine the	absolute x and y co-ordinates	*/
		       /* based	upon cartesian system.	All trig	*/
		       /* functions are	within the 0 to	90ø range.	*/

		   if (	pcw->aptlXHair[0].y < pcw->ptlOrigin.y )
		       y = pcw->ptlOrigin.y - pcw->aptlXHair[0].y;
		   else
		       y = pcw->aptlXHair[0].y - pcw->ptlOrigin.y;

		   if (	pcw->aptlXHair[2].x < pcw->ptlOrigin.x )
		       x = pcw->ptlOrigin.x - pcw->aptlXHair[2].x;
		   else
		       x = pcw->aptlXHair[2].x - pcw->ptlOrigin.x;

		       /* Calculate the	radius from the	x and y		*/
		       /* position of the mouse	using trig and right	*/
		       /* angle	geometry				*/

		   lRadius = (LONG)((double)y /	sin(rdAngle = atan2((double)y, (double)x)));

		       /* Determine the	quadrant that the mouse	pointer	*/
		       /* was in to be able to form correct angle	*/

		   if (	pcw->aptlXHair[0].y < pcw->ptlOrigin.y )
		       if ( pcw->aptlXHair[2].x	> pcw->ptlOrigin.x )
			   rdAngle = 6.283185307 - rdAngle;
		       else
			   rdAngle += 3.141592654;
		   else
		       if ( pcw->aptlXHair[2].x	< pcw->ptlOrigin.x )
			   rdAngle = 3.141592654 - rdAngle;
		   }
       return(MRFROMLONG(MAKELONG((LONG)rdAngle, lRadius * 100 / pcw->lRadius)));

   /*********************************************************************/
   /*  Set HSB colour							*/
   /*********************************************************************/
   /*********************************************************************/
   /*									*/
   /*  Message:								*/
   /*	       CWM_SETHSBCLR						*/
   /*									*/
   /*  Input:								*/
   /*	       mp1 = lHue;						*/
   /*	       mp2 = lSaturation;					*/
   /*									*/
   /*  Return:								*/
   /*	       x = SHORT1FROMMR(mr);					*/
   /*	       y = SHORT2FROMMR(mr);					*/
   /*									*/
   /*********************************************************************/

   case	CWM_SETHSBCLR :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);

		       /* Get the hue (angle) and the saturation	*/
		       /* (radius)					*/

       lAngle  = (LONG)LONGFROMMP(mp1);
       lRadius = (LONG)LONGFROMMP(mp2);

		       /* Set up the arc parameters			*/

       pcw->ap.lP = pcw->ap.lQ = pcw->lRadius;
       GpiSetArcParams(hPS = WinGetPS(hWnd), &pcw->ap);

		       /* Create the clipping path to encompass	just	*/
		       /* the colour wheel itself.  To do this,	open a	*/
		       /* normal path and draw a circle	into the path	*/
		       /* that has the same radius as the colour wheel.	*/

       GpiBeginPath(hPS, 1L);
       GpiMove(hPS, &pcw->ptlOrigin);
       GpiFullArc(hPS, DRO_OUTLINE, MAKEFIXED(1, 0));
       GpiEndPath(hPS);
		       /* Circle complete, convert the normal path to a	*/
		       /* clip path to allow the determination of the	*/
		       /* mouse	button click within the	colour wheel or	*/
		       /* outside the edge of the wheel	but still	*/
		       /* within the limits of the control itself	*/

       GpiSetClipPath(hPS, 1L, SCP_ALTERNATE | SCP_AND);

		       /* Set the mix mode for line invert		*/

       GpiSetMix(hPS, FM_INVERT);

		       /* Erase	the current cross hair by drawing it	*/
		       /* again	which will cause the previous colours	*/
		       /* to be	displayed since	it is being drawn in	*/
		       /* invert mix mode				*/

       GpiMove(hPS, pcw->aptlXHair);
       GpiLine(hPS, &pcw->aptlXHair[1]);

       GpiMove(hPS, &pcw->aptlXHair[2]);
       GpiLine(hPS, &pcw->aptlXHair[3]);

		       /* Save the new position	of the mouse pointer	*/
		       /* within the cross hair	point array		*/

       pcw->aptlXHair[2].x =
       pcw->aptlXHair[3].x = (LONG)((double)lRadius * cos(rdAngle = (double)lAngle / 57.29577951)) +
			     pcw->ptlOrigin.x;
       pcw->aptlXHair[0].y =
       pcw->aptlXHair[1].y = (LONG)((double)lRadius * sin(rdAngle)) + pcw->ptlOrigin.y;

		       /* Draw the new cross hair within the invert	*/
		       /* mix mode					*/

       GpiMove(hPS, pcw->aptlXHair);
       GpiLine(hPS, &pcw->aptlXHair[1]);

       GpiMove(hPS, &pcw->aptlXHair[2]);
       GpiLine(hPS, &pcw->aptlXHair[3]);

		       /* Release the controls presentation space	*/

       WinReleasePS(hPS);
       return(MRFROMLONG(MAKELONG((USHORT)pcw->aptlXHair[2].x, (USHORT)pcw->aptlXHair[0].y)));

/************************************************************************/
/************************************************************************/
/*									*/
/* Part	6: Control destruction coding					*/
/*									*/
/************************************************************************/
/************************************************************************/

   /*********************************************************************/
   /*  Control being destroyed,	perform	clean-up			*/
   /*********************************************************************/

   case	WM_DESTROY :
		       /* Get the address of the control info from the	*/
		       /* control's reserved memory                     */

       pcw = (PCLRWHEEL)WinQueryWindowPtr(hWnd,	QWW_CDATA);
       if ( pcw->tid )
	   {
	   if (	pcw->hpsBitmap )
	       GpiSetStopDraw(pcw->hpsBitmap, SDW_ON);
	   DosKillThread(pcw->tid);
	   }
       WinDestroyPointer(pcw->hptrArrow);
       if ( pcw->hbm )
	   GpiDeleteBitmap(pcw->hbm);
       DosFreeMem((PVOID)pcw);
       break;
		       /* Default message processing			*/
   default:
       return(WinDefWindowProc(hWnd, msg, mp1, mp2));
   }
return(0L);
}
#pragma	subtitle("   Colour Wheel DLL - Control Styles Dialogue Procedure")
#pragma	page ( )

/* --- ClrWheelStyles ---------------------------------- [ Public ] ---	*/
/*									*/
/*     This function is	used for the custom control's styles dialogue   */
/*     box procedure.							*/
/*									*/
/*     When the	dialogue is invoked from Resource Editor, the	     */
/*     address of the user style information is	contained in message	*/
/*     parameter 2.  The dialogue is responsible for saving the		*/
/*     address.	 The best method to do this is to save the pointer	*/
/*     in the dialogue's reserved memory where it can be retrieved as   */
/*     needed.								*/
/*									*/
/*     Upon Entry:							*/
/*									*/
/*     HWND   hWnd; = Dialog Window Handle				*/
/*     ULONG  msg;  = PM Message					*/
/*     MPARAM mp1;  = Message Parameter	1				*/
/*     MPARAM mp2;  = Message Parameter	2				*/
/*									*/
/*     Upon Exit:							*/
/*									*/
/*     ClrWheelStyles =	Message	Handling Result				*/
/*									*/
/* --------------------------------------------------------------------	*/

MRESULT	EXPENTRY ClrWheelStyles(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)

{
PCLRWHLCDATA pcwd;		   /* Colour Wheel CTLDATA Pointer	*/
PUSERSTYLE   pust;		   /* User Style Pointer		*/
SWP	     swp;		   /* Screen Window Position Holder	*/

switch ( msg )
   {
		       /* Perform dialogue initialization		*/
   case	WM_INITDLG :
		       /* Save the pointer to user style information	*/
		       /* within the dialog's reserved memory           */

       WinSetWindowPtr(hWnd, QWL_USER, (PVOID)mp2);

		       /* Get the pointer to the user style information	*/

       if ( (pust = (PUSERSTYLE)mp2) !=	NULL )
	   {
		       /* Set the text,	ID symbol and value for	the	*/
		       /* control					*/

	   pust->pfnSetSymbolID(hWnd, IDBX_SYMBOLVALUE,	pust);
	   pcwd	= (PCLRWHLCDATA)pust->abCtlData;
	   WinSetDlgItemShort(hWnd, EF_ANGLE, (USHORT)pcwd->lAngle, FALSE);
	   WinSetDlgItemShort(hWnd, EF_SATURATIONINC, (USHORT)pcwd->lSaturationInc, FALSE);
	   WinSetDlgItemShort(hWnd, EF_RADIUS, (USHORT)pcwd->lRadius, FALSE);

	   if (	pust->flStyle &	CWS_SOLIDCLR )
	       WinSendDlgItemMsg(hWnd, CB_CWS_SOLIDCLR,	BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	CWS_HSB	)
	       WinSendDlgItemMsg(hWnd, CB_CWS_HSB, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	CWS_RGB	)
	       WinSendDlgItemMsg(hWnd, CB_CWS_RGB, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	CWS_BITMAP )
	       WinSendDlgItemMsg(hWnd, CB_CWS_BITMAP, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	CWS_AUTOSIZE )
	       WinSendDlgItemMsg(hWnd, CB_CWS_AUTOSIZE,	BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	CWS_THREADED )
	       WinSendDlgItemMsg(hWnd, CB_CWS_THREADED,	BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	WS_VISIBLE )
	       WinSendDlgItemMsg(hWnd, CB_VISIBLE, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	WS_GROUP )
	       WinSendDlgItemMsg(hWnd, CB_GROUP, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	WS_DISABLED )
	       WinSendDlgItemMsg(hWnd, CB_DISABLED, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);

	   if (	pust->flStyle &	WS_TABSTOP )
	       WinSendDlgItemMsg(hWnd, CB_TABSTOP, BM_SETCHECK,
				 MPFROMSHORT(TRUE), 0L);
	   }
		       /* Centre dialog	on the screen			*/

       WinQueryWindowPos(hWnd, (PSWP)&swp);
       WinSetWindowPos(hWnd, HWND_TOP,
		       (WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN) - swp.cx) /	2L,
		       (WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - swp.cy) /	2L,
		       0L, 0L, SWP_MOVE);
       break;
		       /* Process push button selections		*/
   case	WM_COMMAND :
       switch (	SHORT1FROMMP(mp1) )
	   {
		       /* Presentation push button selected		*/

	   case	DID_FONTCLR :

		       /* Get the pointer to the user style information	*/

	       if ( (pust = PDATAFROMDLG(hWnd))	!= NULL	)

		       /* Get the address of the look up function from	*/
		       /* user style information structure and display	*/
		       /* the dialog.  The value selected within the	*/
		       /* dialog will be automatically placed within	*/
		       /* the required entry fields			*/

		   pust->pfnGetFontClr(hWnd);
	       break;
		       /* Enter	pushbutton selected get	the definitions	*/
		       /* for the control				*/
	   case	DID_OK :

		       /* Get the pointer to the user style information	*/

	       if ( (pust = PDATAFROMDLG(hWnd))	!= NULL	)
		   {
		       /* Get the address of the symbol	validation	*/
		       /* function from	the user style information	*/
		       /* structure.  The function will	validate the	*/
		       /* symbol and will check	for duplications of	*/
		       /* values.  A return value of TRUE from the	*/
		       /* validation function indicates	that the symbol	*/
		       /* and value are	acceptable.  Conversely, a	*/
		       /* FALSE	return value indicates that symbol or	*/
		       /* value	was not	acceptable.  In	this case,	*/
		       /* the dialog should not	be exited from and the	*/
		       /* values within	the entry fields should	not be	*/
		       /* saved.					*/

		   if (	!pust->pfnGetSymbolID(hWnd, IDBX_SYMBOLVALUE, pust) )
		       break;
		   else
		       {
		       pust->cText = 0;

		       pcwd = (PCLRWHLCDATA)pust->abCtlData;
		       WinQueryDlgItemShort(hWnd, EF_ANGLE, (PSHORT)(PVOID)&pcwd->lAngle, FALSE);
		       WinQueryDlgItemShort(hWnd, EF_SATURATIONINC, (PSHORT)(PVOID)&pcwd->lSaturationInc, FALSE);
		       WinQueryDlgItemShort(hWnd, EF_RADIUS, (PSHORT)(PVOID)&pcwd->lRadius, FALSE);

		       pust->flStyle = 0L;

		       if ( WinSendDlgItemMsg(hWnd, CB_CWS_SOLIDCLR,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= CWS_SOLIDCLR;

		       if ( WinSendDlgItemMsg(hWnd, CB_CWS_HSB,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= CWS_HSB;

		       if ( WinSendDlgItemMsg(hWnd, CB_CWS_RGB,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= CWS_RGB;

		       if ( WinSendDlgItemMsg(hWnd, CB_CWS_BITMAP,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= CWS_BITMAP;

		       if ( WinSendDlgItemMsg(hWnd, CB_CWS_AUTOSIZE,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= CWS_AUTOSIZE;

		       if ( WinSendDlgItemMsg(hWnd, CB_CWS_THREADED,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= CWS_THREADED;


		       if ( WinSendDlgItemMsg(hWnd, CB_VISIBLE,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= WS_VISIBLE;

		       if ( WinSendDlgItemMsg(hWnd, CB_GROUP,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= WS_GROUP;

		       if ( WinSendDlgItemMsg(hWnd, CB_DISABLED,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= WS_DISABLED;

		       if ( WinSendDlgItemMsg(hWnd, CB_TABSTOP,
					      BM_QUERYCHECK, 0L, 0L) )
			   pust->flStyle |= WS_TABSTOP;
		       }
		   }
		       /* Exit the dialogue indicating changes made	*/

	       WinDismissDlg(hWnd, TRUE);
	       break;
		       /* Cancel selected, exit	the dialogue without	*/
		       /* changing anything				*/

	   case	DID_CANCEL :
	       WinDismissDlg(hWnd, FALSE);
	       break;
	   }
       break;
		       /* Close	received, exit dialog			*/
   case	WM_CLOSE :
       WinDismissDlg(hWnd, FALSE);
       break;
		       /* Pass through unhandled messages		*/
   default :
       return(WinDefDlgProc(hWnd, msg, mp1, mp2));
   }
return(0L);

}
