home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 18.ddi / MFC / SRC / BARTOOL.CP_ / BARTOOL.CP
Encoding:
Text File  |  1993-02-08  |  26.6 KB  |  1,001 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library. 
  2. // Copyright (C) 1992 Microsoft Corporation 
  3. // All rights reserved. 
  4. //  
  5. // This source code is only intended as a supplement to the 
  6. // Microsoft Foundation Classes Reference and Microsoft 
  7. // QuickHelp and/or WinHelp documentation provided with the library. 
  8. // See these sources for detailed information regarding the 
  9. // Microsoft Foundation Classes product. 
  10.  
  11.  
  12. #include "stdafx.h"
  13.  
  14. #ifdef AFX_CORE3_SEG
  15. #pragma code_seg(AFX_CORE3_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char BASED_CODE THIS_FILE[] = __FILE__;
  21. #define new DEBUG_NEW
  22. #endif
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25.  
  26. // globals for fast drawing (shared globals)
  27. static HDC NEAR hDCGlyphs = NULL;
  28. static HDC NEAR hDCMono = NULL;
  29. static HBRUSH NEAR hbrDither = NULL;
  30.  
  31. /////////////////////////////////////////////////////////////////////////////
  32. // Init / Term
  33.  
  34. static HBITMAP PASCAL CreateDitherBitmap();
  35.  
  36. #ifdef AFX_INIT_SEG
  37. #pragma code_seg(AFX_INIT_SEG)
  38. #endif
  39.  
  40. static void PASCAL FreeToolBarDraw()
  41. {
  42.     if (hDCMono)
  43.         _AfxExitDelete(hDCMono);
  44.     hDCMono = NULL;
  45.  
  46.     if (hDCGlyphs)
  47.         _AfxExitDelete(hDCGlyphs);
  48.     hDCGlyphs = NULL;
  49.  
  50.     if (hbrDither)
  51.         _AfxExitDelete(hbrDither);      // brushes
  52.     hbrDither = NULL;
  53. }
  54.  
  55.  
  56. static void PASCAL InitToolBarDraw()
  57. {
  58.     hDCGlyphs = CreateCompatibleDC(NULL);
  59.  
  60.     // Mono DC and Bitmap for disabled image
  61.     hDCMono = ::CreateCompatibleDC(NULL);
  62.  
  63.     HBITMAP hbmGray = ::CreateDitherBitmap();
  64.     if (hbmGray != NULL)
  65.     {
  66.         ASSERT(hbrDither == NULL);
  67.         hbrDither = ::CreatePatternBrush(hbmGray);
  68.         ::DeleteObject(hbmGray);
  69.     }
  70.  
  71.     ASSERT(afxData.pfnFreeToolBar == NULL);
  72.     afxData.pfnFreeToolBar = FreeToolBarDraw;
  73.     if (hDCGlyphs == NULL || hDCMono == NULL || hbrDither == NULL)
  74.         AfxThrowResourceException();
  75. }
  76.  
  77. /////////////////////////////////////////////////////////////////////////////
  78.  
  79. #ifdef AFX_CORE3_SEG
  80. #pragma code_seg(AFX_CORE3_SEG)
  81. #endif
  82.  
  83. static void NEAR PASCAL PatB(HDC hDC, int x, int y, int dx, int dy, COLORREF rgb)
  84. {
  85.     SetBkColor(hDC, rgb);
  86.     CRect rect(x, y, x+dx, y+dy);
  87.     ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
  88. }
  89.  
  90. static HBITMAP PASCAL CreateDitherBitmap()
  91. {
  92.     struct  // BITMAPINFO with 16 colors
  93.     {
  94.         BITMAPINFOHEADER bmiHeader;
  95.         RGBQUAD      bmiColors[16];
  96.     } bmi;
  97.     memset(&bmi, 0, sizeof(bmi));
  98.  
  99.     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  100.     bmi.bmiHeader.biWidth = 8;
  101.     bmi.bmiHeader.biHeight = 8;
  102.     bmi.bmiHeader.biPlanes = 1;
  103.     bmi.bmiHeader.biBitCount = 1;
  104.     bmi.bmiHeader.biCompression = BI_RGB;
  105.  
  106.     COLORREF clr = ::GetSysColor(COLOR_BTNFACE);
  107.     bmi.bmiColors[0].rgbBlue = GetBValue(clr);
  108.     bmi.bmiColors[0].rgbGreen = GetGValue(clr);
  109.     bmi.bmiColors[0].rgbRed = GetRValue(clr);
  110.  
  111.     if (afxData.bWin31)
  112.         clr = ::GetSysColor(COLOR_BTNHIGHLIGHT);
  113.     else
  114.         clr = RGB(255, 255, 255);
  115.     bmi.bmiColors[1].rgbBlue = GetBValue(clr);
  116.     bmi.bmiColors[1].rgbGreen = GetGValue(clr);
  117.     bmi.bmiColors[1].rgbRed = GetRValue(clr);
  118.  
  119.     // initialize the brushes
  120.     long patGray[8];
  121.     for (int i = 0; i < 8; i++)
  122.        patGray[i] = (i & 1) ? 0xAAAA5555L : 0x5555AAAAL;
  123.  
  124.     HDC hDC = GetDC(NULL);
  125.     HBITMAP hbm = CreateDIBitmap(hDC, &bmi.bmiHeader, CBM_INIT,
  126.         (LPBYTE)patGray, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS);
  127.     ReleaseDC(NULL, hDC);
  128.  
  129.     return hbm;
  130. }
  131.  
  132.  
  133. // create a mono bitmap mask:
  134. void CToolBar::CreateMask(int iImage, CPoint ptOffset,
  135.      BOOL bHilite, BOOL bHiliteShadow)
  136. {
  137.     // initalize whole area with 0's
  138.     PatBlt(hDCMono, 0, 0, m_sizeButton.cx-2, m_sizeButton.cy-2, WHITENESS);
  139.  
  140.     // create mask based on color bitmap
  141.     // convert this to 1's
  142.     SetBkColor(hDCGlyphs, afxData.clrBtnFace);
  143.     BitBlt(hDCMono, ptOffset.x, ptOffset.y, m_sizeImage.cx, m_sizeImage.cy,
  144.         hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCCOPY);
  145.  
  146.     if (bHilite)
  147.     {
  148.         // convert this to 1's
  149.         SetBkColor(hDCGlyphs, afxData.clrBtnHilite);
  150.         // OR in the new 1's
  151.         BitBlt(hDCMono, ptOffset.x, ptOffset.y, m_sizeImage.cx, m_sizeImage.cy,
  152.             hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCPAINT);
  153.  
  154.         if (bHiliteShadow)
  155.             BitBlt(hDCMono, 1, 1, m_sizeButton.cx-3, m_sizeButton.cy-3,
  156.                 hDCMono, 0, 0, SRCAND);
  157.     }
  158. }
  159.  
  160.  
  161. // Raster Ops
  162. #define ROP_DSPDxax  0x00E20746L
  163. #define ROP_PSDPxax  0x00B8074AL
  164.  
  165. BOOL CToolBar::DrawButton(HDC hDC, int x, int y, int iImage, UINT nStyle)
  166. {
  167.     ASSERT(hDC != NULL);
  168.  
  169.     // make the coordinates the interior of the button
  170.     x++;
  171.     y++;
  172.     int dx = m_sizeButton.cx - 2;
  173.     int dy = m_sizeButton.cy - 2;
  174.  
  175.     // border around button
  176.     PatB(hDC, x,    y-1,    dx, 1,  afxData.clrWindowFrame);
  177.     PatB(hDC, x,    y+dy,   dx, 1,  afxData.clrWindowFrame);
  178.     PatB(hDC, x-1,  y,  1,  dy, afxData.clrWindowFrame);
  179.     PatB(hDC, x+dx, y,  1,  dy, afxData.clrWindowFrame);
  180.  
  181.     // interior grey
  182.     PatB(hDC, x, y, dx, dy, afxData.clrBtnFace);
  183.  
  184.     CPoint ptOffset;
  185.     ptOffset.x = (dx - m_sizeImage.cx - 1) >> 1;
  186.     ptOffset.y = (dy - m_sizeImage.cy) >> 1;
  187.  
  188.     if (nStyle & (TBBS_PRESSED | TBBS_CHECKED))
  189.     {
  190.         // pressed in or down or down disabled
  191.         PatB(hDC, x, y, 1, dy, afxData.clrBtnShadow);
  192.         PatB(hDC, x, y, dx, 1, afxData.clrBtnShadow);
  193.         //For any depressed button, add one to the offsets.
  194.         ptOffset.x++;
  195.         ptOffset.y++;
  196.     }
  197.     else
  198.     {
  199.         // regular button look
  200.         PatB(hDC, x, y, 1, dy - 1, afxData.clrBtnHilite);
  201.         PatB(hDC, x, y, dx - 1, 1, afxData.clrBtnHilite);
  202.  
  203.         PatB(hDC, x + dx - 1, y, 1, dy, afxData.clrBtnShadow);
  204.         PatB(hDC, x, y + dy-1, dx, 1,   afxData.clrBtnShadow);
  205.  
  206.         PatB(hDC, x + 1 + dx - 3, y + 1, 1, dy-2, afxData.clrBtnShadow);
  207.         PatB(hDC, x + 1, y + dy - 2, dx - 2, 1, afxData.clrBtnShadow);
  208.     }
  209.  
  210.     if ((nStyle & TBBS_PRESSED) || !(nStyle & TBBS_DISABLED))
  211.     {
  212.         // normal image version
  213.         BitBlt(hDC, x + ptOffset.x, y + ptOffset.y,
  214.             m_sizeImage.cx, m_sizeImage.cy, 
  215.             hDCGlyphs, iImage * m_sizeImage.cx, 0, SRCCOPY);
  216.  
  217.         if (nStyle & TBBS_PRESSED)
  218.             return TRUE;        // nothing more to do (rest of style is ignored)
  219.     }
  220.  
  221.     if (nStyle & (TBBS_DISABLED | TBBS_INDETERMINATE))
  222.     {
  223.         // disabled or indeterminate version
  224.         CreateMask(iImage, ptOffset, TRUE, FALSE);
  225.  
  226.         SetTextColor(hDC, 0L);                  // 0's in mono -> 0 (for ROP)
  227.         SetBkColor(hDC, (COLORREF)0x00FFFFFFL); // 1's in mono -> 1
  228.  
  229.         if (nStyle & TBBS_DISABLED)
  230.         {
  231.             // disabled - draw the hilighted shadow
  232.             HGDIOBJ hbrOld = SelectObject(hDC, afxData.hbrBtnHilite);
  233.             if (hbrOld != NULL)
  234.             {
  235.                 // draw hilight color where we have 0's in the mask
  236.                 BitBlt(hDC, x + 1, y + 1,
  237.                     m_sizeButton.cx-2, m_sizeButton.cy-2,
  238.                     hDCMono, 0, 0, ROP_PSDPxax);
  239.                 SelectObject(hDC, hbrOld);
  240.             }
  241.         }
  242.  
  243.         //BLOCK: always draw the shadow
  244.         {
  245.             HGDIOBJ hbrOld = SelectObject(hDC, afxData.hbrBtnShadow);
  246.             if (hbrOld != NULL)
  247.             {
  248.                 // draw the shadow color where we have 0's in the mask
  249.                 BitBlt(hDC, x, y, m_sizeButton.cx-2, m_sizeButton.cy-2,
  250.                     hDCMono, 0, 0, ROP_PSDPxax);
  251.                 SelectObject(hDC, hbrOld);
  252.             }
  253.         }
  254.     }
  255.  
  256.     // if it is checked do the dither brush avoiding the glyph
  257.     if (nStyle & (TBBS_CHECKED | TBBS_INDETERMINATE))
  258.     {
  259.         HGDIOBJ hbrOld = SelectObject(hDC, hbrDither);
  260.         if (hbrOld != NULL)
  261.         {
  262.             ptOffset.x--;
  263.             ptOffset.y--;
  264.             CreateMask(iImage, ptOffset, ~(nStyle & TBBS_INDETERMINATE),
  265.                     nStyle & TBBS_DISABLED);
  266.  
  267.             SetTextColor(hDC, 0L);              // 0 -> 0
  268.             SetBkColor(hDC, (COLORREF)0x00FFFFFFL); // 1 -> 1
  269.  
  270.             int delta = (nStyle & TBBS_INDETERMINATE) ? 3 : 1;
  271.             // only draw the dither brush where the mask is 1's
  272.             BitBlt(hDC, x+1, y+1, dx-delta, dy-delta,
  273.                 hDCMono, 0, 0, ROP_DSPDxax);
  274.             SelectObject(hDC, hbrOld);
  275.         }
  276.     }
  277.  
  278.     return TRUE;
  279. }
  280.  
  281. BOOL CToolBar::PrepareDrawButton(DrawState& ds)
  282. {
  283.     ASSERT(m_hbmImageWell != NULL);
  284.     ASSERT(m_sizeButton.cx > 2 && m_sizeButton.cy > 2);
  285.  
  286.     // We need to kick-start the bitmap selection process.
  287.     ds.hbmOldGlyphs = (HBITMAP)SelectObject(hDCGlyphs, m_hbmImageWell);
  288.     ds.hbmMono = CreateBitmap(m_sizeButton.cx-2, m_sizeButton.cy-2,
  289.                     1, 1, NULL);
  290.     ds.hbmMonoOld = (HBITMAP)SelectObject(hDCMono, ds.hbmMono);
  291.     if (ds.hbmOldGlyphs == NULL || ds.hbmMono == NULL || ds.hbmMonoOld == NULL)
  292.     {
  293.         TRACE0("Error: can't draw toolbar\n");
  294.         if (ds.hbmMono)
  295.             DeleteObject(ds.hbmMono);
  296.         return FALSE;
  297.     }
  298.     return TRUE;
  299. }
  300.  
  301. void CToolBar::EndDrawButton(DrawState& ds)
  302. {
  303.     SelectObject(hDCMono, ds.hbmMonoOld);
  304.     DeleteObject(ds.hbmMono);
  305.     SelectObject(hDCGlyphs, ds.hbmOldGlyphs);
  306. }
  307.  
  308. /////////////////////////////////////////////////////////////////////////////
  309. // CToolBar creation etc
  310.  
  311. struct AFX_TBBUTTON
  312. {
  313.    UINT nID;        // Command ID that this button sends
  314.     UINT nStyle;    // TBBS_ styles
  315.     int iImage;     // index into mondo bitmap of this button's picture
  316.                         // or size of this spacer
  317. };
  318.  
  319. inline AFX_TBBUTTON* CToolBar::_GetButtonPtr(int nIndex) const
  320. {
  321.     ASSERT(nIndex >= 0 && nIndex < m_nCount);
  322.     ASSERT(m_pData != NULL);
  323.     return ((AFX_TBBUTTON*)m_pData) + nIndex;
  324. }
  325.  
  326. // DIBs use BGR format
  327. #define RGB_TO_BGR(r,g,b)       RGB(b,g,r)
  328.  
  329. static inline COLORREF RGBtoBGR(COLORREF clr)      
  330. {
  331.     return RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr));
  332. }
  333.  
  334. HBITMAP AFXAPI AfxLoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc)
  335. {
  336.     static struct
  337.     {
  338.         COLORREF bgrFrom;
  339.         int iSysColorTo;
  340.     }
  341.     BASED_CODE sysColorMap[] =
  342.     {
  343.         // mapping from color in DIB to system color
  344.         {RGB_TO_BGR(000,000,000),  COLOR_BTNTEXT},     // black
  345.         {RGB_TO_BGR(128,128,128),  COLOR_BTNSHADOW},   // dark grey
  346.         {RGB_TO_BGR(192,192,192),  COLOR_BTNFACE},     // bright grey
  347.         {RGB_TO_BGR(255,255,255),  COLOR_BTNHIGHLIGHT} // white
  348.     };
  349.     const int nMaps = afxData.bWin31 ? 4 : 3;
  350.  
  351.     HGLOBAL hglb;
  352.     if ((hglb = ::LoadResource(hInst, hRsrc)) == NULL)
  353.         return NULL;
  354.  
  355.     LPBITMAPINFOHEADER lpBitmapInfo = (LPBITMAPINFOHEADER)LockResource(hglb);
  356.     if (lpBitmapInfo == NULL)
  357.         return NULL;
  358.  
  359.     COLORREF FAR* pColorTable;
  360.     pColorTable = (COLORREF FAR*)
  361.         (((LPSTR)lpBitmapInfo) + (UINT)lpBitmapInfo->biSize);
  362.         // Color table is in BGR DIB format
  363.  
  364.     // save old color table so we will not change the bitmap
  365.     const int nColorTableSize = 16;
  366.     COLORREF clrSaveColors[nColorTableSize];
  367.     _fmemcpy(clrSaveColors, pColorTable, nColorTableSize*sizeof(COLORREF));
  368.  
  369.     for (int iColor = 0; iColor < nColorTableSize; iColor++)
  370.     {
  371.         // look for matching BGR color in original
  372.         for (int i = 0; i < nMaps; i++) 
  373.         {
  374.             if (pColorTable[iColor] == sysColorMap[i].bgrFrom)
  375.             {
  376.                 pColorTable[iColor] = RGBtoBGR(::GetSysColor(
  377.                     sysColorMap[i].iSysColorTo));
  378.                 break;
  379.             }
  380.         }
  381.     }
  382.  
  383.     int nWidth = (int)lpBitmapInfo->biWidth;
  384.     int nHeight = (int)lpBitmapInfo->biHeight;
  385.     HDC hDCScreen = ::GetDC(NULL);
  386.     HBITMAP hbm = ::CreateCompatibleBitmap(hDCScreen, nWidth, nHeight);
  387.     ::ReleaseDC(NULL, hDCScreen);
  388.  
  389.     if (hbm != NULL)
  390.     {
  391.         HBITMAP hbmOld = (HBITMAP)::SelectObject(hDCGlyphs, hbm);
  392.  
  393.         LPSTR lpBits;
  394.         lpBits = (LPSTR)(lpBitmapInfo + 1);
  395.         lpBits += (1 << (lpBitmapInfo->biBitCount)) * sizeof(RGBQUAD);
  396.  
  397.         StretchDIBits(hDCGlyphs, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight, 
  398.             lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY);
  399.         SelectObject(hDCGlyphs, hbmOld);
  400.     }
  401.  
  402.     _fmemcpy(pColorTable, clrSaveColors, nColorTableSize*sizeof(COLORREF));
  403.         // restore old color table
  404.  
  405.     ::UnlockResource(hglb);
  406.     ::FreeResource(hglb);
  407.     return hbm;
  408. }
  409.  
  410. IMPLEMENT_DYNAMIC(CToolBar, CControlBar)
  411.  
  412. #ifdef AFX_INIT_SEG
  413. #pragma code_seg(AFX_INIT_SEG)
  414. #endif
  415.  
  416. CToolBar::CToolBar()
  417. {
  418.     m_hbmImageWell = NULL;
  419.     m_hInstImageWell = NULL;
  420.     m_hRsrcImageWell = NULL;
  421.     m_iButtonCapture = -1;      // nothing captured
  422.  
  423.     // UISG standard sizes
  424.     m_sizeButton.cx = 24;
  425.     m_sizeButton.cy = 22;
  426.     m_sizeImage.cx = 16;
  427.     m_sizeImage.cy = 15;
  428.     m_cxDefaultGap = 6;         // 6 pixels between buttons
  429.     m_cyTopBorder = m_cyBottomBorder = 2;   // 2 pixel for top/bottom gaps
  430.  
  431.     // initialize the toolbar drawing engine
  432.     if (afxData.pfnFreeToolBar == NULL)
  433.         InitToolBarDraw();
  434. }
  435.  
  436. CToolBar::~CToolBar()
  437. {
  438.     if (m_hbmImageWell != NULL)
  439.         ::DeleteObject(m_hbmImageWell);
  440. }
  441.  
  442. BOOL CToolBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
  443. {
  444.     if (pParentWnd != NULL)
  445.         ASSERT_VALID(pParentWnd);   // must have a parent
  446.  
  447.     // create the HWND
  448.     CRect rect;
  449.     rect.SetRectEmpty();
  450.     if (!CWnd::Create(_afxWndControlBar, NULL, dwStyle, rect, pParentWnd, nID))
  451.         return FALSE;
  452.         // NOTE: Parent must resize itself for control bar to be resized
  453.  
  454.     // set height
  455.     CRect rectSize;
  456.     rectSize.SetRectEmpty();
  457.     CalcInsideRect(rectSize);       // will be negative size
  458.     m_sizeFixedLayout.cy = m_sizeButton.cy - rectSize.Height();
  459.     ASSERT(m_sizeFixedLayout.cx == 32767);  // max size
  460.     return TRUE;
  461. }
  462.  
  463. void CToolBar::SetSizes(SIZE sizeButton, SIZE sizeImage)
  464. {
  465.     ASSERT_VALID(this);
  466.     ASSERT(sizeButton.cx > 0 && sizeButton.cy > 0);
  467.     ASSERT(sizeImage.cx > 0 && sizeImage.cy > 0);
  468.     // button must be big enough to hold image + 3 pixels on each side
  469.     ASSERT(sizeButton.cx >= sizeImage.cx + 6);
  470.     ASSERT(sizeButton.cy >= sizeImage.cy + 6);
  471.  
  472.     m_sizeButton = sizeButton;
  473.     m_sizeImage = sizeImage;
  474.     // set height
  475.  
  476.     CRect rectSize;
  477.     rectSize.SetRectEmpty();
  478.     CalcInsideRect(rectSize);       // will be negative size
  479.     m_sizeFixedLayout.cy = m_sizeButton.cy - rectSize.Height();
  480.     ASSERT(m_sizeFixedLayout.cx == 32767);  // max size
  481.     Invalidate();   // just to be nice if called when toolbar is visible
  482. }
  483.  
  484. void CToolBar::SetHeight(int cyHeight)
  485. {
  486.     ASSERT_VALID(this);
  487.  
  488.     m_sizeFixedLayout.cy = cyHeight;
  489.     ASSERT(m_sizeFixedLayout.cx == 32767);  // max size
  490.     m_cyTopBorder = (cyHeight - m_sizeButton.cy) / 2;
  491.     if (m_cyTopBorder < 0)
  492.     {
  493.         TRACE1("Warning: CToolBar::SetHeight(%d) is smaller than button\n",
  494.             cyHeight);
  495.         m_cyTopBorder = 0;  // will clip at bottom
  496.     }
  497.     // bottom border will be ignored (truncate as needed)
  498.     Invalidate();   // just to be nice if called when toolbar is visible
  499. }
  500.  
  501. BOOL CToolBar::LoadBitmap(LPCSTR lpszResourceName)
  502. {
  503.     ASSERT_VALID(this);
  504.     ASSERT(lpszResourceName != NULL);
  505.  
  506.     if (m_hbmImageWell != NULL)
  507.         ::DeleteObject(m_hbmImageWell);     // get rid of old one
  508.  
  509. #ifndef _AFXDLL
  510.     m_hInstImageWell = AfxGetResourceHandle();
  511. #else
  512.     m_hInstImageWell = AfxFindResourceHandle(lpszResourceName, RT_BITMAP);
  513. #endif
  514.  
  515.     if ((m_hRsrcImageWell = ::FindResource(m_hInstImageWell, 
  516.         lpszResourceName, RT_BITMAP)) == NULL)
  517.         return FALSE;
  518.  
  519.     m_hbmImageWell = AfxLoadSysColorBitmap(m_hInstImageWell,m_hRsrcImageWell);
  520.     return (m_hbmImageWell != NULL);
  521. }
  522.  
  523. BOOL CToolBar::SetButtons(const UINT FAR* lpIDArray, int nIDCount)
  524. {
  525.     ASSERT_VALID(this);
  526.     ASSERT(nIDCount >= 1);  // must be at least one of them
  527.     ASSERT(lpIDArray == NULL ||
  528.         AfxIsValidAddress(lpIDArray, sizeof(UINT) * nIDCount, FALSE));
  529.  
  530.     // first allocate array for panes and copy initial data
  531.     if (!AllocElements(nIDCount, sizeof(AFX_TBBUTTON)))
  532.         return FALSE;
  533.     ASSERT(nIDCount == m_nCount);
  534.  
  535.     if (lpIDArray != NULL)
  536.     {
  537.         int iImage = 0;
  538.         // go through them adding buttons
  539.         AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
  540.         for (int i = 0; i < nIDCount; i++, pTBB++)
  541.         {
  542.             ASSERT(pTBB != NULL);
  543.             if ((pTBB->nID = *lpIDArray++) == 0)
  544.             {
  545.                 // separator
  546.                 pTBB->nStyle = TBBS_SEPARATOR;
  547.                 // width of separator includes 2 pixel overlap
  548.                 pTBB->iImage = m_cxDefaultGap + 2;
  549.             }
  550.             else
  551.             {
  552.                 // a command button with image
  553.                 pTBB->nStyle = TBBS_BUTTON;
  554.                 pTBB->iImage = iImage++;
  555.             }
  556.         }
  557.     }
  558.     return TRUE;
  559. }
  560.  
  561. #ifdef AFX_CORE3_SEG
  562. #pragma code_seg(AFX_CORE3_SEG)
  563. #endif
  564.  
  565. /////////////////////////////////////////////////////////////////////////////
  566. // CToolBar attribute access
  567.  
  568. int CToolBar::CommandToIndex(UINT nIDFind) const
  569. {
  570.     ASSERT_VALID(this);
  571.  
  572.     AFX_TBBUTTON* pTBB = _GetButtonPtr(0);
  573.     for (int i = 0; i < m_nCount; i++, pTBB++)
  574.         if (pTBB->nID == nIDFind)
  575.             return i;
  576.     return -1;
  577. }
  578.  
  579. UINT CToolBar::GetItemID(int nIndex) const
  580. {
  581.     ASSERT_VALID(this);
  582.  
  583.     return _GetButtonPtr(nIndex)->nID;
  584. }
  585.  
  586. void CToolBar::GetItemRect(int nIndex, LPRECT lpRect) const
  587. {
  588.     ASSERT_VALID(this);
  589.     ASSERT(nIndex >= 0 && nIndex < m_nCount);
  590.     ASSERT(AfxIsValidAddress(lpRect, sizeof(RECT)));
  591.  
  592.     CRect rect;
  593.     rect.SetRectEmpty();        // only need top and left
  594.     CalcInsideRect(rect);
  595.     AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
  596.     for (int iButton = 0; iButton < nIndex; iButton++, pTBB++)
  597.     {
  598.         ASSERT(pTBB != NULL);
  599.         // skip this button or separator
  600.         rect.left += (pTBB->nStyle & TBBS_SEPARATOR) ?
  601.                         pTBB->iImage : m_sizeButton.cx;
  602.         rect.left--;    // go back one for overlap
  603.     }
  604.     ASSERT(iButton == nIndex);
  605.     ASSERT(pTBB == _GetButtonPtr(nIndex));
  606.  
  607.     // button or image width
  608.     int cx = (pTBB->nStyle & TBBS_SEPARATOR) ? pTBB->iImage : m_sizeButton.cx;
  609.     lpRect->right = (lpRect->left = rect.left) + cx;
  610.     lpRect->bottom = (lpRect->top = rect.top) + m_sizeButton.cy;
  611. }
  612.  
  613. inline UINT CToolBar::_GetButtonStyle(int nIndex) const
  614. {
  615.     return _GetButtonPtr(nIndex)->nStyle;
  616. }
  617.  
  618. void CToolBar::_SetButtonStyle(int nIndex, UINT nStyle)
  619. {
  620.     AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
  621.     if (pTBB->nStyle != nStyle)
  622.     {
  623.         // change the style and invalidate
  624.         pTBB->nStyle = nStyle;
  625.         InvalidateButton(nIndex);
  626.     }
  627. }
  628.  
  629. void CToolBar::GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const
  630. {
  631.     ASSERT_VALID(this);
  632.  
  633.     AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
  634.     nID = pTBB->nID;
  635.     nStyle = pTBB->nStyle;
  636.     iImage = pTBB->iImage;
  637. }
  638.  
  639. void CToolBar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage)
  640. {
  641.     ASSERT_VALID(this);
  642.  
  643.     AFX_TBBUTTON* pTBB = _GetButtonPtr(nIndex);
  644.     pTBB->nID = nID;
  645.     pTBB->iImage = iImage;
  646.     pTBB->nStyle = nStyle;
  647.     InvalidateButton(nIndex);
  648. }
  649.  
  650. void CToolBar::DoPaint(CDC* pDC)
  651. {
  652.     ASSERT_VALID(this);
  653.     ASSERT_VALID(pDC);
  654.  
  655.     CControlBar::DoPaint(pDC);      // draw border
  656.     CRect rect;
  657.     GetClientRect(rect);
  658.     CalcInsideRect(rect);
  659.     // force the full size of the button
  660.     rect.bottom = rect.top + m_sizeButton.cy;
  661.  
  662.     DrawState ds;
  663.     if (!PrepareDrawButton(ds))
  664.         return;     // something went wrong
  665.  
  666.     AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
  667.     for (int iButton = 0; iButton < m_nCount; iButton++, pTBB++)
  668.     {
  669.         ASSERT(pTBB != NULL);
  670.         if (pTBB->nStyle & TBBS_SEPARATOR)
  671.         {
  672.             // separator
  673.             rect.right = rect.left + pTBB->iImage;
  674.         }
  675.         else
  676.         {
  677.             rect.right = rect.left + m_sizeButton.cx;
  678.             if (::RectVisible(pDC->m_hDC, &rect))
  679.             {
  680.                 DrawButton(pDC->m_hDC, rect.left, rect.top,
  681.                     pTBB->iImage, pTBB->nStyle);
  682.             }
  683.         }
  684.         // adjust for overlap
  685.         rect.left = rect.right - 1;
  686.     }
  687.     EndDrawButton(ds);
  688. }
  689.  
  690. void CToolBar::InvalidateButton(int nIndex)
  691. {
  692.     ASSERT_VALID(this);
  693.  
  694.     CRect rect;
  695.     GetItemRect(nIndex, &rect);
  696.     InvalidateRect(rect, FALSE);    // don't erase background
  697. }
  698.  
  699.  
  700. int CToolBar::HitTest(CPoint point) // in window relative coords
  701. {
  702.     CRect rect;
  703.     rect.SetRectEmpty();        // only need top and left
  704.     CalcInsideRect(rect);
  705.     if (point.y < rect.top || point.y >= rect.top + m_sizeButton.cy)
  706.         return -1;      // no Y hit
  707.  
  708.     AFX_TBBUTTON* pTBB = (AFX_TBBUTTON*)m_pData;
  709.     ASSERT(pTBB != NULL);
  710.     for (int iButton = 0; iButton < m_nCount; iButton++, pTBB++)
  711.     {
  712.         if (point.x < rect.left)
  713.             break;      // missed it
  714.         rect.left += (pTBB->nStyle & TBBS_SEPARATOR) ?
  715.                         pTBB->iImage : m_sizeButton.cx;
  716.         rect.left--;    // go back one for overlap
  717.         if (point.x <= rect.left && !(pTBB->nStyle & TBBS_SEPARATOR))
  718.             return iButton;     // hit !
  719.     }
  720.     return -1;      // nothing hit
  721. }
  722.  
  723.  
  724. /////////////////////////////////////////////////////////////////////////////
  725. // CToolBar message handlers
  726.  
  727. BEGIN_MESSAGE_MAP(CToolBar, CControlBar)
  728.     //{{AFX_MSG_MAP(CToolBar)
  729.     ON_WM_LBUTTONDOWN()
  730.     ON_WM_MOUSEMOVE()
  731.     ON_WM_LBUTTONUP()
  732.     ON_WM_CANCELMODE()
  733.     ON_WM_SYSCOLORCHANGE()
  734.     ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)
  735.     //}}AFX_MSG_MAP
  736. END_MESSAGE_MAP()
  737.  
  738. void CToolBar::OnLButtonDown(UINT, CPoint point)
  739. {
  740.     if ((m_iButtonCapture = HitTest(point)) < 0)
  741.         return;     // nothing hit
  742.  
  743.     AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
  744.     ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));
  745.     if (pTBB->nStyle & TBBS_DISABLED)
  746.     {
  747.         m_iButtonCapture = -1;
  748.         return;     // don't press it
  749.     }
  750.  
  751.     SetCapture();
  752.     pTBB->nStyle |= TBBS_PRESSED;
  753.     InvalidateButton(m_iButtonCapture);
  754.     UpdateWindow(); // immediate feedback
  755.  
  756.     GetParent()->SendMessage(WM_SETMESSAGESTRING, (WPARAM)pTBB->nID);
  757. }
  758.  
  759. LRESULT CToolBar::OnHelpHitTest(WPARAM wParam, LPARAM lParam)
  760. {
  761.     CPoint point = lParam;
  762.     int iButton = HitTest(point);
  763.     if (iButton == -1)
  764.         return CControlBar::OnHelpHitTest(wParam, lParam);
  765.     AFX_TBBUTTON* pTBB = _GetButtonPtr(iButton);
  766.     ASSERT(pTBB->nID != 0);
  767.     return HID_BASE_COMMAND+pTBB->nID;
  768. }
  769.  
  770. void CToolBar::OnMouseMove(UINT, CPoint point)
  771. {
  772.     if (m_iButtonCapture < 0)
  773.         return;     // not captured
  774.     AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
  775.     ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));
  776.  
  777.     UINT nNewStyle = (pTBB->nStyle & ~TBBS_PRESSED);
  778.     if (GetCapture() != this)
  779.     {
  780.         m_iButtonCapture = -1; // lost capture
  781.         GetParent()->SendMessage(WM_SETMESSAGESTRING,
  782.             (WPARAM)AFX_IDS_IDLEMESSAGE);
  783.     }
  784.     else
  785.     {
  786.         // should be pressed if still hitting the captured button
  787.         if (HitTest(point) == m_iButtonCapture)
  788.             nNewStyle |= TBBS_PRESSED;
  789.     }
  790.     _SetButtonStyle(m_iButtonCapture, nNewStyle);
  791.     UpdateWindow(); // immediate feedback
  792. }
  793.  
  794. void CToolBar::OnLButtonUp(UINT, CPoint point)
  795. {
  796.     if (m_iButtonCapture < 0)
  797.         return;     // not captured
  798.  
  799.     AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
  800.     ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));
  801.     UINT nIDCmd = 0;
  802.  
  803.     UINT nNewStyle = (pTBB->nStyle & ~TBBS_PRESSED);
  804.     if (GetCapture() == this)
  805.     {
  806.         // we did not lose the capture
  807.         ReleaseCapture();
  808.         if (HitTest(point) == m_iButtonCapture &&
  809.           !(pTBB->nStyle & TBBS_DISABLED))
  810.         {
  811.             // pressed, will send command notification
  812.             nIDCmd = pTBB->nID;
  813.  
  814.             if (pTBB->nStyle & TBBS_CHECKBOX)
  815.             {
  816.                 // auto check: three state => down
  817.                 if (nNewStyle & TBBS_INDETERMINATE)
  818.                     nNewStyle &= ~TBBS_INDETERMINATE;
  819.  
  820.                 nNewStyle ^= TBBS_CHECKED;
  821.             }
  822.         }
  823.     }
  824.  
  825.     _SetButtonStyle(m_iButtonCapture, nNewStyle);
  826.     m_iButtonCapture = -1;
  827.     UpdateWindow(); // immediate feedback
  828.  
  829.     // first flush the drag mode
  830.     GetParent()->SendMessage(WM_SETMESSAGESTRING,
  831.         (WPARAM)AFX_IDS_IDLEMESSAGE);
  832.  
  833.     if (nIDCmd != 0)
  834.         GetParent()->SendMessage(WM_COMMAND, nIDCmd, 0L);       // send command
  835. }
  836.  
  837. void CToolBar::OnCancelMode()
  838. {
  839.     if (m_iButtonCapture < 0)
  840.         return;     // not captured
  841.  
  842.     AFX_TBBUTTON* pTBB = _GetButtonPtr(m_iButtonCapture);
  843.     ASSERT(!(pTBB->nStyle & TBBS_SEPARATOR));
  844.     UINT nNewStyle = (pTBB->nStyle & ~TBBS_PRESSED);
  845.     if (GetCapture() == this)
  846.         ReleaseCapture();
  847.     _SetButtonStyle(m_iButtonCapture, nNewStyle);
  848.     m_iButtonCapture = -1;
  849.     UpdateWindow();
  850.     GetParent()->SendMessage(WM_SETMESSAGESTRING,
  851.         (WPARAM)AFX_IDS_IDLEMESSAGE);
  852. }
  853.  
  854. void CToolBar::OnSysColorChange()
  855. {
  856.     // re-initialize global dither brush
  857.     HBITMAP hbmGray = ::CreateDitherBitmap();
  858.     if (hbmGray != NULL)
  859.     {
  860.         HBRUSH hbrNew = ::CreatePatternBrush(hbmGray);
  861.         if (hbrNew != NULL)
  862.         {
  863.             if (hbrDither != NULL)
  864.                 ::DeleteObject(hbrDither);      // free old one
  865.             hbrDither = hbrNew;
  866.         }
  867.         ::DeleteObject(hbmGray);
  868.     }
  869.  
  870.     // re-color bitmap for toolbar
  871.     if (m_hbmImageWell != NULL)
  872.     {
  873.         HBITMAP hbmNew;
  874.         hbmNew = AfxLoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell);
  875.         if (hbmNew != NULL)
  876.         {
  877.             ::DeleteObject(m_hbmImageWell);     // free old one
  878.             m_hbmImageWell = hbmNew;
  879.         }
  880.     }
  881. }
  882.  
  883. /////////////////////////////////////////////////////////////////////////////
  884. // CToolBar idle update through CToolCmdUI class
  885.  
  886. class CToolCmdUI : public CCmdUI        // class private to this file !
  887. {
  888. public: // re-implementations only
  889.     virtual void Enable(BOOL bOn);
  890.     virtual void SetCheck(int nCheck);
  891.     virtual void SetText(LPCSTR lpszText);
  892. };
  893.  
  894. void CToolCmdUI::Enable(BOOL bOn)
  895. {
  896.     m_bEnableChanged = TRUE;
  897.     CToolBar* pToolBar = (CToolBar*)m_pOther;
  898.     ASSERT(pToolBar != NULL);
  899.     ASSERT(pToolBar->IsKindOf(RUNTIME_CLASS(CToolBar)));
  900.     ASSERT(m_nIndex < m_nIndexMax);
  901.  
  902.     UINT nNewStyle = pToolBar->_GetButtonStyle(m_nIndex) & ~TBBS_DISABLED;
  903.     if (!bOn)
  904.         nNewStyle |= TBBS_DISABLED;
  905.     ASSERT(!(nNewStyle & TBBS_SEPARATOR));
  906.     pToolBar->_SetButtonStyle(m_nIndex, nNewStyle);
  907. }
  908.  
  909. void CToolCmdUI::SetCheck(int nCheck)
  910. {
  911.     ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate
  912.     CToolBar* pToolBar = (CToolBar*)m_pOther;
  913.     ASSERT(pToolBar != NULL);
  914.     ASSERT(pToolBar->IsKindOf(RUNTIME_CLASS(CToolBar)));
  915.     ASSERT(m_nIndex < m_nIndexMax);
  916.  
  917.     UINT nNewStyle = pToolBar->_GetButtonStyle(m_nIndex) &
  918.                 ~(TBBS_CHECKED | TBBS_INDETERMINATE);
  919.     if (nCheck == 1)
  920.         nNewStyle |= TBBS_CHECKED;
  921.     else if (nCheck == 2)
  922.         nNewStyle |= TBBS_INDETERMINATE;
  923.     ASSERT(!(nNewStyle & TBBS_SEPARATOR));
  924.     pToolBar->_SetButtonStyle(m_nIndex, nNewStyle | TBBS_CHECKBOX);
  925. }
  926.  
  927. void CToolCmdUI::SetText(LPCSTR)
  928. {
  929.     // ignore it
  930. }
  931.  
  932.  
  933. void CToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
  934. {
  935.     CToolCmdUI state;
  936.     state.m_pOther = this;
  937.  
  938.     state.m_nIndexMax = (UINT)m_nCount;
  939.     for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
  940.       state.m_nIndex++)
  941.     {
  942.         AFX_TBBUTTON* pTBB = _GetButtonPtr(state.m_nIndex);
  943.         state.m_nID = pTBB->nID;
  944.  
  945.         // ignore pressed buttons or separators
  946.         if (!(pTBB->nStyle & (TBBS_PRESSED|TBBS_SEPARATOR)))
  947.             state.DoUpdate(pTarget, bDisableIfNoHndler);
  948.     }
  949.  
  950.     // update the dialog controls added to the toolbar
  951.     UpdateDialogControls(pTarget, bDisableIfNoHndler);
  952. }
  953.  
  954.  
  955. /////////////////////////////////////////////////////////////////////////////
  956. // CToolBar diagnostics
  957.  
  958. #ifdef _DEBUG
  959. void CToolBar::AssertValid() const
  960. {
  961.     CControlBar::AssertValid();
  962.     if (afxData.bWin31)
  963.         ASSERT(m_hbmImageWell == NULL || ::IsGDIObject(m_hbmImageWell));
  964.  
  965.     if (m_hbmImageWell != NULL)
  966.     {
  967.         ASSERT(m_hRsrcImageWell != NULL);
  968.         ASSERT(m_hInstImageWell != NULL);
  969.     }
  970. }
  971.  
  972. void CToolBar::Dump(CDumpContext& dc) const
  973. {
  974.     CControlBar::Dump(dc);
  975.     AFX_DUMP1(dc, "\nm_hbmImageWell = ", (UINT)m_hbmImageWell);
  976.     AFX_DUMP1(dc, "\nm_hInstImageWell = ", (UINT)m_hInstImageWell);
  977.     AFX_DUMP1(dc, "\nm_hRsrcImageWell = ", (UINT)m_hRsrcImageWell);
  978.     AFX_DUMP1(dc, "\nm_iButtonCapture = ", m_iButtonCapture);
  979.     AFX_DUMP1(dc, "\nm_sizeButton = ", m_sizeButton);
  980.     AFX_DUMP1(dc, "\nm_sizeImage = ", m_sizeImage);
  981.  
  982.     if (dc.GetDepth() > 0)
  983.     {
  984.         for (int i = 0; i < m_nCount; i++)
  985.         {
  986.             AFX_TBBUTTON* pTBB = _GetButtonPtr(i);
  987.             AFX_DUMP1(dc, "\ntoolbar button[", i); AFX_DUMP0(dc, "] = { ");
  988.             AFX_DUMP1(dc, "\n\tnID = ", pTBB->nID);
  989.             AFX_DUMP1(dc, "\n\tnStyle = ", pTBB->nStyle);
  990.             if (pTBB->nStyle & TBBS_SEPARATOR)
  991.                 AFX_DUMP1(dc, "\n\tiImage (separator width) = ", pTBB->iImage);
  992.             else
  993.                 AFX_DUMP1(dc,"\n\tiImage (bitmap image index) = ",pTBB->iImage);
  994.             AFX_DUMP0(dc, "\n\t}");
  995.         }
  996.     }
  997. }
  998. #endif
  999.  
  1000. /////////////////////////////////////////////////////////////////////////////
  1001.