home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / inole / bttncur.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  28KB  |  885 lines

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