/*
 * BTTNCUR.C
 * Buttons & Cursors Version 1.1, March 1993
 *
 * Public functions to generate different states of toolbar buttons from
 * a single bitmap.  States are normal, pressed, checked, and disabled.
 *
 * Copyright (c)1992-1993 Microsoft Corporation, All Rights Reserved,
 * as applied to redistribution of this source code in source form
 * License is granted to use of compiled code in shipped binaries.
 */

#ifdef WIN32
#define _INC_OLE
#define __RPC_H__
#endif


#define STRICT
#include <windows.h>
#include <memory.h>
#include "bttncur.h"
#include "bttncuri.h"



//Display sensitive information
TOOLDISPLAYDATA     tdd;

//Library instance
HINSTANCE           ghInst;


//Cache GDI objects to speed drawing.
HDC     hDCGlyphs    = NULL;
HDC     hDCMono      = NULL;
HBRUSH  hBrushDither = NULL;

//Standard images to use in case caller doesn't provide them
HBITMAP rghBmpStandardImages[3];

//Standard button colors.
const COLORREF crStandard[4]={ RGB(0, 0, 0)          //STDCOLOR_BLACK
							 , RGB(128, 128, 128)    //STDCOLOR_DKGRAY
							 , RGB(192, 192, 192)    //STDCOLOR_LTGRAY
							 , RGB(255, 255, 255)};  //STDCOLOR_WHITE


/*
 * Mapping from image identifier to button type (command/attribute).
 * Version 1.00 of this DLL has no attribute images defined, so
 * the code will only support three states for each command
 * button.  Any state is, however, valid for an application
 * defined image.
 */

UINT mpButtonType[TOOLIMAGE_MAX-TOOLIMAGE_MIN+1]=
		{
		BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND,
		BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND,
		BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND
		};




/*
 * LibMain
 *
 * Purpose:
 *  DLL-specific entry point called from LibEntry.  Initializes
 *  global variables and loads standard image bitmaps.
 *
 * Parameters:
 *  hInstance       HANDLE instance of the DLL.
 *  wDataSeg        WORD segment selector of the DLL's data segment.
 *  wHeapSize       WORD byte count of the heap.
 *  lpCmdLine       LPSTR to command line used to start the module.
 *
 * Return Value:
 *  HANDLE          Instance handle of the DLL.
 */

BOOL xxxLibMain(HINSTANCE hInstance)
	{
	 int i;

	ghInst=hInstance;

	 tdd.uDPI     =96;
	tdd.cyBar    =CYBUTTONBAR96;
	tdd.cxButton =TOOLBUTTON_STD96WIDTH;
	tdd.cyButton =TOOLBUTTON_STD96HEIGHT;
	tdd.cxImage  =TOOLBUTTON_STD96IMAGEWIDTH;
	tdd.cyImage  =TOOLBUTTON_STD96IMAGEHEIGHT;
	tdd.uIDImages=IDB_STANDARDIMAGES96;

	for (i=0; i < 3; i++)
		{
		rghBmpStandardImages[i]=LoadBitmap(hInstance
			, MAKEINTRESOURCE(IDB_STANDARDIMAGESMIN+i));

		if (NULL==rghBmpStandardImages[i])
			return FALSE;
		}


	//Perform global initialization.
	if (ToolButtonInit())
		{
		CursorsCache(hInstance);


		return TRUE;
		}

	return FALSE;

	}

#ifdef WIN32

extern  BOOL WINAPI _CRT_INIT(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved);
extern  _cexit(void);
void FAR PASCAL WEP(int);

extern  BOOL __stdcall LibMain
(
	HINSTANCE hInstance,
	ULONG     Reason,
	PCONTEXT  Context
)
{
	OutputDebugString("bttncur LibMain:  bttncur.dll loaded\r\n");

	UNREFERENCED_PARAMETER(Context);

	if (Reason == DLL_PROCESS_DETACH) {

		_CRT_INIT(hInstance,Reason,NULL);
		WEP(0);
		return TRUE;
	}
	else if (Reason != DLL_PROCESS_ATTACH)
		return TRUE;

	if (!_CRT_INIT(hInstance,Reason,NULL))
		 return FALSE;

	 return xxxLibMain(hInstance);

}

#else

HANDLE FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg
						  , WORD cbHeapSize, LPSTR lpCmdLine)
	{

	 //Perform global initialization.
	if (xxxLibMain(hInstance))
		if (0!=cbHeapSize)
			UnlockData(0);

	return hInstance;

	}

#endif


/*
 * WEP
 *
 * Purpose:
 *  Required DLL Exit function.  Does nothing.
 *
 * Parameters:
 *  bSystemExit     BOOL indicating if the system is being shut
 *                  down or the DLL has just been unloaded.
 *
 * Return Value:
 *  void
 *
 */

void FAR PASCAL WEP(int bSystemExit)
	{
	/*
	 * **Developers:  Note that WEP is called AFTER Windows does any
	 *                automatic task cleanup.  You may see warnings for
	 *                that two DCs, a bitmap, and a brush, were not
	 *                deleted before task termination.  THIS IS NOT A
	 *                PROBLEM WITH THIS CODE AND IT IS NOT A BUG.  This
	 *                WEP function is properly called and performs the
	 *                cleanup as appropriate.  The fact that Windows is
	 *                calling WEP after checking task cleanup is not
	 *                something we can control.  Just to prove it, the
	 *                OutputDebugStrings in this and ToolButtonFree
	 *                show that the code is exercised.
	 */

	CursorsFree();
	ToolButtonFree();
	return;
	}





/*
 * UIToolConfigureForDisplay
 * Public API
 *
 * Purpose:
 *  Initializes the library to scale button images for the display type.
 *  Without calling this function the library defaults to 96 DPI (VGA).
 *  By calling this function an application acknowledges that it must
 *  use the data returned from this function to configure itself for
 *  the display.
 *
 * Parameters:
 *  lpDD            LPTOOLDISPLAYDATA to fill with the display-sensitive
 *                  size values.
 *
 * Return Value:
 *  BOOL            TRUE if the sizes were obtained, FALSE otherwise.
 */

BOOL WINAPI UIToolConfigureForDisplay(LPTOOLDISPLAYDATA lpDD)
	{
	int         cy;
	HDC         hDC;


	if (NULL==lpDD || IsBadWritePtr(lpDD, sizeof(TOOLDISPLAYDATA)))
		return FALSE;

	/*
	 * Determine the aspect ratio of the display we're currently
	 * running on and calculate the necessary information.
	 *
	 * By retrieving the logical Y extent of the display driver, you
	 * only have limited possibilities:
	 *      LOGPIXELSY      Display
	 *      ----------------------------------------
	 *         48             CGA    (unsupported)
	 *         72             EGA
	 *         96             VGA
	 *        120             8514/a (i.e. HiRes VGA)
	 */

	hDC=GetDC(NULL);

	if (NULL==hDC)
		return FALSE;

	cy=GetDeviceCaps(hDC, LOGPIXELSY);
	ReleaseDC(NULL, hDC);

	/*
	 * Instead of single comparisons, check ranges instead, so in case
	 * we get something funky, we'll act reasonable.
	 */
	if (72 >=cy)
		{
		lpDD->uDPI     =72;
		lpDD->cyBar    =CYBUTTONBAR72;
		lpDD->cxButton =TOOLBUTTON_STD72WIDTH;
		lpDD->cyButton =TOOLBUTTON_STD72HEIGHT;
		lpDD->cxImage  =TOOLBUTTON_STD72IMAGEWIDTH;
		lpDD->cyImage  =TOOLBUTTON_STD72IMAGEHEIGHT;
		lpDD->uIDImages=IDB_STANDARDIMAGES72;
		}
	else
		{
		if (72 < cy && 120 > cy)
			{
			lpDD->uDPI     =96;
			lpDD->cyBar    =CYBUTTONBAR96;
			lpDD->cxButton =TOOLBUTTON_STD96WIDTH;
			lpDD->cyButton =TOOLBUTTON_STD96HEIGHT;
			lpDD->cxImage  =TOOLBUTTON_STD96IMAGEWIDTH;
			lpDD->cyImage  =TOOLBUTTON_STD96IMAGEHEIGHT;
			lpDD->uIDImages=IDB_STANDARDIMAGES96;
			}
		else
			{
			lpDD->uDPI     =120;
			lpDD->cyBar    =CYBUTTONBAR120;
			lpDD->cxButton =TOOLBUTTON_STD120WIDTH;
			lpDD->cyButton =TOOLBUTTON_STD120HEIGHT;
			lpDD->cxImage  =TOOLBUTTON_STD120IMAGEWIDTH;
			lpDD->cyImage  =TOOLBUTTON_STD120IMAGEHEIGHT;
			lpDD->uIDImages=IDB_STANDARDIMAGES120;
			}
		}

	return TRUE;
	}








/*
 * ToolButtonInit
 * Internal
 *
 * Purpose:
 *  Initializes GDI objects for drawing images through UIToolButtonDraw.
 *  If the function fails, the function has already performed proper
 *  cleanup.
 *
 * Parameters:
 *  None
 *
 * Return Value:
 *  BOOL            TRUE if initialization succeeded.  FALSE otherwise.
 */

static BOOL ToolButtonInit(void)
	{
	COLORREF        rgbHi;

	//DC for BitBltting the image (the glyph)
	hDCGlyphs=CreateCompatibleDC(NULL);

	//Create a monochrome DC and a brush for doing pattern dithering.
	hDCMono=CreateCompatibleDC(NULL);

	//Windows 3.0 doesn't support COLOR_BTNHIGHLIGHT, so leave it white.
	if (0x0300 < (UINT)GetVersion())
		rgbHi=GetSysColor(COLOR_BTNHIGHLIGHT);
	else
		rgbHi=crStandard[STDCOLOR_WHITE];

	hBrushDither=HBrushDitherCreate(GetSysColor(COLOR_BTNFACE), rgbHi);

	if (NULL==hDCGlyphs || NULL==hDCMono || NULL==hBrushDither)
		{
		//On failure, cleanup whatever might have been allocated.
		ToolButtonFree();
		return FALSE;
		}

	return TRUE;
	}





/*
 * ToolButtonFree
 * Internal
 *
 * Purpose:
 *  Free all GDI allocations made during initialization.  Note that the
 *  DEBUG output included here shows that WEP is called and cleanup actually
 *  occurs.  However, if you watch debug output in DBWIN or on a terminal,
 *  the debugging version of Windows does automatic app cleanup before WEP
 *  is called, leading some to believe that this code is buggy.  The
 *  debug output below shows that we do perform all necessary cleanup.
 *
 * Parameters:
 *  None
 *
 * Return Value:
 *  None
 */

static void ToolButtonFree(void)
	{
	UINT        i;

	if (NULL!=hDCMono)
		DeleteDC(hDCMono);
	hDCMono=NULL;


	if (NULL!=hDCGlyphs)
		DeleteDC(hDCGlyphs);
	hDCGlyphs=NULL;

	if (NULL!=hBrushDither)
		DeleteObject(hBrushDither);
	hBrushDither=NULL;

	for (i=0; i < 3; i++)
		{
		if (NULL!=rghBmpStandardImages[i])
			DeleteObject(rghBmpStandardImages[i]);
		rghBmpStandardImages[i]=NULL;
		}

	return;
	}





/*
 * HBrushDitherCreate
 * Internal
 *
 * Purpose:
 *  Creates and returns a handle to a pattern brush created from
 *  an 8*8 monochrome pattern bitmap.  We use the button face and
 *  highlight colors to indicate the resulting colors of a PatBlt
 *  using this brush.
 *
 * Parameters:
 *  rgbFace         COLORREF of the button face color.
 *  rgbHilight      COLORREF of the button highlight color.
 *
 * Return Value:
 *  HBITMAP         Handle to the dither bitmap.
 */

static HBRUSH HBrushDitherCreate(COLORREF rgbFace, COLORREF rgbHilight)
	{
	struct  //BITMAPINFO with 16 colors
		{
		BITMAPINFOHEADER    bmiHeader;
		RGBQUAD             bmiColors[16];
		} bmi;

	HBRUSH          hBrush=NULL;
	DWORD           patGray[8];
	HDC             hDC;
	HBITMAP         hBmp;
	static COLORREF rgbFaceOld   =0xFFFFFFFF;  //Initially an impossible color
	static COLORREF rgbHilightOld=0xFFFFFFFF;  //so at first we always create

	/*
	 * If the colors haven't changed from last time, just return the
	 * existing brush.
	 */
	if (rgbFace==rgbFaceOld && rgbHilight==rgbHilightOld)
		return hBrushDither;

	rgbFaceOld=rgbFace;
	rgbHilightOld=rgbHilight;

	/*
	 * We're going to create an 8*8 brush for PatBlt using the
	 * button face color and button highlight color.  We use this
	 * brush to affect the pressed state and the disabled state.
	 */
	bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth        = 8;
	bmi.bmiHeader.biHeight       = 8;
	bmi.bmiHeader.biPlanes       = 1;
	bmi.bmiHeader.biBitCount     = 1;
	bmi.bmiHeader.biCompression  = BI_RGB;
	bmi.bmiHeader.biSizeImage    = 0;
	bmi.bmiHeader.biXPelsPerMeter= 0;
	bmi.bmiHeader.biYPelsPerMeter= 0;
	bmi.bmiHeader.biClrUsed      = 0;
	bmi.bmiHeader.biClrImportant = 0;

	bmi.bmiColors[0].rgbBlue     = GetBValue(rgbFace);
	bmi.bmiColors[0].rgbGreen    = GetGValue(rgbFace);
	bmi.bmiColors[0].rgbRed      = GetRValue(rgbFace);
	bmi.bmiColors[0].rgbReserved = 0;

	bmi.bmiColors[1].rgbBlue     = GetBValue(rgbHilight);
	bmi.bmiColors[1].rgbGreen    = GetGValue(rgbHilight);
	bmi.bmiColors[1].rgbRed      = GetRValue(rgbHilight);
	bmi.bmiColors[1].rgbReserved = 0;

	//Create the byte array for CreateDIBitmap.
	patGray[6]=patGray[4]=patGray[2]=patGray[0]=0x5555AAAAL;
	patGray[7]=patGray[5]=patGray[3]=patGray[1]=0xAAAA5555L;

	//Create the bitmap
	hDC=GetDC(NULL);
	hBmp=CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT, patGray
						, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS);
	ReleaseDC(NULL, hDC);

	//Create the brush from the bitmap
	if (NULL!=hBmp)
		{
		hBrush=CreatePatternBrush(hBmp);
		DeleteObject(hBmp);
		}

	/*
	 * If we could recreate a brush, clean up and make it the current
	 * pattern.  Otherwise the best we can do it return the old one,
	 * which will be colored wrong, but at least it works.
	 */
	if (NULL!=hBrush)
		{
		if (NULL!=hBrushDither)
			DeleteObject(hBrushDither);

		hBrushDither=hBrush;
		}

	return hBrushDither;
	}





/*
 * UIToolButtonDraw
 * Public API
 *
 * Purpose:
 *  Draws the complete image of a toolbar-style button with a given
 *  image in the center and in a specific state.  The button is drawn
 *  on a specified hDC at a given location, so this function is useful
 *  on standard owner-draw buttons as well as on toolbar controls that
 *  have only one window but show images of multiple buttons.
 *
 * Parameters:
 *  hDC             HDC on which to draw.
 *  x, y            int coordinates at which to draw.
 *  dx, dy          int dimensions of the *button*, not necessarily the image.
 *  hBmp            HBITMAP from which to draw the image.
 *  bmx, bmy        int dimensions of each bitmap in hBmp.  If hBmp is NULL
 *                  then these are forced to the standard sizes.
 *  iImage          int index to the image to draw in the button
 *  uStateIn        UINT containing the state index for the button and the
 *                  color control bits.
 *
 * Return Value:
 *  BOOL            TRUE if drawing succeeded, FALSE otherwise meaning that
 *                  hDC is NULL or hBmp is NULL and iImage is not a valid
 *                  index for a standard image.
 */

BOOL WINAPI UIToolButtonDraw(HDC hDC, int x, int y, int dx, int dy
	, HBITMAP hBmp, int bmx, int bmy, int iImage, UINT uStateIn)
	{
	return UIToolButtonDrawTDD(hDC, x, y, dx, dy, hBmp, bmx, bmy, iImage
		, uStateIn, &tdd);
	}






/*
 * UIToolButtonDrawTDD
 * Public API
 *
 * Purpose:
 *  Draws the complete image of a toolbar-style button with a given
 *  image in the center and in a specific state.  The button is drawn
 *  on a specified hDC at a given location, so this function is useful
 *  on standard owner-draw buttons as well as on toolbar controls that
 *  have only one window but show images of multiple buttons.
 *
 *  This is the same as UIToolButtonDraw but adds the pTDD configuration
 *  structure.  UIToolButtonDraw calls us with that pointing to the
 *  default 96dpi structure.
 *
 * Parameters:
 *  hDC             HDC on which to draw.
 *  x, y            int coordinates at which to draw.
 *  dx, dy          int dimensions of the *button*, not necessarily the image.
 *  hBmp            HBITMAP from which to draw the image.
 *  bmx, bmy        int dimensions of each bitmap in hBmp.  If hBmp is NULL
 *                  then these are forced to the standard sizes.
 *  iImage          int index to the image to draw in the button
 *  uStateIn        UINT containing the state index for the button and the
 *                  color control bits.
 *  pTDD            LPTOOLDISPLAYDATA containing display configuration.
 *                  Can be NULL if hBmp is non-NULL.
 *
 * Return Value:
 *  BOOL            TRUE if drawing succeeded, FALSE otherwise meaning that
 *                  hDC is NULL or hBmp is NULL and iImage is not a valid
 *                  index for a standard image.
 */

BOOL WINAPI UIToolButtonDrawTDD(HDC hDC, int x, int y, int dx, int dy
	, HBITMAP hBmp, int bmx, int bmy, int iImage, UINT uStateIn
	, LPTOOLDISPLAYDATA pTDD)
	{
	static COLORREF crSys[5];  //Avoid stack arrays in DLLs: use static
	UINT            uState=(UINT)LOBYTE((WORD)uStateIn);
	UINT            uColors=(UINT)HIBYTE((WORD)uStateIn & PRESERVE_ALL);
	int             xOffsetGlyph, yOffsetGlyph;
	int             i, iSaveDC;
	HDC             hMemDC;
	HGDIOBJ         hObj;
	HBRUSH          hBR;
	HBITMAP         hBmpT;
	HBITMAP         hBmpMono;
	HBITMAP         hBmpMonoOrg;
	HBITMAP         hBmpSave=NULL;

	if (NULL==hDC)
		return FALSE;

	/*
	 * If we're given no image bitmap, then use the standard and validate the
	 * image index.  We also enforce the standard bitmap size and the size of
	 * the button (as requested by User Interface designers).
	 */
	if (NULL==hBmp && !(uState & BUTTONGROUP_BLANK))
		{
		hBmp=rghBmpStandardImages[pTDD->uIDImages-IDB_STANDARDIMAGESMIN];

		bmx=pTDD->cxImage;            //Force bitmap dimensions
		bmy=pTDD->cyImage;

		dx=pTDD->cxButton;            //Force button dimensions
		dy=pTDD->cyButton;

		if (iImage > TOOLIMAGE_MAX)
			return FALSE;

		/*
		 * If we are using a standard command button, verify that the state
		 * does not contain the LIGHTFACE group which only applies to
		 * attribute buttons.
		 */
		if (BUTTONTYPE_COMMAND==mpButtonType[iImage]
			&& (uState & BUTTONGROUP_LIGHTFACE))
			return FALSE;
		}

	//Create a dithered bitmap.
	hBmpMono=CreateBitmap(dx-2, dy-2, 1, 1, NULL);

	if (NULL==hBmpMono)
		return FALSE;

	hBmpMonoOrg=(HBITMAP)SelectObject(hDCMono,  hBmpMono);


	//Save the DC state before we munge on it.
	iSaveDC=SaveDC(hDC);

	/*
	 * Draw a button sans image.  This also fills crSys with the system
	 * colors for us which has space for five colors.  We don't use the
	 * fifth, the frame color, in this function.
	 */
	DrawBlankButton(hDC, x, y, dx, dy, (BOOL)(uState & BUTTONGROUP_DOWN), crSys);

	//Shift coordinates to account for the button's border
	x++;
	y++;
	dx-=2;
	dy-=2;

	/*
	 * Determine the offset necessary to center the image but also reflect
	 * the pushed-in state, which means just adding 1 to the up state.
	 */
	i=(uState & BUTTONGROUP_DOWN) ? 1 : 0;
	xOffsetGlyph=((dx-bmx) >> 1)+i;
	yOffsetGlyph=((dy-bmy) >> 1)+i;


	//Select the given image bitmap into the glyph DC before calling MaskCreate
	if (NULL!=hBmp)
		hBmpSave=(HBITMAP)SelectObject(hDCGlyphs, hBmp);


	/*
	 * Draw the face on the button.  If we have an up or [mouse]down
	 * button then we can just draw it as-is.  For indeterminate,
	 * disabled, or down disabled we have to gray the image and possibly
	 * add a white shadow to it (disabled/down disabled).
	 *
	 * Also note that for the intermediate state we first draw the normal
	 * up state, then proceed to add disabling looking highlights.
	 */

	//Up, mouse down, down, indeterminate
	if ((uState & BUTTONGROUP_ACTIVE) && !(uState & BUTTONGROUP_BLANK))
		{
		BOOL            fColorsSame=TRUE;

		/*
		 * In here we pay close attention to the system colors.  Where
		 * the source image is black, we paint COLOR_BTNTEXT.  Where
		 * light gray, we paint COLOR_BTNFACE.  Where dark gray we paint
		 * COLOR_BTNSHADOW, and where white we paint COLOR_BTNHILIGHT.
		 *
		 * The uColors variable contains flags to prevent color
		 * conversion.  To do a little optimization, we just do a
		 * single BitBlt if we're preserving all colors or if no colors
		 * are different than the standards, which is by far the most
		 * common case.  Otherwise, cycle through the four colors we can
		 * convert and do a BitBlt that converts it to the system color.
		 */

		//See what colors are different.
		for (i=STDCOLOR_BLACK; i<=STDCOLOR_WHITE; i++)
			fColorsSame &= (crSys[i]==crStandard[i]);

		if (PRESERVE_ALL==uColors || fColorsSame)
			{
			BitBlt(hDC, x+xOffsetGlyph, y+yOffsetGlyph, bmx, bmy
				   , hDCGlyphs, iImage*bmx, 0, SRCCOPY);
			}
		else
			{
			/*
			 * Cycle through hard-coded colors and create a mask that has all
			 * regions of that color in white and all other regions black.
			 * Then we select a pattern brush of the color to convert to:
			 * if we aren't converting the color then we use a brush of
			 * the standard hard-coded color, otherwise we use the actual
			 * system color.  The ROP_DSPDxax means that anything that's
			 * 1's in the mask get the pattern, anything that's 0 is unchanged
			 * in the destination.
			 *
			 * To prevent too many Blts to the screen, we use an intermediate
			 * bitmap and DC.
			 */

			hMemDC=CreateCompatibleDC(hDC);

			//Make sure conversion of monochrome to color stays B&W
			SetTextColor(hMemDC, 0L);                     //0's in mono -> 0
			SetBkColor(hMemDC, (COLORREF)0x00FFFFFF);     //1's in mono -> 1

			hBmpT=CreateCompatibleBitmap(hDC, bmx, bmy);
			SelectObject(hMemDC, hBmpT);

			//Copy the unmodified bitmap to the temporary bitmap
			BitBlt(hMemDC, 0, 0, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCCOPY);

			for (i=STDCOLOR_BLACK; i<=STDCOLOR_WHITE; i++)
				{
				//Convert pixels of the color to convert to 1's in the mask
				SetBkColor(hDCGlyphs, crStandard[i]);
				BitBlt(hDCMono, 0, 0, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCCOPY);

				//Preserve or modify the color depending on the flag.
				hBR=CreateSolidBrush((uColors & (1 << i))
									 ? crStandard[i] : crSys[i]);

				if (NULL!=hBR)
					{
					hObj=SelectObject(hMemDC, hBR);

					if (NULL!=hObj)
						{
						BitBlt(hMemDC, 0, 0, dx-1, dy-1, hDCMono, 0, 0, ROP_DSPDxax);
						SelectObject(hMemDC, hObj);
						}

					DeleteObject(hBR);
					}
				}

			//Now put the final version on the display and clean up
			BitBlt(hDC, x+xOffsetGlyph, y+yOffsetGlyph, dx-1, dy-1
				   , hMemDC, 0, 0, SRCCOPY);

			DeleteDC(hMemDC);
			DeleteObject(hBmpT);

			}
		}


	//Disabled and indeterminate states (unless we're blank)
	if ((uState & BUTTONGROUP_DISABLED || ATTRIBUTEBUTTON_INDETERMINATE==uState)
		&& !(uState & BUTTONGROUP_BLANK))
		{
		//Grayed state (up or down, no difference)
		MaskCreate(iImage, dx, dy, bmx, bmy, xOffsetGlyph, yOffsetGlyph, 0);

		//Make sure conversion of monochrome to color stays B&W
		SetTextColor(hDC, 0L);                     //0's in mono -> 0
		SetBkColor(hDC, (COLORREF)0x00FFFFFF);     //1's in mono -> 1

		//If we're disabled, up or down, draw the highlighted shadow.
		if (uState & BUTTONGROUP_DISABLED)
			{
			hBR=CreateSolidBrush(crSys[SYSCOLOR_HILIGHT]);

			if (NULL!=hBR)
				{
				hObj=SelectObject(hDC, hBR);

				if (NULL!=hObj)
					{
					//Draw hilight color where we have 0's in the mask
					BitBlt(hDC, x+1, y+1, dx-2, dy-2, hDCMono, 0, 0, ROP_PSDPxax);
					SelectObject(hDC, hObj);
					}
				DeleteObject(hBR);
				}
			}

		//Draw the gray image.
		hBR=CreateSolidBrush(crSys[SYSCOLOR_SHADOW]);

		if (NULL!=hBR)
			{
			hObj=SelectObject(hDC, hBR);

			if (NULL!=hObj)
				{
				//Draw the shadow color where we have 0's in the mask
				BitBlt(hDC, x, y, dx-2, dy-2, hDCMono, 0, 0, ROP_PSDPxax);
				SelectObject(hDC, hObj);
				}

			DeleteObject(hBR);
			}
		}

	//If the button is selected do the dither brush avoiding the glyph
	if (uState & BUTTONGROUP_LIGHTFACE)
		{
		HBRUSH      hBRDither;

		/*
		 * Get the dither brush.  This function will recreate it if
		 * necessary or return the global one if the colors already match.
		 */
		hBRDither=HBrushDitherCreate(crSys[SYSCOLOR_FACE], crSys[SYSCOLOR_HILIGHT]);
		hObj=SelectObject(hDC, hBRDither);

		if (NULL!=hObj)
			{
			/*
			 * The mask we create now determines where the dithering
			 * ends up.  In the down disabled state, we have to preserve
			 * the highlighted shadow, so the mask we create must have
			 * two masks of the original glyph, one of them offset by
			 * one pixel in both x & y.  For the indeterminate state,
			 * we have to mask all highlighted areas.  The state passed
			 * to MaskCreate matters here (we've used zero before).
			 */
			MaskCreate(iImage, dx, dy, bmx, bmy
					   , xOffsetGlyph-1, yOffsetGlyph-1, uState);

			//Convert monochrome masks to B&W color bitmap in the BitBlt.
			SetTextColor(hDC, 0L);
			SetBkColor(hDC, (COLORREF)0x00FFFFFF);

			/*
			 * Only draw the dither brush where the mask is 1's.  For
			 * the indeterminate state we have to not overdraw the
			 * shadow highlight so we use dx-3, dy-3 instead of dx-1
			 * and dy-1.  We do this whether or not we're blank.
			 */
			i=(ATTRIBUTEBUTTON_INDETERMINATE==uState
			   || BLANKBUTTON_INDETERMINATE==uState) ? 3 : 1;

			BitBlt(hDC, x+1, y+1, dx-i, dy-i, hDCMono, 0, 0, ROP_DSPDxax);
			SelectObject(hDC, hObj);
			}

		//DO NOT delete hBRDither!  It's a reference to a shared global.
		}

	//Cleanup hDCGlyphs:  Must do AFTER calling MaskCreate
	if (NULL!=hBmpSave)
		SelectObject(hDCGlyphs, hBmpSave);

	SelectObject(hDCMono,   hBmpMonoOrg);
	DeleteObject(hBmpMono);

	//Restore everything in the DC.
	RestoreDC(hDC, iSaveDC);
	return TRUE;
	}






/*
 * DrawBlankButton
 *
 * Purpose:
 *  Draws a button with no face using the current system colors in either
 *  an up or down state.
 *
 * Parameters:
 *  hDC             HDC on which to draw
 *  x, y            int coordinates where we start drawing
 *  dx,dy           int size of the button
 *  fDown           BOOL indicating the up or down state of the button
 *  pcr             COLORREF FAR * to five colors in which we store text,
 *                  shadow, face, highlight, and frame colors.  This is
 *                  a matter of convenience for the caller, since we have
 *                  to load these colors anyway we might as well send them
 *                  back.
 *
 * Return Value:
 *  None
 */

static void DrawBlankButton(HDC hDC, int x, int y, int dx, int dy
	, BOOL fDown, COLORREF FAR *pcr)
	{
	//Get the current system colors for buttons.
	pcr[0]=GetSysColor(COLOR_BTNTEXT);
	pcr[1]=GetSysColor(COLOR_BTNSHADOW);
	pcr[2]=GetSysColor(COLOR_BTNFACE);

	//Windows 3.0 doesn't support COLOR_BTNHIGHLIGHT, so leave it white.
	if (0x0300 < (UINT)GetVersion())
		pcr[3]=GetSysColor(COLOR_BTNHIGHLIGHT);
	else
		pcr[3]=crStandard[STDCOLOR_WHITE];

	pcr[4]=GetSysColor(COLOR_WINDOWFRAME);

	//Draw the border around the button.
	PatB(hDC, x+1,    y,      dx-2, 1,    pcr[4]);
	PatB(hDC, x+1,    y+dy-1, dx-2, 1,    pcr[4]);
	PatB(hDC, x,      y+1,    1,    dy-2, pcr[4]);
	PatB(hDC, x+dx-1, y+1,    1,    dy-2, pcr[4]);

	//Shift coordinates to account for the border we just drew
	x++;
	y++;
	dx-=2;
	dy-=2;

	//Paint the interior grey as a default.
	PatB(hDC, x, y, dx, dy, pcr[2]);

	/*
	 * Draw shadows and highlights.  The DOWN grouping that contains
	 * down, mouse down, and down disabled are drawn depressed.  Up,
	 * indeterminate, and disabled are drawn up.
	 */

	if (fDown)
		{
		PatB(hDC, x, y, 1,  dy, pcr[1]);
		PatB(hDC, x, y, dx, 1,  pcr[1]);
		}
	else
		{
		//Normal button look.
		PatB(hDC, x, y, 1,    dy-1, pcr[3]);
		PatB(hDC, x, y, dx-1, 1,    pcr[3]);

		PatB(hDC, x+dx-1, y,      1,  dy, pcr[1]);
		PatB(hDC, x,      y+dy-1, dx, 1,  pcr[1]);

		PatB(hDC, x+1+dx-3, y+1,    1,    dy-2, pcr[1]);
		PatB(hDC, x+1,      y+dy-2, dx-2, 1,    pcr[1]);
		}

	return;
	}






/*
 * PatB
 * Internal
 *
 * Purpose:
 *  A more convenient PatBlt operation for drawing button borders and
 *  highlights.
 *
 * Parameters:
 *  hDC             HDC on which to paint.
 *  x, y            int coordinates at which to paint.
 *  dx, dy          int dimensions of rectangle to paint.
 *  rgb             COLORREF to use as the background color.
 *
 * Return Value:
 *  None
 */

static void PatB(HDC hDC, int x, int y, int dx, int dy, COLORREF rgb)
	{
	RECT        rc;

	SetBkColor(hDC, rgb);
	SetRect(&rc, x, y, x+dx, y+dy);
	ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
	}




/*
 * MaskCreate
 * Internal
 *
 * Purpose:
 *  Creates a monochrome mask bitmap of the given image at the given offset
 *  in the global hDCMono.  Anywhere in the image that you have the light
 *  gray (STDCOLOR_LTGRAY) or the white highlight (STDCOLOR_WHITE) you get
 *  get 1's.  All other pixels are 0's
 *
 * Parameters:
 *  iImage          UINT index of the image for which to create a mask.
 *  dx, dy          int dimensions of the button.
 *  bmx, bmy        int dimensions of the bitmap to use.
 *  xOffset         int offset for x inside hDCMono where we paint.
 *  yOffset         int offset for y inside hDCMono where we paint.
 *  uState          UINT state of the image.  Special cases are made
 *                  for ATTRIBUTEBUTTON_DOWNDISABLED and
 *                  ATTRIBUTEBUTTON_INDETERMINATE.  In any case where you
 *                  do not want a special case, pass zero here, regardless
 *                  of the true button state.
 *
 * Return Value:
 *  None
 */

static void MaskCreate(UINT iImage, int dx, int dy, int bmx, int bmy
	,int xOffset, int yOffset, UINT uState)
	{
	//Initalize whole area with zeros
	PatBlt(hDCMono, 0, 0, dx, dy, WHITENESS);

	if (uState & BUTTONGROUP_BLANK)
		return;

	//Convert face colored pixels to 1's. all others to black.
	SetBkColor(hDCGlyphs, crStandard[STDCOLOR_LTGRAY]);
	BitBlt(hDCMono, xOffset, yOffset, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCCOPY);

	//In the indeterminate state, don't turn highlight's to 1's. Leave black.
	if (ATTRIBUTEBUTTON_INDETERMINATE!=uState)
		{
		//Convert highlight colored pixels to 1's and OR them with the previous.
		SetBkColor(hDCGlyphs, crStandard[STDCOLOR_WHITE]);
		BitBlt(hDCMono, xOffset, yOffset, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCPAINT);
		}

	/*
	 * For the down disabled state, AND this same mask with itself at an
	 * offset of 1, which accounts for the highlight shadow.
	 */
	if (ATTRIBUTEBUTTON_DOWNDISABLED==uState)
		BitBlt(hDCMono, 1, 1, dx-1, dy-1, hDCMono,  0, 0, SRCAND);

	return;
	}
