#include "../windows.h"
#include "../windowsx.h"
#include "../wintools.h"
/*
 * WINCTL Custom Control Library
 * Push button Custom Control
 *	This control implements a custom pushbutton control.
 *
 * 4/8/98 g haerr original version from control palette v2.00, Blaise Computing
 */

#define IDTIMERMOUSEMOVE	10	/* id of timer on mouse moves*/

void WINAPI cpDrawPushButton(HWND hwnd,HDC hDCwParam,UINT wEnumState,
	DWORD dwStyle);

#define GET_PBSTATE(h)			(GetWindowWord(h, 0))
#define GET_PBCAPTURE(h)		(GetWindowWord(h, 2))
#define GET_PBWASINSIDE(h)		(GetWindowWord(h, 4))
#define GET_PBUPDATELOCK(h)		(GetWindowWord(h, 6))
#define GET_PBDELETEFONT(h)		(GetWindowWord(h, 8))
#define GET_PBFONT(h)			(GetWindowWord(h, 10))

#define SET_PBSTATE(h,x)		(SetWindowWord(h, 0, x))
#define SET_PBCAPTURE(h,x)		(SetWindowWord(h, 2, x))
#define SET_PBWASINSIDE(h,x)		(SetWindowWord(h, 4, x))
#define SET_PBUPDATELOCK(h,x)		(SetWindowWord(h, 6, x))
#define SET_PBDELETEFONT(h,x)		(SetWindowWord(h, 8, x))
#define SET_PBFONT(h,x)			(SetWindowWord(h, 10, x))

#define PARENT(hwnd)		((HWND)GetWindowLong(hwnd,GWL_HWNDPARENT))

/* Internal state variable bit positions							   */
#define PUSH_UP			0x0000
#define PUSH_DOWN		0x0001	/* Button is down					   */
#define PUSH_FOCUS		0x0002	/* Button is focused				   */
#define PUSH_DISABLED	0x0004	/* Button is disabled				   */
#define PUSH_DEFAULT	0x0008	/* Button is currently a default	   */

/* Push Button states */
#define PBS_UP			0x0000			/* Normal button state.				*/
#define PBS_FOCUSDOWN	0x0001			/* Button pressed.					*/
#define PBS_FOCUSUP		0x0002			/* Focused state.					*/
#define PBS_DISABLED	0x0004			/* Disabled state.					*/
#define PBS_DEFAULT		0x0008			/* Default state.					*/
#define PBS_CHECKED		0x0010			/* checked state.					*/

/* BOOL Cls_OnGetState( HWND hwnd); */
#define HANDLE_BM_GETSTATE(hwnd, wParam, lParam, fn) ((fn)(hwnd))
#define FORWARD_BM_GETSTATE(hwnd) \
	(LONG)(fn)((hwnd), BM_GETSTATE, (WPARAM)0, (LPARAM)0)

/* void Cls_OnSetState( HWND hwnd, WORD wState); */
#define HANDLE_BM_SETSTATE( hwnd, wParam, lParam, fn) \
	((fn)((hwnd), (WORD)wParam), 0)
#define FORWARD_BM_SETSTATE( hwnd, wState) \
	(fn)((hwnd), BM_SETSTATE, (WPARAM)wState, (LPARAM)0)

/* void Cls_OnSetStyle( HWND hwnd, WORD style, BOOL bRedraw); */
#define HANDLE_BM_SETSTYLE( hwnd, wParam, lParam, fn) \
	((fn)((hwnd), (WORD)wParam, (BOOL)LOWORD(lParam)), 0)
#define FORWARD_BM_SETSTYLE( hwnd, style, bRedraw, fn) \
	(fn)((hwnd), BM_SETSTYLE, (WPARAM)style, MAKELPARAM(bRedraw, 0))

/* local procs*/
static void WINAPI	cenButton_FnEnd( HWND, WORD);
static WORD WINAPI	cenButton_FnStart( HWND);
static BOOL WINAPI	cenButton_OnCreate( HWND, LPCREATESTRUCT);
/*static void WINAPI	cenButton_OnDestroy( HWND);*/
/*static void WINAPI	cenButton_OnEnable( HWND, BOOL);*/
static BOOL WINAPI	cenButton_OnEraseBkgnd( HWND, HDC);
/*static UINT WINAPI	cenButton_OnGetDlgCode( HWND, LPMSG);*/
static LONG WINAPI	cenButton_OnGetState( HWND);
/*static void WINAPI	cenButton_OnKey( HWND, UINT, BOOL, int, UINT);*/
static void WINAPI	cenButton_OnKillFocus( HWND, HWND);
static void WINAPI	cenButton_OnLButtonDown( HWND, BOOL, UINT, UINT, UINT);
static void WINAPI	cenButton_OnLButtonUp( HWND, UINT, UINT, UINT);
static void WINAPI	cenButton_OnMouseMove( HWND, UINT, UINT, UINT);
static void WINAPI	cenButton_OnPaint( HWND, HDC);
/*static void WINAPI	cenButton_OnTimer( HWND, UINT);*/
static void WINAPI	cenButton_OnSetFocus( HWND, HWND);
static void WINAPI	cenButton_OnSetStyle( HWND, WORD, BOOL);
static void WINAPI	cenButton_OnSetState( HWND, WORD);
static void WINAPI	cenButton_SetState( HWND, WORD, BOOL);
static void WINAPI	cenButton_OnSetText( HWND, LPCSTR);

static void WINAPI
cenButton_FnEnd(
HWND		hwnd,
WORD		wState)
{
	WORD		wLock;

	wLock = GET_PBUPDATELOCK( hwnd) - 1;
	if( wLock > 32767)
		wLock = 0;
	SET_PBUPDATELOCK( hwnd, wLock);
	if( ( wLock == 0) && ( wState != GET_PBSTATE( hwnd))) {
		if( IsWindowVisible( hwnd))
			UpdateWindow( hwnd);
	}
}

static WORD WINAPI
cenButton_FnStart(
HWND		hwnd)
{
	WORD		wState;
	WORD		wLock;

	wState = GET_PBSTATE( hwnd);
	wLock = GET_PBUPDATELOCK( hwnd) + 1;
	SET_PBUPDATELOCK( hwnd, wLock);

	return( wState);
}

static BOOL WINAPI
cenButton_OnCreate(
HWND			hwnd,
LPCREATESTRUCT	lpCreate)
{
	/* WM_CREATE is sent as the control is created but before  */
	/* it is displayed.	 The control is registered (that is,   */
	/* a data structure is created and associated with this	   */
	/* control instance.  This data structure is used to hold  */
	/* attributes set as a response to the WM_CTLATTRIBS	   */
	/* message.)  Several window extra-bytes variables are set */
	/* with the initial capture state information.	Some of	   */
	/* this information (default & disabled) is found in the   */
	/* window style.										   */

	/* Set initial states */
	/*SET_PBDELETEFONT( hwnd, FALSE);*/
    	/*SET_PBFONT( hwnd, NULL);*/
	SET_PBUPDATELOCK( hwnd, 1);
	SET_PBSTATE( hwnd, PUSH_UP );
	SET_PBCAPTURE( hwnd, FALSE );
	SET_PBWASINSIDE( hwnd, FALSE );

	if ((lpCreate->style & 0x0f) == BS_DEFPUSHBUTTON)
		cenButton_SetState( hwnd, PUSH_DEFAULT, TRUE );

	if (lpCreate->style & WS_DISABLED)
		cenButton_SetState( hwnd, PUSH_DISABLED, TRUE );

	SET_PBUPDATELOCK( hwnd, 0);

	return( TRUE);
}

#if 0
static void WINAPI
cenButton_OnDestroy(
HWND		hwnd)
{
	if( GET_PBDELETEFONT( hwnd)) {
		DeleteObject( GET_PBFONT( hwnd));
		SET_PBDELETEFONT( hwnd, FALSE);
	}
}

static void WINAPI
cenButton_OnEnable(
HWND		hwnd,
BOOL		bEnable)
{
	/* WM_ENABLE is sent when the button is enabled or		   */
	/* disabled.  This information is saved for use when the   */
	/* button is eventually drawn.							   */

	WORD		wState;

	wState = cenButton_FnStart( hwnd);
	cenButton_SetState( hwnd, PUSH_DISABLED, !bEnable);
	cenButton_FnEnd( hwnd, wState);
}
#endif

static BOOL WINAPI
cenButton_OnEraseBkgnd(
HWND		hwnd,
HDC			hdc)
{
	/* WM_ERASEBKGND is sent when the window background is	   */
	/* to be redrawn.  Since all of the painting (including	   */
	/* the background) is accomplished in WM_PAINT, it is	   */
	/* disabled.											   */

	return TRUE;
}

#if 0
static UINT WINAPI
cenButton_OnGetDlgCode(
HWND	hwnd,
LPMSG 	lpMsg)
{
	/* WM_GETDLGCODE is sent by the dialog manager to find	   */
	/* what type/style of control is responding and/or to	   */
	/* determine what keystrokes the control wants to process  */
	/* itself.	In this case, the pushbutton identifies itself */
	/* and also indicates whether it is currently the default  */
	/* pushbutton.											   */

	/*return( DLGC_BUTTON | ((GET_PBSTATE( hwnd) & PUSH_DEFAULT) ?
				DLGC_DEFPUSHBUTTON : DLGC_UNDEFPUSHBUTTON));*/
	return( DLGC_BUTTON);
}
#endif

static LONG WINAPI
cenButton_OnGetState(
HWND		hwnd)
{
	/* BM_GETSTATE is sent to enquire about the state of the   */
	/* control.	 It returns TRUE if the button is in the down  */
	/* state.												   */

	return( ( GET_PBSTATE( hwnd) & PUSH_DOWN) == PUSH_DOWN);
}

#if 0
static void WINAPI
cenButton_OnKey(
HWND		hwnd,
UINT		vk,
BOOL		bDown,
int			cRepeat,
UINT		flag)
{
	WORD		wState;

	wState = cenButton_FnStart( hwnd);
	if (bDown) {
	 	/* WM_KEYDOWN is sent when a non-system key is pressed.	   */
		/* If a spacebar is detected and the previous key state	   */
		/* was up, then the control should switch to the down	   */
		/* state.												   */

		if ( (vk == ' ') && !(HIBYTE(flag) & 0x40) )
			cenButton_SetState( hwnd, PUSH_DOWN, TRUE );
	}
	else {
		/* WM_KEYUP is sent when a non-system key is released.	   */
		/* If a space bar is detected, change to the up state.	If */
		/* the control is the focused control, send the BN_CLICKED */
		/* notification message.								   */

		if ( vk == ' ' )
		{	cenButton_SetState( hwnd, PUSH_DOWN, FALSE );

			if (GET_PBSTATE( hwnd) & PUSH_FOCUS) {
				FORWARD_WM_COMMAND( PARENT( hwnd), GetDlgCtrlID( hwnd),
					hwnd, BN_CLICKED, SendMessage);
				if(!IsWindow(hwnd))
					return;
			}
		}
	}
	cenButton_FnEnd( hwnd, wState);
}
#endif

static void WINAPI
cenButton_OnKillFocus(
HWND		hwnd,
HWND		hwndNewFocus)
{
	/* WM_KILLFOCUS is sent as the control loses focus.	 Two   */
	/* states are disabled since the control is no longer	   */
	/* focused and can no longer be in the down state.		   */

	WORD		wState;

	wState = cenButton_FnStart( hwnd);
	cenButton_SetState( hwnd, PUSH_FOCUS, FALSE );
	cenButton_FnEnd( hwnd, wState);
}

static void WINAPI
cenButton_OnLButtonDown(
HWND		hwnd,
BOOL		bDblClick,
UINT		x,
UINT		y,
UINT		keyState)
{
	/* WM_LBUTTONDOWN is sent when the user presses the left   */
	/* mouse button.  First the control captures the mouse and */
	/* sets the focus to itself.  Then the capture variables   */
	/* are set and the down state is set.					   */

	WORD		wState;

	wState = cenButton_FnStart( hwnd);
	SetCapture( hwnd );
	SetFocus( hwnd );
	SET_PBCAPTURE( hwnd, TRUE );
	SET_PBWASINSIDE( hwnd, TRUE );
	cenButton_SetState( hwnd, PUSH_DOWN, TRUE );
	cenButton_FnEnd( hwnd, wState);
}

static void WINAPI
cenButton_OnLButtonUp(
HWND		hwnd,
UINT		x,
UINT		y,
UINT		keys)
{
	/* WM_LBUTTONUP is sent when the user releases the left	   */
	/* mouse button.  Release the mouse capture and update the */
	/* state.  If the cursor is inside the control send the	   */
	/* BN_CLICKED notification to the parent.  Then reset the  */
	/* capture variables.									   */

	WORD		wState;

	wState = cenButton_FnStart( hwnd);

	/* only draw up state if we captured mouse*/
	if(GetCapture() == hwnd)
		cenButton_SetState( hwnd, PUSH_DOWN, FALSE );
	ReleaseCapture();

	if (GET_PBWASINSIDE( hwnd)) {
		FORWARD_WM_COMMAND( PARENT( hwnd), GetDlgCtrlID( hwnd), hwnd,
			BN_CLICKED, SendMessage);
		if(!IsWindow(hwnd))
			return;
	}

	SET_PBCAPTURE( hwnd, FALSE );
	SET_PBWASINSIDE( hwnd, FALSE );
	cenButton_FnEnd( hwnd, wState);
}

static void WINAPI
cenButton_OnMouseMove(
HWND		hwnd,
UINT		x,
UINT		y,
UINT		keys)
{
	/* WM_MOUSEMOVE is sent at every discernable mouse		   */
	/* movement.  It is necessary to detect this because if	   */
	/* the mouse has been captured (because of a button down   */
	/* message), the location of the cursor needs to be		   */
	/* tracked.	 If it moves out of the confines of the		   */
	/* control, the control should change to the focus/up	   */
	/* state (and retain capture.)	If the cursor then moves   */
	/* back into the control, change back to the down state.   */

	WORD		wState;
	DWORD		dwStyle;

	wState = cenButton_FnStart( hwnd);
	dwStyle = GetWindowLong(hwnd, GWL_STYLE);
	if( GET_PBCAPTURE( hwnd)) {
		if( !PtInsideWindow( hwnd, x, y) ) {
			if( GET_PBWASINSIDE( hwnd)) {
				cenButton_SetState( hwnd, PUSH_DOWN, FALSE);
				SET_PBWASINSIDE( hwnd, FALSE );
			}
		} else {
			if ( !( GET_PBWASINSIDE( hwnd)) ) {
				cenButton_SetState( hwnd, PUSH_DOWN, TRUE );
				SET_PBWASINSIDE( hwnd, TRUE );
			}
		}
	}
#if 0
	else if(dwStyle & BS_FLAT) {
		// draw the button as though it has focus
		// and set a timer to determine when the cursor
		// has moved off the button
		cenButton_SetState( hwnd, PUSH_FOCUS, TRUE );
		SetTimer(hwnd, IDTIMERMOUSEMOVE, 50, NULL);
		InvalidateRect(hwnd, NULL, FALSE);
	}
#endif
	cenButton_FnEnd( hwnd, wState);
}

#if 0
static void WINAPI
cenButton_OnTimer(
HWND		hwnd,
UINT		id)
{
	POINT		pt;

	switch(id) {
	case IDTIMERMOUSEMOVE:
		// if the cursor has moved off the button,
		// redraw the button without focus and kill the timer
		GetCursorPos(&pt);
		ScreenToClient(hwnd, &pt);
		if(!PtInsideWindow(hwnd, pt.x, pt.y)) {
			KillTimer(hwnd, IDTIMERMOUSEMOVE);
			cenButton_SetState( hwnd, PUSH_FOCUS, FALSE );
			InvalidateRect(hwnd, NULL, FALSE);
		}
		break;
	}
}
#endif

static void WINAPI
cenButton_OnPaint(
HWND		hwnd,
HDC		hDCwParam)
{
	UINT		wEnumState;
	DWORD		dwStyle;

	if( GET_PBSTATE( hwnd) & PUSH_DISABLED)
		wEnumState = PBS_DISABLED;
	else if( GET_PBSTATE( hwnd) & PUSH_DOWN)
		wEnumState = PBS_FOCUSDOWN;
	else
	{
		if( GET_PBSTATE( hwnd) & PUSH_FOCUS)
			wEnumState = PBS_FOCUSUP;
		else
			wEnumState = PBS_UP;
		if( GET_PBSTATE( hwnd) & PUSH_DEFAULT)
			wEnumState |= PBS_DEFAULT;
	}

	/* common draw code for button and checkbox*/
	dwStyle = GetWindowLong(hwnd, GWL_STYLE);
	cpDrawPushButton( hwnd, hDCwParam, wEnumState, dwStyle);
}

void WINAPI
cpDrawPushButton(HWND hwnd,HDC hDCwParam,UINT wEnumState,DWORD dwStyle)
{
	HDC		hdc;
	HBRUSH		hNewBrush;
	RECT		rect;
	RECT		rectClient;
	RECT		rectSave;
	RECT		rc;
	int		iFaceOffset;
	INT		uiHeight;
	INT		uiWidth;
	COLORREF	crOld;
	COLORREF	crBkOld;
	int		oldBkMode;
	HFONT		hNewFont;
	HFONT		hOldFont;
	COLORREF	hOldColor;
	PAINTSTRUCT	ps;
	char		buf[256];
#define uiWidthFrame	0
#define uiWidthShadow	2

	hdc = BeginPaint(hwnd, &ps);
	if(!hdc)
		goto Return;

	GetWindowText(hwnd, buf, sizeof(buf));
	GetClientRect( hwnd, &rectClient );
	uiWidth	= rectClient.right - rectClient.left;
	uiHeight = rectClient.bottom - rectClient.top;

	hNewBrush = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
	crOld = SetTextColor( hdc, GetSysColor( COLOR_BTNTEXT));
	crBkOld = SetBkColor( hdc, GetSysColor( COLOR_BTNFACE));

	rc = rectClient;
	switch((int)(dwStyle & 0x0f)) {
	case BS_PUSHBUTTON:
	case BS_DEFPUSHBUTTON:
		if( wEnumState & PBS_FOCUSDOWN) {
			Draw3dBox(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
				GetSysColor(COLOR_WINDOWFRAME), GetSysColor(COLOR_WINDOWFRAME));
			InsetR(&rc, 1, 1);
			Draw3dBox(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
				GetSysColor(COLOR_BTNSHADOW), GetSysColor(COLOR_BTNSHADOW));
			InsetR(&rc, 1, 1);
			FastFillRect(hdc, &rc, GetSysColor(COLOR_BTNFACE));
			iFaceOffset = 1;
		} else {
			if(wEnumState & PBS_DEFAULT) {
				Draw3dBox(hdc, rc.left, rc.top,
					rc.right-rc.left, rc.bottom-rc.top,
					GetSysColor(COLOR_WINDOWFRAME),
					GetSysColor(COLOR_WINDOWFRAME));
				InsetR(&rc, 1, 1);
			}
			Draw3dBox(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
				GetSysColor(COLOR_BTNHIGHLIGHT),GetSysColor(COLOR_WINDOWFRAME));
			InsetR(&rc, 1, 1);
			Draw3dBox(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
				GetSysColor(COLOR_3DLIGHT), GetSysColor(COLOR_BTNSHADOW));
			InsetR(&rc, 1, 1);
			FastFillRect(hdc, &rc, GetSysColor(COLOR_BTNFACE));
			iFaceOffset = 0;
		}
		break;
	case BS_CHECKBOX:
	case BS_AUTOCHECKBOX:
		FastFillRect(hdc, &rc, GetSysColor(COLOR_BTNFACE));
		rc.left += 2;
		rc.top += 2;
		rc.right = rc.left + 8;
		rc.bottom = rc.top + 8;
		Draw3dBox(hdc, rc.left, rc.top, 8, 8,
			GetSysColor(COLOR_BTNSHADOW), GetSysColor(COLOR_3DLIGHT));
		InsetR(&rc, 1, 1);
		FastFillRect(hdc, &rc, GetSysColor(COLOR_BTNHIGHLIGHT));
		iFaceOffset = 1;
		break;
	}

	/*
	 * draw text
	 */
	if(buf[ 0]) {
		hNewFont = GetStockObject( DEFAULT_GUI_FONT);
		hOldFont = SelectObject( hdc, hNewFont);

		/* calculate text bounding rect*/
		rect.left = 0;
		rect.top = 0;
		DrawText( hdc, buf, -1, &rect, DT_CALCRECT | DT_LEFT |
			DT_SINGLELINE | DT_TOP);
		rectSave = rect;

		/*
		 * calculate text draw location
		 */
		switch((int)(dwStyle & (BS_LEFT|BS_CENTER|BS_RIGHT))) {
		case BS_CENTER:
		default:
			rect.left = (uiWidth - (rect.right - rect.left)) / 2
				+ iFaceOffset;
			break; 
		case BS_LEFT:
			rect.left = uiWidthFrame + uiWidthShadow + 2
				+ iFaceOffset;
			break;
		case BS_RIGHT:
			rect.left = (rect.right - rect.left) + uiWidthFrame
				+ uiWidthShadow + 4 + iFaceOffset;
			break;
		}
		rect.right += rect.left - rectSave.left;

		switch((int)(dwStyle & (BS_TOP|BS_VCENTER|BS_BOTTOM))) {
		case BS_VCENTER:
		default:
			rect.top = (uiHeight - (rect.bottom - rect.top)) / 2
				+ iFaceOffset;
			break;
		case BS_TOP:
			rect.top = 2 + uiWidthFrame + uiWidthShadow
				+ iFaceOffset;
			break;
		case BS_BOTTOM:
			rect.top = uiHeight - uiWidthFrame - uiWidthShadow -
				(rect.bottom - rect.top) - 1 + iFaceOffset;
			break;
		}
		rect.bottom += rect.top - rectSave.top;

		oldBkMode = SetBkMode( hdc, TRANSPARENT);
		if(wEnumState & PBS_DISABLED)
			hOldColor = SetTextColor( hdc,
				GetSysColor( COLOR_GRAYTEXT));
		else
			hOldColor = SetTextColor( hdc,
				GetSysColor( COLOR_BTNTEXT));

		DrawText( hdc, buf, -1, &rect,DT_LEFT | DT_SINGLELINE | DT_TOP);

		SetBkMode( hdc, oldBkMode);
		SetTextColor( hdc, hOldColor);
		SelectObject( hdc, hOldFont);
	}

#if 0
	if( (!(wEnumState&PBS_CHECKED) && (wEnumState&PBS_FOCUSDOWN)) || 
						(wEnumState & PBS_FOCUSUP)) {
		rect = rectClient;
		uiWidth = uiWidthFrame + uiWidthShadow + 2;
		rect.left += uiWidth;
		rect.top += uiWidth;
		rect.right -= uiWidth;
		rect.bottom -= uiWidth;
		if((dwStyle & (BS_FLAT|BS_NOFOCUSRECT)) == 0)
			DrawFocusRect( hdc, &rect);
	}
#endif

	SetTextColor( hdc, crOld);
	SetBkColor( hdc, crBkOld);

Return:
	EndPaint(hwnd, &ps);
}

static void WINAPI
cenButton_OnSetFocus(
HWND		hwnd,
HWND		hwndOldFocus)
{
	/* WM_SETFOCUS is sent when the user clicks on the control */
	/* or when the dialog manager determines that a keystroke  */
	/* should cause the control to be focused.	This affects   */
	/* the appearance of the control so the state is saved for */
	/* future drawing.										   */

	WORD		wState;

	wState = cenButton_FnStart( hwnd);
	/*if(!IsWindowEnabled(hwnd))
		cenButton_SetState( hwnd, PUSH_FOCUS, TRUE );*/
	cenButton_FnEnd( hwnd, wState);
}

static void WINAPI
cenButton_OnSetStyle(
HWND		hwnd,
WORD		style,
BOOL		bRedraw)
{
	/* BM_SETSTYLE is sent to set the style of the button.	   */
	/* The internal state variable is updated.				   */

	WORD		wState;

	wState = cenButton_FnStart( hwnd);
	cenButton_SetState( hwnd, PUSH_DEFAULT, (style & 0x0f) == BS_DEFPUSHBUTTON);
	cenButton_FnEnd( hwnd, wState);
}

static void WINAPI
cenButton_OnSetState(
HWND		hwnd,
WORD		wState)
{
	/* BM_SETSTATE is sent to set the state of the control.	   */
	/* The internal state variable is updated.				   */

	WORD		wStateOld;

	wStateOld = cenButton_FnStart( hwnd);
	cenButton_SetState( hwnd, PUSH_DOWN, (wState ? TRUE : FALSE ) );
	cenButton_FnEnd( hwnd, wStateOld);
}

static void WINAPI
cenButton_SetState(
HWND	hwnd,
WORD	wState,
BOOL	bEnable )
{
	/* Turn on/off state bits according to the bEnable flag.  If the   */
	/* new state is different, invalidate the client window so that	   */
	/* the proper bitmap is displayed.								   */

	WORD	wNewState;
	WORD	wOldState;
	RECT	rectClient;

	wOldState = GET_PBSTATE( hwnd);
	wNewState = (bEnable ? (wOldState | wState) : (wOldState & ~wState));

	if (wOldState != wNewState)
	{	SET_PBSTATE( hwnd, wNewState );
		GetClientRect(hwnd, &rectClient);
		InvalidateRect(hwnd, &rectClient, FALSE);
	}
}

#if 0
static void WINAPI
cenButton_OnSetFont(
HWND		hwnd,
HFONT		hFont,
BOOL		bRedraw)
{
	BOOL		bDeleteFont = FALSE;
	HFONT		hFontNew = 0;
	LOGFONT		logFont;

	/* create a thin font*/
	if( GetObject( hFont, sizeof( logFont), &logFont) != 0) {
		if( logFont.lfWeight != FW_NORMAL) {
			logFont.lfWeight = FW_NORMAL;
			if( ( hFontNew = CreateFontIndirect( &logFont)) != 0) {
				hFont = hFontNew;
				bDeleteFont = TRUE;
			}
		}
	}

	if( GET_PBDELETEFONT( hwnd))
		DeleteObject( GET_PBFONT( hwnd));

	SET_PBDELETEFONT( hwnd, bDeleteFont);
	SET_PBFONT( hwnd, hFont);

	FORWARD_WM_SETFONT( hwnd, hFont, bRedraw, DefWindowProc);
}
#endif

static void WINAPI
cenButton_OnSetText(
HWND		hwnd,
LPCSTR		lpszText)
{
	/* WM_SETTEXT is sent to change the text of the button	   */
	/* control.	 In this case we allow the default window proc */
	/* to handle the message first.	 But this only affects the */
	/* internal Windows data structure of the control, it does */
	/* not display the change.	To do this we invalidate and   */
	/* update the client area of the control which displays	   */
	/* the new text.										   */

	FORWARD_WM_SETTEXT( hwnd, lpszText, DefWindowProc);
	InvalidateRect( hwnd, NULL, FALSE);
	UpdateWindow( hwnd);
}

LRESULT CALLBACK
cenButtonWndFn(
HWND	hwnd,
UINT	message,
WPARAM	wParam,
LPARAM	lParam)
{
	/* This is the window proc for the pushbutton control.	Most of	   */
	/* the drawing is accomplished in the cpDrawPushButton() function. */
	/* The code below is mainly concerned with the keyboard and mouse  */
	/* events that the control detects.								   */

#define WM_PAINT_SPECIAL	WM_PAINT
#define HANDLE_WM_PAINT_SPECIAL(hwnd, wParam, lParam, fn) \
    ((fn)((hwnd),(HDC)(wParam)), 0L)

	switch( message) {
	HANDLE_MSG( hwnd, WM_CREATE, cenButton_OnCreate);
	/*HANDLE_MSG( hwnd, WM_ENABLE, cenButton_OnEnable);*/
	HANDLE_MSG( hwnd, WM_SETFOCUS, cenButton_OnSetFocus);
	HANDLE_MSG( hwnd, WM_KILLFOCUS, cenButton_OnKillFocus);
	HANDLE_MSG( hwnd, WM_LBUTTONDOWN, cenButton_OnLButtonDown);
	HANDLE_MSG( hwnd, WM_LBUTTONDBLCLK, cenButton_OnLButtonDown);
	HANDLE_MSG( hwnd, WM_LBUTTONUP, cenButton_OnLButtonUp);
	HANDLE_MSG( hwnd, WM_MOUSEMOVE, cenButton_OnMouseMove);
	/*HANDLE_MSG( hwnd, WM_KEYDOWN, cenButton_OnKey);*/
	/*HANDLE_MSG( hwnd, WM_KEYUP, cenButton_OnKey);*/
	HANDLE_MSG( hwnd, WM_ERASEBKGND, cenButton_OnEraseBkgnd);
	HANDLE_MSG( hwnd, WM_PAINT_SPECIAL, cenButton_OnPaint);
	/*HANDLE_MSG( hwnd, WM_TIMER, cenButton_OnTimer);*/
	/*HANDLE_MSG( hwnd, WM_GETDLGCODE, cenButton_OnGetDlgCode);*/
	HANDLE_MSG( hwnd, BM_SETSTYLE, cenButton_OnSetStyle);
	HANDLE_MSG( hwnd, BM_GETSTATE, cenButton_OnGetState);
	HANDLE_MSG( hwnd, BM_SETSTATE, cenButton_OnSetState);
	/*HANDLE_MSG( hwnd, WM_DESTROY, cenButton_OnDestroy);*/
	/*HANDLE_MSG( hwnd, WM_SETFONT, cenButton_OnSetFont);*/
	HANDLE_MSG( hwnd, WM_SETTEXT, cenButton_OnSetText);

	case BM_GETCHECK:
		return cenButton_OnGetState(hwnd);

	case BM_SETCHECK:
		cenButton_OnSetState(hwnd, (WORD)wParam);
		return 0;
	}

	return DefWindowProc( hwnd, message, wParam, lParam);
}

int WINAPI
WndButtonRegister(HINSTANCE hInstance)
{
	WNDCLASS	wc;

	wc.style	= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_GLOBALCLASS;
	wc.lpfnWndProc	= (WNDPROC)cenButtonWndFn;
	wc.cbClsExtra	= 0;
	wc.cbWndExtra	= 12;
	wc.hInstance	= hInstance;
	wc.hIcon	= NULL;
	wc.hCursor	= 0; /*LoadCursor(NULL, IDC_ARROW);*/
	wc.hbrBackground= GetStockObject(LTGRAY_BRUSH);
	wc.lpszMenuName	= NULL;
	wc.lpszClassName= "BUTTON";

	return RegisterClass(&wc);
}
