BTTNCUR.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:31k
源码类别:

Windows编程

开发平台:

Visual C++

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