/* * BTTNCUR.C * Buttons & Cursors Version 1.1, Win32 version August 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 #include #include #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; // Common clean up code void FAR PASCAL WEP(int bSystemExit); //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: * Entry point conditionally compiled for Windows NT and Windows * 3.1. Provides the proper structure for each environment * and calls InternalLibMain for real initialization. */ #ifdef WIN32 BOOL _cdecl LibMain( HINSTANCE hDll, DWORD dwReason, LPVOID lpvReserved) { if (DLL_PROCESS_ATTACH == dwReason) { return FInitialize(hDll); } else if (DLL_PROCESS_DETACH == dwReason) { WEP(0); } else { return TRUE; } } #else HANDLE FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg , WORD cbHeapSize, LPSTR lpCmdLine) { //Perform global initialization. if (FInitialize(hInstance)) { if (0!=cbHeapSize) UnlockData(0); } return hInstance; } #endif /* * FInitialize * * Purpose: * Initialization function for the DLL. * * Parameters: * hInstance HANDLE instance of the DLL. * * Return Value: * BOOL TRUE if the function was successful, FALSE otherwise. */ BOOL FInitialize(HANDLE hInstance) { UINT i; /* * To remain backwards compatible with 1.0 we'll default to 96DPI * like we forced in the older version. If the application calls * UIToolButtonDraw we use the values here. If the application * calls UIToolButtonDrawTDD then we use the pointer to the * application-provided TOOLDISPLAYDATA structure. */ 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; } ghInst=hInstance; //Perform global initialization. if (ToolButtonInit()) { CursorsCache(hInstance); return TRUE; } return FALSE; } /* * 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. */ #ifdef DEBUG OutputDebugString("BTTNCUR.DLL: WEP Entry\r\n"); OutputDebugString("BTTNCUR.DLL: The two DC's, the brush, and the three\r\n"); OutputDebugString("BTTNCUR.DLL: bitmaps that Debug Windows shows\r\n"); OutputDebugString("BTTNCUR.DLL: above were detected BEFORE this WEP\r\n"); OutputDebugString("BTTNCUR.DLL: had a chance to do it! NOT A BUG!\r\n"); #endif CursorsFree(); ToolButtonFree(); #ifdef DEBUG OutputDebugString("BTTNCUR.DLL: WEP Exit\r\n"); #endif 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; }