Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1070 lines
33 KiB

  1. /*
  2. * BTTNCUR.C
  3. * Buttons & Cursors Version 1.1, Win32 version August 1993
  4. *
  5. * Public functions to generate different states of toolbar buttons from
  6. * a single bitmap. States are normal, pressed, checked, and disabled.
  7. *
  8. * Copyright (c)1992-1993 Microsoft Corporation, All Rights Reserved,
  9. * as applied to redistribution of this source code in source form
  10. * License is granted to use of compiled code in shipped binaries.
  11. */
  12. #ifdef WIN32
  13. #define _INC_OLE
  14. #define __RPC_H__
  15. #endif
  16. #include <windows.h>
  17. #include <memory.h>
  18. #include "bttncur.h"
  19. #include "bttncuri.h"
  20. //Display sensitive information
  21. TOOLDISPLAYDATA tdd;
  22. //Library instance
  23. HINSTANCE ghInst;
  24. //Cache GDI objects to speed drawing.
  25. HDC hDCGlyphs = NULL;
  26. HDC hDCMono = NULL;
  27. HBRUSH hBrushDither = NULL;
  28. // Common clean up code
  29. void FAR PASCAL WEP(int bSystemExit);
  30. //Standard images to use in case caller doesn't provide them
  31. HBITMAP rghBmpStandardImages[3];
  32. //Standard button colors.
  33. const COLORREF crStandard[4]={ RGB(0, 0, 0) //STDCOLOR_BLACK
  34. , RGB(128, 128, 128) //STDCOLOR_DKGRAY
  35. , RGB(192, 192, 192) //STDCOLOR_LTGRAY
  36. , RGB(255, 255, 255)}; //STDCOLOR_WHITE
  37. /*
  38. * Mapping from image identifier to button type (command/attribute).
  39. * Version 1.00 of this DLL has no attribute images defined, so
  40. * the code will only support three states for each command
  41. * button. Any state is, however, valid for an application
  42. * defined image.
  43. */
  44. UINT mpButtonType[TOOLIMAGE_MAX-TOOLIMAGE_MIN+1]=
  45. {
  46. BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND,
  47. BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND,
  48. BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND, BUTTONTYPE_COMMAND
  49. };
  50. /*
  51. * LibMain
  52. *
  53. * Purpose:
  54. * Entry point conditionally compiled for Windows NT and Windows
  55. * 3.1. Provides the proper structure for each environment
  56. * and calls InternalLibMain for real initialization.
  57. */
  58. #ifdef WIN32
  59. BOOL _cdecl LibMain(
  60. HINSTANCE hDll,
  61. DWORD dwReason,
  62. LPVOID lpvReserved)
  63. {
  64. if (DLL_PROCESS_ATTACH == dwReason)
  65. {
  66. return FInitialize(hDll);
  67. }
  68. else if (DLL_PROCESS_DETACH == dwReason)
  69. {
  70. WEP(0);
  71. }
  72. else
  73. {
  74. return TRUE;
  75. }
  76. }
  77. #else
  78. HANDLE FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg
  79. , WORD cbHeapSize, LPSTR lpCmdLine)
  80. {
  81. //Perform global initialization.
  82. if (FInitialize(hInstance))
  83. {
  84. if (0!=cbHeapSize)
  85. UnlockData(0);
  86. }
  87. return hInstance;
  88. }
  89. #endif
  90. /*
  91. * FInitialize
  92. *
  93. * Purpose:
  94. * Initialization function for the DLL.
  95. *
  96. * Parameters:
  97. * hInstance HANDLE instance of the DLL.
  98. *
  99. * Return Value:
  100. * BOOL TRUE if the function was successful, FALSE otherwise.
  101. */
  102. BOOL FInitialize(HANDLE hInstance)
  103. {
  104. UINT i;
  105. /*
  106. * To remain backwards compatible with 1.0 we'll default to 96DPI
  107. * like we forced in the older version. If the application calls
  108. * UIToolButtonDraw we use the values here. If the application
  109. * calls UIToolButtonDrawTDD then we use the pointer to the
  110. * application-provided TOOLDISPLAYDATA structure.
  111. */
  112. tdd.uDPI =96;
  113. tdd.cyBar =CYBUTTONBAR96;
  114. tdd.cxButton =TOOLBUTTON_STD96WIDTH;
  115. tdd.cyButton =TOOLBUTTON_STD96HEIGHT;
  116. tdd.cxImage =TOOLBUTTON_STD96IMAGEWIDTH;
  117. tdd.cyImage =TOOLBUTTON_STD96IMAGEHEIGHT;
  118. tdd.uIDImages=IDB_STANDARDIMAGES96;
  119. for (i=0; i < 3; i++)
  120. {
  121. rghBmpStandardImages[i]=LoadBitmap(hInstance
  122. , MAKEINTRESOURCE(IDB_STANDARDIMAGESMIN+i));
  123. if (NULL==rghBmpStandardImages[i])
  124. return FALSE;
  125. }
  126. ghInst=hInstance;
  127. //Perform global initialization.
  128. if (ToolButtonInit())
  129. {
  130. CursorsCache(hInstance);
  131. return TRUE;
  132. }
  133. return FALSE;
  134. }
  135. /*
  136. * WEP
  137. *
  138. * Purpose:
  139. * Required DLL Exit function. Does nothing.
  140. *
  141. * Parameters:
  142. * bSystemExit BOOL indicating if the system is being shut
  143. * down or the DLL has just been unloaded.
  144. *
  145. * Return Value:
  146. * void
  147. *
  148. */
  149. void FAR PASCAL WEP(int bSystemExit)
  150. {
  151. /*
  152. * **Developers: Note that WEP is called AFTER Windows does any
  153. * automatic task cleanup. You may see warnings for
  154. * that two DCs, a bitmap, and a brush, were not
  155. * deleted before task termination. THIS IS NOT A
  156. * PROBLEM WITH THIS CODE AND IT IS NOT A BUG. This
  157. * WEP function is properly called and performs the
  158. * cleanup as appropriate. The fact that Windows is
  159. * calling WEP after checking task cleanup is not
  160. * something we can control. Just to prove it, the
  161. * OutputDebugStrings in this and ToolButtonFree
  162. * show that the code is exercised.
  163. */
  164. #ifdef DEBUG
  165. OutputDebugString("BTTNCUR.DLL: WEP Entry\r\n");
  166. OutputDebugString("BTTNCUR.DLL: The two DC's, the brush, and the three\r\n");
  167. OutputDebugString("BTTNCUR.DLL: bitmaps that Debug Windows shows\r\n");
  168. OutputDebugString("BTTNCUR.DLL: above were detected BEFORE this WEP\r\n");
  169. OutputDebugString("BTTNCUR.DLL: had a chance to do it! NOT A BUG!\r\n");
  170. #endif
  171. CursorsFree();
  172. ToolButtonFree();
  173. #ifdef DEBUG
  174. OutputDebugString("BTTNCUR.DLL: WEP Exit\r\n");
  175. #endif
  176. return;
  177. }
  178. /*
  179. * UIToolConfigureForDisplay
  180. * Public API
  181. *
  182. * Purpose:
  183. * Initializes the library to scale button images for the display type.
  184. * Without calling this function the library defaults to 96 DPI (VGA).
  185. * By calling this function an application acknowledges that it must
  186. * use the data returned from this function to configure itself for
  187. * the display.
  188. *
  189. * Parameters:
  190. * lpDD LPTOOLDISPLAYDATA to fill with the display-sensitive
  191. * size values.
  192. *
  193. * Return Value:
  194. * BOOL TRUE if the sizes were obtained, FALSE otherwise.
  195. */
  196. BOOL WINAPI UIToolConfigureForDisplay(LPTOOLDISPLAYDATA lpDD)
  197. {
  198. int cy;
  199. HDC hDC;
  200. if (NULL==lpDD || IsBadWritePtr(lpDD, sizeof(TOOLDISPLAYDATA)))
  201. return FALSE;
  202. /*
  203. * Determine the aspect ratio of the display we're currently
  204. * running on and calculate the necessary information.
  205. *
  206. * By retrieving the logical Y extent of the display driver, you
  207. * only have limited possibilities:
  208. * LOGPIXELSY Display
  209. * ----------------------------------------
  210. * 48 CGA (unsupported)
  211. * 72 EGA
  212. * 96 VGA
  213. * 120 8514/a (i.e. HiRes VGA)
  214. */
  215. hDC=GetDC(NULL);
  216. if (NULL==hDC)
  217. return FALSE;
  218. cy=GetDeviceCaps(hDC, LOGPIXELSY);
  219. ReleaseDC(NULL, hDC);
  220. /*
  221. * Instead of single comparisons, check ranges instead, so in case
  222. * we get something funky, we'll act reasonable.
  223. */
  224. if (72 >=cy)
  225. {
  226. lpDD->uDPI =72;
  227. lpDD->cyBar =CYBUTTONBAR72;
  228. lpDD->cxButton =TOOLBUTTON_STD72WIDTH;
  229. lpDD->cyButton =TOOLBUTTON_STD72HEIGHT;
  230. lpDD->cxImage =TOOLBUTTON_STD72IMAGEWIDTH;
  231. lpDD->cyImage =TOOLBUTTON_STD72IMAGEHEIGHT;
  232. lpDD->uIDImages=IDB_STANDARDIMAGES72;
  233. }
  234. else
  235. {
  236. if (72 < cy && 120 > cy)
  237. {
  238. lpDD->uDPI =96;
  239. lpDD->cyBar =CYBUTTONBAR96;
  240. lpDD->cxButton =TOOLBUTTON_STD96WIDTH;
  241. lpDD->cyButton =TOOLBUTTON_STD96HEIGHT;
  242. lpDD->cxImage =TOOLBUTTON_STD96IMAGEWIDTH;
  243. lpDD->cyImage =TOOLBUTTON_STD96IMAGEHEIGHT;
  244. lpDD->uIDImages=IDB_STANDARDIMAGES96;
  245. }
  246. else
  247. {
  248. lpDD->uDPI =120;
  249. lpDD->cyBar =CYBUTTONBAR120;
  250. lpDD->cxButton =TOOLBUTTON_STD120WIDTH;
  251. lpDD->cyButton =TOOLBUTTON_STD120HEIGHT;
  252. lpDD->cxImage =TOOLBUTTON_STD120IMAGEWIDTH;
  253. lpDD->cyImage =TOOLBUTTON_STD120IMAGEHEIGHT;
  254. lpDD->uIDImages=IDB_STANDARDIMAGES120;
  255. }
  256. }
  257. return TRUE;
  258. }
  259. /*
  260. * ToolButtonInit
  261. * Internal
  262. *
  263. * Purpose:
  264. * Initializes GDI objects for drawing images through UIToolButtonDraw.
  265. * If the function fails, the function has already performed proper
  266. * cleanup.
  267. *
  268. * Parameters:
  269. * None
  270. *
  271. * Return Value:
  272. * BOOL TRUE if initialization succeeded. FALSE otherwise.
  273. */
  274. static BOOL ToolButtonInit(void)
  275. {
  276. COLORREF rgbHi;
  277. //DC for BitBltting the image (the glyph)
  278. hDCGlyphs=CreateCompatibleDC(NULL);
  279. //Create a monochrome DC and a brush for doing pattern dithering.
  280. hDCMono=CreateCompatibleDC(NULL);
  281. //Windows 3.0 doesn't support COLOR_BTNHIGHLIGHT, so leave it white.
  282. if (0x0300 < (UINT)GetVersion())
  283. rgbHi=GetSysColor(COLOR_BTNHIGHLIGHT);
  284. else
  285. rgbHi=crStandard[STDCOLOR_WHITE];
  286. hBrushDither=HBrushDitherCreate(GetSysColor(COLOR_BTNFACE), rgbHi);
  287. if (NULL==hDCGlyphs || NULL==hDCMono || NULL==hBrushDither)
  288. {
  289. //On failure, cleanup whatever might have been allocated.
  290. ToolButtonFree();
  291. return FALSE;
  292. }
  293. return TRUE;
  294. }
  295. /*
  296. * ToolButtonFree
  297. * Internal
  298. *
  299. * Purpose:
  300. * Free all GDI allocations made during initialization. Note that the
  301. * DEBUG output included here shows that WEP is called and cleanup actually
  302. * occurs. However, if you watch debug output in DBWIN or on a terminal,
  303. * the debugging version of Windows does automatic app cleanup before WEP
  304. * is called, leading some to believe that this code is buggy. The
  305. * debug output below shows that we do perform all necessary cleanup.
  306. *
  307. * Parameters:
  308. * None
  309. *
  310. * Return Value:
  311. * None
  312. */
  313. static void ToolButtonFree(void)
  314. {
  315. UINT i;
  316. if (NULL!=hDCMono)
  317. DeleteDC(hDCMono);
  318. hDCMono=NULL;
  319. if (NULL!=hDCGlyphs)
  320. DeleteDC(hDCGlyphs);
  321. hDCGlyphs=NULL;
  322. if (NULL!=hBrushDither)
  323. DeleteObject(hBrushDither);
  324. hBrushDither=NULL;
  325. for (i=0; i < 3; i++)
  326. {
  327. if (NULL!=rghBmpStandardImages[i])
  328. DeleteObject(rghBmpStandardImages[i]);
  329. rghBmpStandardImages[i]=NULL;
  330. }
  331. return;
  332. }
  333. /*
  334. * HBrushDitherCreate
  335. * Internal
  336. *
  337. * Purpose:
  338. * Creates and returns a handle to a pattern brush created from
  339. * an 8*8 monochrome pattern bitmap. We use the button face and
  340. * highlight colors to indicate the resulting colors of a PatBlt
  341. * using this brush.
  342. *
  343. * Parameters:
  344. * rgbFace COLORREF of the button face color.
  345. * rgbHilight COLORREF of the button highlight color.
  346. *
  347. * Return Value:
  348. * HBITMAP Handle to the dither bitmap.
  349. */
  350. static HBRUSH HBrushDitherCreate(COLORREF rgbFace, COLORREF rgbHilight)
  351. {
  352. struct //BITMAPINFO with 16 colors
  353. {
  354. BITMAPINFOHEADER bmiHeader;
  355. RGBQUAD bmiColors[16];
  356. } bmi;
  357. HBRUSH hBrush=NULL;
  358. DWORD patGray[8];
  359. HDC hDC;
  360. HBITMAP hBmp;
  361. static COLORREF rgbFaceOld =0xFFFFFFFF; //Initially an impossible color
  362. static COLORREF rgbHilightOld=0xFFFFFFFF; //so at first we always create
  363. /*
  364. * If the colors haven't changed from last time, just return the
  365. * existing brush.
  366. */
  367. if (rgbFace==rgbFaceOld && rgbHilight==rgbHilightOld)
  368. return hBrushDither;
  369. rgbFaceOld=rgbFace;
  370. rgbHilightOld=rgbHilight;
  371. /*
  372. * We're going to create an 8*8 brush for PatBlt using the
  373. * button face color and button highlight color. We use this
  374. * brush to affect the pressed state and the disabled state.
  375. */
  376. bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  377. bmi.bmiHeader.biWidth = 8;
  378. bmi.bmiHeader.biHeight = 8;
  379. bmi.bmiHeader.biPlanes = 1;
  380. bmi.bmiHeader.biBitCount = 1;
  381. bmi.bmiHeader.biCompression = BI_RGB;
  382. bmi.bmiHeader.biSizeImage = 0;
  383. bmi.bmiHeader.biXPelsPerMeter= 0;
  384. bmi.bmiHeader.biYPelsPerMeter= 0;
  385. bmi.bmiHeader.biClrUsed = 0;
  386. bmi.bmiHeader.biClrImportant = 0;
  387. bmi.bmiColors[0].rgbBlue = GetBValue(rgbFace);
  388. bmi.bmiColors[0].rgbGreen = GetGValue(rgbFace);
  389. bmi.bmiColors[0].rgbRed = GetRValue(rgbFace);
  390. bmi.bmiColors[0].rgbReserved = 0;
  391. bmi.bmiColors[1].rgbBlue = GetBValue(rgbHilight);
  392. bmi.bmiColors[1].rgbGreen = GetGValue(rgbHilight);
  393. bmi.bmiColors[1].rgbRed = GetRValue(rgbHilight);
  394. bmi.bmiColors[1].rgbReserved = 0;
  395. //Create the byte array for CreateDIBitmap.
  396. patGray[6]=patGray[4]=patGray[2]=patGray[0]=0x5555AAAAL;
  397. patGray[7]=patGray[5]=patGray[3]=patGray[1]=0xAAAA5555L;
  398. //Create the bitmap
  399. hDC=GetDC(NULL);
  400. hBmp=CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT, patGray
  401. , (LPBITMAPINFO)&bmi, DIB_RGB_COLORS);
  402. ReleaseDC(NULL, hDC);
  403. //Create the brush from the bitmap
  404. if (NULL!=hBmp)
  405. {
  406. hBrush=CreatePatternBrush(hBmp);
  407. DeleteObject(hBmp);
  408. }
  409. /*
  410. * If we could recreate a brush, clean up and make it the current
  411. * pattern. Otherwise the best we can do it return the old one,
  412. * which will be colored wrong, but at least it works.
  413. */
  414. if (NULL!=hBrush)
  415. {
  416. if (NULL!=hBrushDither)
  417. DeleteObject(hBrushDither);
  418. hBrushDither=hBrush;
  419. }
  420. return hBrushDither;
  421. }
  422. /*
  423. * UIToolButtonDraw
  424. * Public API
  425. *
  426. * Purpose:
  427. * Draws the complete image of a toolbar-style button with a given
  428. * image in the center and in a specific state. The button is drawn
  429. * on a specified hDC at a given location, so this function is useful
  430. * on standard owner-draw buttons as well as on toolbar controls that
  431. * have only one window but show images of multiple buttons.
  432. *
  433. * Parameters:
  434. * hDC HDC on which to draw.
  435. * x, y int coordinates at which to draw.
  436. * dx, dy int dimensions of the *button*, not necessarily the image.
  437. * hBmp HBITMAP from which to draw the image.
  438. * bmx, bmy int dimensions of each bitmap in hBmp. If hBmp is NULL
  439. * then these are forced to the standard sizes.
  440. * iImage int index to the image to draw in the button
  441. * uStateIn UINT containing the state index for the button and the
  442. * color control bits.
  443. *
  444. * Return Value:
  445. * BOOL TRUE if drawing succeeded, FALSE otherwise meaning that
  446. * hDC is NULL or hBmp is NULL and iImage is not a valid
  447. * index for a standard image.
  448. */
  449. BOOL WINAPI UIToolButtonDraw(HDC hDC, int x, int y, int dx, int dy
  450. , HBITMAP hBmp, int bmx, int bmy, int iImage, UINT uStateIn)
  451. {
  452. return UIToolButtonDrawTDD(hDC, x, y, dx, dy, hBmp, bmx, bmy, iImage
  453. , uStateIn, &tdd);
  454. }
  455. /*
  456. * UIToolButtonDrawTDD
  457. * Public API
  458. *
  459. * Purpose:
  460. * Draws the complete image of a toolbar-style button with a given
  461. * image in the center and in a specific state. The button is drawn
  462. * on a specified hDC at a given location, so this function is useful
  463. * on standard owner-draw buttons as well as on toolbar controls that
  464. * have only one window but show images of multiple buttons.
  465. *
  466. * This is the same as UIToolButtonDraw but adds the pTDD configuration
  467. * structure. UIToolButtonDraw calls us with that pointing to the
  468. * default 96dpi structure.
  469. *
  470. * Parameters:
  471. * hDC HDC on which to draw.
  472. * x, y int coordinates at which to draw.
  473. * dx, dy int dimensions of the *button*, not necessarily the image.
  474. * hBmp HBITMAP from which to draw the image.
  475. * bmx, bmy int dimensions of each bitmap in hBmp. If hBmp is NULL
  476. * then these are forced to the standard sizes.
  477. * iImage int index to the image to draw in the button
  478. * uStateIn UINT containing the state index for the button and the
  479. * color control bits.
  480. * pTDD LPTOOLDISPLAYDATA containing display configuration.
  481. * Can be NULL if hBmp is non-NULL.
  482. *
  483. * Return Value:
  484. * BOOL TRUE if drawing succeeded, FALSE otherwise meaning that
  485. * hDC is NULL or hBmp is NULL and iImage is not a valid
  486. * index for a standard image.
  487. */
  488. BOOL WINAPI UIToolButtonDrawTDD(HDC hDC, int x, int y, int dx, int dy
  489. , HBITMAP hBmp, int bmx, int bmy, int iImage, UINT uStateIn
  490. , LPTOOLDISPLAYDATA pTDD)
  491. {
  492. static COLORREF crSys[5]; //Avoid stack arrays in DLLs: use static
  493. UINT uState=(UINT)LOBYTE((WORD)uStateIn);
  494. UINT uColors=(UINT)HIBYTE((WORD)uStateIn & PRESERVE_ALL);
  495. int xOffsetGlyph, yOffsetGlyph;
  496. int i, iSaveDC;
  497. HDC hMemDC;
  498. HGDIOBJ hObj;
  499. HBRUSH hBR;
  500. HBITMAP hBmpT;
  501. HBITMAP hBmpMono;
  502. HBITMAP hBmpMonoOrg;
  503. HBITMAP hBmpSave=NULL;
  504. if (NULL==hDC)
  505. return FALSE;
  506. /*
  507. * If we're given no image bitmap, then use the standard and validate the
  508. * image index. We also enforce the standard bitmap size and the size of
  509. * the button (as requested by User Interface designers).
  510. */
  511. if (NULL==hBmp && !(uState & BUTTONGROUP_BLANK))
  512. {
  513. hBmp=rghBmpStandardImages[pTDD->uIDImages-IDB_STANDARDIMAGESMIN];
  514. bmx=pTDD->cxImage; //Force bitmap dimensions
  515. bmy=pTDD->cyImage;
  516. dx=pTDD->cxButton; //Force button dimensions
  517. dy=pTDD->cyButton;
  518. if (iImage > TOOLIMAGE_MAX)
  519. return FALSE;
  520. /*
  521. * If we are using a standard command button, verify that the state
  522. * does not contain the LIGHTFACE group which only applies to
  523. * attribute buttons.
  524. */
  525. if (BUTTONTYPE_COMMAND==mpButtonType[iImage]
  526. && (uState & BUTTONGROUP_LIGHTFACE))
  527. return FALSE;
  528. }
  529. //Create a dithered bitmap.
  530. hBmpMono=CreateBitmap(dx-2, dy-2, 1, 1, NULL);
  531. if (NULL==hBmpMono)
  532. return FALSE;
  533. hBmpMonoOrg=(HBITMAP)SelectObject(hDCMono, hBmpMono);
  534. //Save the DC state before we munge on it.
  535. iSaveDC=SaveDC(hDC);
  536. /*
  537. * Draw a button sans image. This also fills crSys with the system
  538. * colors for us which has space for five colors. We don't use the
  539. * fifth, the frame color, in this function.
  540. */
  541. DrawBlankButton(hDC, x, y, dx, dy, (BOOL)(uState & BUTTONGROUP_DOWN), crSys);
  542. //Shift coordinates to account for the button's border
  543. x++;
  544. y++;
  545. dx-=2;
  546. dy-=2;
  547. /*
  548. * Determine the offset necessary to center the image but also reflect
  549. * the pushed-in state, which means just adding 1 to the up state.
  550. */
  551. i=(uState & BUTTONGROUP_DOWN) ? 1 : 0;
  552. xOffsetGlyph=((dx-bmx) >> 1)+i;
  553. yOffsetGlyph=((dy-bmy) >> 1)+i;
  554. //Select the given image bitmap into the glyph DC before calling MaskCreate
  555. if (NULL!=hBmp)
  556. hBmpSave=(HBITMAP)SelectObject(hDCGlyphs, hBmp);
  557. /*
  558. * Draw the face on the button. If we have an up or [mouse]down
  559. * button then we can just draw it as-is. For indeterminate,
  560. * disabled, or down disabled we have to gray the image and possibly
  561. * add a white shadow to it (disabled/down disabled).
  562. *
  563. * Also note that for the intermediate state we first draw the normal
  564. * up state, then proceed to add disabling looking highlights.
  565. */
  566. //Up, mouse down, down, indeterminate
  567. if ((uState & BUTTONGROUP_ACTIVE) && !(uState & BUTTONGROUP_BLANK))
  568. {
  569. BOOL fColorsSame=TRUE;
  570. /*
  571. * In here we pay close attention to the system colors. Where
  572. * the source image is black, we paint COLOR_BTNTEXT. Where
  573. * light gray, we paint COLOR_BTNFACE. Where dark gray we paint
  574. * COLOR_BTNSHADOW, and where white we paint COLOR_BTNHILIGHT.
  575. *
  576. * The uColors variable contains flags to prevent color
  577. * conversion. To do a little optimization, we just do a
  578. * single BitBlt if we're preserving all colors or if no colors
  579. * are different than the standards, which is by far the most
  580. * common case. Otherwise, cycle through the four colors we can
  581. * convert and do a BitBlt that converts it to the system color.
  582. */
  583. //See what colors are different.
  584. for (i=STDCOLOR_BLACK; i<=STDCOLOR_WHITE; i++)
  585. fColorsSame &= (crSys[i]==crStandard[i]);
  586. if (PRESERVE_ALL==uColors || fColorsSame)
  587. {
  588. BitBlt(hDC, x+xOffsetGlyph, y+yOffsetGlyph, bmx, bmy
  589. , hDCGlyphs, iImage*bmx, 0, SRCCOPY);
  590. }
  591. else
  592. {
  593. /*
  594. * Cycle through hard-coded colors and create a mask that has all
  595. * regions of that color in white and all other regions black.
  596. * Then we select a pattern brush of the color to convert to:
  597. * if we aren't converting the color then we use a brush of
  598. * the standard hard-coded color, otherwise we use the actual
  599. * system color. The ROP_DSPDxax means that anything that's
  600. * 1's in the mask get the pattern, anything that's 0 is unchanged
  601. * in the destination.
  602. *
  603. * To prevent too many Blts to the screen, we use an intermediate
  604. * bitmap and DC.
  605. */
  606. hMemDC=CreateCompatibleDC(hDC);
  607. //Make sure conversion of monochrome to color stays B&W
  608. SetTextColor(hMemDC, 0L); //0's in mono -> 0
  609. SetBkColor(hMemDC, (COLORREF)0x00FFFFFF); //1's in mono -> 1
  610. hBmpT=CreateCompatibleBitmap(hDC, bmx, bmy);
  611. SelectObject(hMemDC, hBmpT);
  612. //Copy the unmodified bitmap to the temporary bitmap
  613. BitBlt(hMemDC, 0, 0, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCCOPY);
  614. for (i=STDCOLOR_BLACK; i<=STDCOLOR_WHITE; i++)
  615. {
  616. //Convert pixels of the color to convert to 1's in the mask
  617. SetBkColor(hDCGlyphs, crStandard[i]);
  618. BitBlt(hDCMono, 0, 0, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCCOPY);
  619. //Preserve or modify the color depending on the flag.
  620. hBR=CreateSolidBrush((uColors & (1 << i))
  621. ? crStandard[i] : crSys[i]);
  622. if (NULL!=hBR)
  623. {
  624. hObj=SelectObject(hMemDC, hBR);
  625. if (NULL!=hObj)
  626. {
  627. BitBlt(hMemDC, 0, 0, dx-1, dy-1, hDCMono, 0, 0, ROP_DSPDxax);
  628. SelectObject(hMemDC, hObj);
  629. }
  630. DeleteObject(hBR);
  631. }
  632. }
  633. //Now put the final version on the display and clean up
  634. BitBlt(hDC, x+xOffsetGlyph, y+yOffsetGlyph, dx-1, dy-1
  635. , hMemDC, 0, 0, SRCCOPY);
  636. DeleteDC(hMemDC);
  637. DeleteObject(hBmpT);
  638. }
  639. }
  640. //Disabled and indeterminate states (unless we're blank)
  641. if ((uState & BUTTONGROUP_DISABLED || ATTRIBUTEBUTTON_INDETERMINATE==uState)
  642. && !(uState & BUTTONGROUP_BLANK))
  643. {
  644. //Grayed state (up or down, no difference)
  645. MaskCreate(iImage, dx, dy, bmx, bmy, xOffsetGlyph, yOffsetGlyph, 0);
  646. //Make sure conversion of monochrome to color stays B&W
  647. SetTextColor(hDC, 0L); //0's in mono -> 0
  648. SetBkColor(hDC, (COLORREF)0x00FFFFFF); //1's in mono -> 1
  649. //If we're disabled, up or down, draw the highlighted shadow.
  650. if (uState & BUTTONGROUP_DISABLED)
  651. {
  652. hBR=CreateSolidBrush(crSys[SYSCOLOR_HILIGHT]);
  653. if (NULL!=hBR)
  654. {
  655. hObj=SelectObject(hDC, hBR);
  656. if (NULL!=hObj)
  657. {
  658. //Draw hilight color where we have 0's in the mask
  659. BitBlt(hDC, x+1, y+1, dx-2, dy-2, hDCMono, 0, 0, ROP_PSDPxax);
  660. SelectObject(hDC, hObj);
  661. }
  662. DeleteObject(hBR);
  663. }
  664. }
  665. //Draw the gray image.
  666. hBR=CreateSolidBrush(crSys[SYSCOLOR_SHADOW]);
  667. if (NULL!=hBR)
  668. {
  669. hObj=SelectObject(hDC, hBR);
  670. if (NULL!=hObj)
  671. {
  672. //Draw the shadow color where we have 0's in the mask
  673. BitBlt(hDC, x, y, dx-2, dy-2, hDCMono, 0, 0, ROP_PSDPxax);
  674. SelectObject(hDC, hObj);
  675. }
  676. DeleteObject(hBR);
  677. }
  678. }
  679. //If the button is selected do the dither brush avoiding the glyph
  680. if (uState & BUTTONGROUP_LIGHTFACE)
  681. {
  682. HBRUSH hBRDither;
  683. /*
  684. * Get the dither brush. This function will recreate it if
  685. * necessary or return the global one if the colors already match.
  686. */
  687. hBRDither=HBrushDitherCreate(crSys[SYSCOLOR_FACE], crSys[SYSCOLOR_HILIGHT]);
  688. hObj=SelectObject(hDC, hBRDither);
  689. if (NULL!=hObj)
  690. {
  691. /*
  692. * The mask we create now determines where the dithering
  693. * ends up. In the down disabled state, we have to preserve
  694. * the highlighted shadow, so the mask we create must have
  695. * two masks of the original glyph, one of them offset by
  696. * one pixel in both x & y. For the indeterminate state,
  697. * we have to mask all highlighted areas. The state passed
  698. * to MaskCreate matters here (we've used zero before).
  699. */
  700. MaskCreate(iImage, dx, dy, bmx, bmy
  701. , xOffsetGlyph-1, yOffsetGlyph-1, uState);
  702. //Convert monochrome masks to B&W color bitmap in the BitBlt.
  703. SetTextColor(hDC, 0L);
  704. SetBkColor(hDC, (COLORREF)0x00FFFFFF);
  705. /*
  706. * Only draw the dither brush where the mask is 1's. For
  707. * the indeterminate state we have to not overdraw the
  708. * shadow highlight so we use dx-3, dy-3 instead of dx-1
  709. * and dy-1. We do this whether or not we're blank.
  710. */
  711. i=(ATTRIBUTEBUTTON_INDETERMINATE==uState
  712. || BLANKBUTTON_INDETERMINATE==uState) ? 3 : 1;
  713. BitBlt(hDC, x+1, y+1, dx-i, dy-i, hDCMono, 0, 0, ROP_DSPDxax);
  714. SelectObject(hDC, hObj);
  715. }
  716. //DO NOT delete hBRDither! It's a reference to a shared global.
  717. }
  718. //Cleanup hDCGlyphs: Must do AFTER calling MaskCreate
  719. if (NULL!=hBmpSave)
  720. SelectObject(hDCGlyphs, hBmpSave);
  721. SelectObject(hDCMono, hBmpMonoOrg);
  722. DeleteObject(hBmpMono);
  723. //Restore everything in the DC.
  724. RestoreDC(hDC, iSaveDC);
  725. return TRUE;
  726. }
  727. /*
  728. * DrawBlankButton
  729. *
  730. * Purpose:
  731. * Draws a button with no face using the current system colors in either
  732. * an up or down state.
  733. *
  734. * Parameters:
  735. * hDC HDC on which to draw
  736. * x, y int coordinates where we start drawing
  737. * dx,dy int size of the button
  738. * fDown BOOL indicating the up or down state of the button
  739. * pcr COLORREF FAR * to five colors in which we store text,
  740. * shadow, face, highlight, and frame colors. This is
  741. * a matter of convenience for the caller, since we have
  742. * to load these colors anyway we might as well send them
  743. * back.
  744. *
  745. * Return Value:
  746. * None
  747. */
  748. static void DrawBlankButton(HDC hDC, int x, int y, int dx, int dy
  749. , BOOL fDown, COLORREF FAR *pcr)
  750. {
  751. //Get the current system colors for buttons.
  752. pcr[0]=GetSysColor(COLOR_BTNTEXT);
  753. pcr[1]=GetSysColor(COLOR_BTNSHADOW);
  754. pcr[2]=GetSysColor(COLOR_BTNFACE);
  755. //Windows 3.0 doesn't support COLOR_BTNHIGHLIGHT, so leave it white.
  756. if (0x0300 < (UINT)GetVersion())
  757. pcr[3]=GetSysColor(COLOR_BTNHIGHLIGHT);
  758. else
  759. pcr[3]=crStandard[STDCOLOR_WHITE];
  760. pcr[4]=GetSysColor(COLOR_WINDOWFRAME);
  761. //Draw the border around the button.
  762. PatB(hDC, x+1, y, dx-2, 1, pcr[4]);
  763. PatB(hDC, x+1, y+dy-1, dx-2, 1, pcr[4]);
  764. PatB(hDC, x, y+1, 1, dy-2, pcr[4]);
  765. PatB(hDC, x+dx-1, y+1, 1, dy-2, pcr[4]);
  766. //Shift coordinates to account for the border we just drew
  767. x++;
  768. y++;
  769. dx-=2;
  770. dy-=2;
  771. //Paint the interior grey as a default.
  772. PatB(hDC, x, y, dx, dy, pcr[2]);
  773. /*
  774. * Draw shadows and highlights. The DOWN grouping that contains
  775. * down, mouse down, and down disabled are drawn depressed. Up,
  776. * indeterminate, and disabled are drawn up.
  777. */
  778. if (fDown)
  779. {
  780. PatB(hDC, x, y, 1, dy, pcr[1]);
  781. PatB(hDC, x, y, dx, 1, pcr[1]);
  782. }
  783. else
  784. {
  785. //Normal button look.
  786. PatB(hDC, x, y, 1, dy-1, pcr[3]);
  787. PatB(hDC, x, y, dx-1, 1, pcr[3]);
  788. PatB(hDC, x+dx-1, y, 1, dy, pcr[1]);
  789. PatB(hDC, x, y+dy-1, dx, 1, pcr[1]);
  790. PatB(hDC, x+1+dx-3, y+1, 1, dy-2, pcr[1]);
  791. PatB(hDC, x+1, y+dy-2, dx-2, 1, pcr[1]);
  792. }
  793. return;
  794. }
  795. /*
  796. * PatB
  797. * Internal
  798. *
  799. * Purpose:
  800. * A more convenient PatBlt operation for drawing button borders and
  801. * highlights.
  802. *
  803. * Parameters:
  804. * hDC HDC on which to paint.
  805. * x, y int coordinates at which to paint.
  806. * dx, dy int dimensions of rectangle to paint.
  807. * rgb COLORREF to use as the background color.
  808. *
  809. * Return Value:
  810. * None
  811. */
  812. static void PatB(HDC hDC, int x, int y, int dx, int dy, COLORREF rgb)
  813. {
  814. RECT rc;
  815. SetBkColor(hDC, rgb);
  816. SetRect(&rc, x, y, x+dx, y+dy);
  817. ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
  818. }
  819. /*
  820. * MaskCreate
  821. * Internal
  822. *
  823. * Purpose:
  824. * Creates a monochrome mask bitmap of the given image at the given offset
  825. * in the global hDCMono. Anywhere in the image that you have the light
  826. * gray (STDCOLOR_LTGRAY) or the white highlight (STDCOLOR_WHITE) you get
  827. * get 1's. All other pixels are 0's
  828. *
  829. * Parameters:
  830. * iImage UINT index of the image for which to create a mask.
  831. * dx, dy int dimensions of the button.
  832. * bmx, bmy int dimensions of the bitmap to use.
  833. * xOffset int offset for x inside hDCMono where we paint.
  834. * yOffset int offset for y inside hDCMono where we paint.
  835. * uState UINT state of the image. Special cases are made
  836. * for ATTRIBUTEBUTTON_DOWNDISABLED and
  837. * ATTRIBUTEBUTTON_INDETERMINATE. In any case where you
  838. * do not want a special case, pass zero here, regardless
  839. * of the true button state.
  840. *
  841. * Return Value:
  842. * None
  843. */
  844. static void MaskCreate(UINT iImage, int dx, int dy, int bmx, int bmy
  845. ,int xOffset, int yOffset, UINT uState)
  846. {
  847. //Initalize whole area with zeros
  848. PatBlt(hDCMono, 0, 0, dx, dy, WHITENESS);
  849. if (uState & BUTTONGROUP_BLANK)
  850. return;
  851. //Convert face colored pixels to 1's. all others to black.
  852. SetBkColor(hDCGlyphs, crStandard[STDCOLOR_LTGRAY]);
  853. BitBlt(hDCMono, xOffset, yOffset, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCCOPY);
  854. //In the indeterminate state, don't turn highlight's to 1's. Leave black.
  855. if (ATTRIBUTEBUTTON_INDETERMINATE!=uState)
  856. {
  857. //Convert highlight colored pixels to 1's and OR them with the previous.
  858. SetBkColor(hDCGlyphs, crStandard[STDCOLOR_WHITE]);
  859. BitBlt(hDCMono, xOffset, yOffset, bmx, bmy, hDCGlyphs, iImage*bmx, 0, SRCPAINT);
  860. }
  861. /*
  862. * For the down disabled state, AND this same mask with itself at an
  863. * offset of 1, which accounts for the highlight shadow.
  864. */
  865. if (ATTRIBUTEBUTTON_DOWNDISABLED==uState)
  866. BitBlt(hDCMono, 1, 1, dx-1, dy-1, hDCMono, 0, 0, SRCAND);
  867. return;
  868. }