home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / bartool.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  40KB  |  1,497 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 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 related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_CORE3_SEG
  14. #pragma code_seg(AFX_CORE3_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CToolBar creation etc
  26.  
  27. #ifdef AFX_CORE3_SEG
  28. #pragma code_seg(AFX_CORE3_SEG)
  29. #endif
  30.  
  31. /*
  32.     DIBs use RGBQUAD format:
  33.         0xbb 0xgg 0xrr 0x00
  34.  
  35.     Reasonably efficient code to convert a COLORREF into an
  36.     RGBQUAD is byte-order-dependent, so we need different
  37.     code depending on the byte order we're targeting.
  38. */
  39. #define RGB_TO_RGBQUAD(r,g,b)   (RGB(b,g,r))
  40. #define CLR_TO_RGBQUAD(clr)     (RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr)))
  41.  
  42. struct AFX_COLORMAP
  43. {
  44.     // use DWORD instead of RGBQUAD so we can compare two RGBQUADs easily
  45.     DWORD rgbqFrom;
  46.     int iSysColorTo;
  47. };
  48.  
  49. AFX_STATIC_DATA const AFX_COLORMAP _afxSysColorMap[] =
  50. {
  51.     // mapping from color in DIB to system color
  52.     { RGB_TO_RGBQUAD(0x00, 0x00, 0x00),  COLOR_BTNTEXT },       // black
  53.     { RGB_TO_RGBQUAD(0x80, 0x80, 0x80),  COLOR_BTNSHADOW },     // dark gray
  54.     { RGB_TO_RGBQUAD(0xC0, 0xC0, 0xC0),  COLOR_BTNFACE },       // bright gray
  55.     { RGB_TO_RGBQUAD(0xFF, 0xFF, 0xFF),  COLOR_BTNHIGHLIGHT }   // white
  56. };
  57.  
  58. HBITMAP AFXAPI
  59. AfxLoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc, BOOL bMono)
  60. {
  61.     HGLOBAL hglb;
  62.     if ((hglb = LoadResource(hInst, hRsrc)) == NULL)
  63.         return NULL;
  64.  
  65.     LPBITMAPINFOHEADER lpBitmap = (LPBITMAPINFOHEADER)LockResource(hglb);
  66.     if (lpBitmap == NULL)
  67.         return NULL;
  68.  
  69.     // make copy of BITMAPINFOHEADER so we can modify the color table
  70.     const int nColorTableSize = 16;
  71.     UINT nSize = lpBitmap->biSize + nColorTableSize * sizeof(RGBQUAD);
  72.     LPBITMAPINFOHEADER lpBitmapInfo = (LPBITMAPINFOHEADER)::malloc(nSize);
  73.     if (lpBitmapInfo == NULL)
  74.         return NULL;
  75.     memcpy(lpBitmapInfo, lpBitmap, nSize);
  76.  
  77.     // color table is in RGBQUAD DIB format
  78.     DWORD* pColorTable =
  79.         (DWORD*)(((LPBYTE)lpBitmapInfo) + (UINT)lpBitmapInfo->biSize);
  80.  
  81.     for (int iColor = 0; iColor < nColorTableSize; iColor++)
  82.     {
  83.         // look for matching RGBQUAD color in original
  84.         for (int i = 0; i < _countof(_afxSysColorMap); i++)
  85.         {
  86.             if (pColorTable[iColor] == _afxSysColorMap[i].rgbqFrom)
  87.             {
  88.                 if (bMono)
  89.                 {
  90.                     // all colors except text become white
  91.                     if (_afxSysColorMap[i].iSysColorTo != COLOR_BTNTEXT)
  92.                         pColorTable[iColor] = RGB_TO_RGBQUAD(255, 255, 255);
  93.                 }
  94.                 else
  95.                     pColorTable[iColor] =
  96.                         CLR_TO_RGBQUAD(::GetSysColor(_afxSysColorMap[i].iSysColorTo));
  97.                 break;
  98.             }
  99.         }
  100.     }
  101.  
  102.     int nWidth = (int)lpBitmapInfo->biWidth;
  103.     int nHeight = (int)lpBitmapInfo->biHeight;
  104.     HDC hDCScreen = ::GetDC(NULL);
  105.     HBITMAP hbm = ::CreateCompatibleBitmap(hDCScreen, nWidth, nHeight);
  106.  
  107.     if (hbm != NULL)
  108.     {
  109.         HDC hDCGlyphs = ::CreateCompatibleDC(hDCScreen);
  110.         HBITMAP hbmOld = (HBITMAP)::SelectObject(hDCGlyphs, hbm);
  111.  
  112.         LPBYTE lpBits;
  113.         lpBits = (LPBYTE)(lpBitmap + 1);
  114.         lpBits += (1 << (lpBitmapInfo->biBitCount)) * sizeof(RGBQUAD);
  115.  
  116.         StretchDIBits(hDCGlyphs, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight,
  117.             lpBits, (LPBITMAPINFO)lpBitmapInfo, DIB_RGB_COLORS, SRCCOPY);
  118.         SelectObject(hDCGlyphs, hbmOld);
  119.         ::DeleteDC(hDCGlyphs);
  120.     }
  121.     ::ReleaseDC(NULL, hDCScreen);
  122.  
  123.     // free copy of bitmap info struct and resource itself
  124.     ::free(lpBitmapInfo);
  125.     ::FreeResource(hglb);
  126.  
  127.     return hbm;
  128. }
  129.  
  130. #ifdef AFX_INIT_SEG
  131. #pragma code_seg(AFX_INIT_SEG)
  132. #endif
  133.  
  134. struct AFX_DLLVERSIONINFO
  135. {
  136.         DWORD cbSize;
  137.         DWORD dwMajorVersion;                   // Major version
  138.         DWORD dwMinorVersion;                   // Minor version
  139.         DWORD dwBuildNumber;                    // Build number
  140.         DWORD dwPlatformID;                     // DLLVER_PLATFORM_*
  141. };
  142.  
  143. typedef HRESULT (CALLBACK* AFX_DLLGETVERSIONPROC)(AFX_DLLVERSIONINFO *);
  144.  
  145. int _afxComCtlVersion = -1;
  146.  
  147. DWORD AFXAPI _AfxGetComCtlVersion()
  148. {
  149.     // return cached version if already determined...
  150.     if (_afxComCtlVersion != -1)
  151.         return _afxComCtlVersion;
  152.  
  153.     // otherwise determine comctl32.dll version via DllGetVersion
  154.     HINSTANCE hInst = ::GetModuleHandleA("COMCTL32.DLL");
  155.     ASSERT(hInst != NULL);
  156.     AFX_DLLGETVERSIONPROC pfn;
  157.     pfn = (AFX_DLLGETVERSIONPROC)GetProcAddress(hInst, "DllGetVersion");
  158.     DWORD dwVersion = VERSION_WIN4;
  159.     if (pfn != NULL)
  160.     {
  161.         AFX_DLLVERSIONINFO dvi;
  162.         memset(&dvi, 0, sizeof(dvi));
  163.         dvi.cbSize = sizeof(dvi);
  164.         HRESULT hr = (*pfn)(&dvi);
  165.         if (SUCCEEDED(hr))
  166.         {
  167.             ASSERT(dvi.dwMajorVersion <= 0xFFFF);
  168.             ASSERT(dvi.dwMinorVersion <= 0xFFFF);
  169.             dwVersion = MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion);
  170.         }
  171.     }
  172.     _afxComCtlVersion = dwVersion;
  173.     return dwVersion;
  174. }
  175.  
  176. int _afxDropDownWidth = -1;
  177.  
  178. int AFXAPI _AfxGetDropDownWidth()
  179. {
  180.     // return cached version if already determined...
  181.     if (_afxDropDownWidth != -1)
  182.         return _afxDropDownWidth;
  183.  
  184.     // otherwise calculate it...
  185.     HDC hDC = GetDC(NULL);
  186.     ASSERT(hDC != NULL);
  187.     HFONT hFont;
  188.     if ((hFont = CreateFont(GetSystemMetrics(SM_CYMENUCHECK), 0, 0, 0,
  189.         FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, _T("Marlett"))) != NULL)
  190.         hFont = (HFONT)SelectObject(hDC, hFont);
  191.     VERIFY(GetCharWidth(hDC, '6', '6', &_afxDropDownWidth));
  192.     if (hFont != NULL)
  193.     {
  194.         SelectObject(hDC, hFont);
  195.         DeleteObject(hFont);
  196.     }
  197.     ReleaseDC(NULL, hDC);
  198.     ASSERT(_afxDropDownWidth != -1);
  199.     return _afxDropDownWidth;
  200. }
  201.  
  202. CToolBar::CToolBar()
  203. {
  204.     // initialize state
  205.     m_pStringMap = NULL;
  206.     m_hRsrcImageWell = NULL;
  207.     m_hInstImageWell = NULL;
  208.     m_hbmImageWell = NULL;
  209.     m_bDelayedButtonLayout = TRUE;
  210.  
  211.     // default image sizes
  212.     m_sizeImage.cx = 16;
  213.     m_sizeImage.cy = 15;
  214.  
  215.     // default button sizes
  216.     m_sizeButton.cx = 23;
  217.     m_sizeButton.cy = 22;
  218.  
  219.     // top and bottom borders are 1 larger than default for ease of grabbing
  220.     m_cyTopBorder = 3;
  221.     m_cyBottomBorder = 3;
  222. }
  223.  
  224. CToolBar::~CToolBar()
  225. {
  226.     AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell);
  227.     delete m_pStringMap;
  228.  
  229.     m_nCount = 0;
  230. }
  231.  
  232. BOOL CToolBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
  233. {
  234.     return CreateEx(pParentWnd, 0, dwStyle,
  235.         CRect(m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder), nID);
  236. }
  237.  
  238. BOOL CToolBar::CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle, DWORD dwStyle, CRect rcBorders, UINT nID)
  239. {
  240.     ASSERT_VALID(pParentWnd);   // must have a parent
  241.     ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC)));
  242.  
  243.     SetBorders(rcBorders);
  244.  
  245.     // save the style
  246.     m_dwStyle = (dwStyle & CBRS_ALL);
  247.     if (nID == AFX_IDW_TOOLBAR)
  248.         m_dwStyle |= CBRS_HIDE_INPLACE;
  249.  
  250.     dwStyle &= ~CBRS_ALL;
  251.     dwStyle |= CCS_NOPARENTALIGN|CCS_NOMOVEY|CCS_NODIVIDER|CCS_NORESIZE;
  252.     dwStyle |= dwCtrlStyle;
  253.  
  254.     // initialize common controls
  255.     VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTL_BAR_REG));
  256.     _AfxGetComCtlVersion();
  257.     ASSERT(_afxComCtlVersion != -1);
  258.     _AfxGetDropDownWidth();
  259.     ASSERT(_afxDropDownWidth != -1);
  260.  
  261.     // create the HWND
  262.     CRect rect; rect.SetRectEmpty();
  263.     if (!CWnd::Create(TOOLBARCLASSNAME, NULL, dwStyle, rect, pParentWnd, nID))
  264.         return FALSE;
  265.  
  266.     // sync up the sizes
  267.     SetSizes(m_sizeButton, m_sizeImage);
  268.  
  269.     // Note: Parent must resize itself for control bar to be resized
  270.  
  271.     return TRUE;
  272. }
  273.  
  274. /////////////////////////////////////////////////////////////////////////////
  275. // CToolBar
  276.  
  277. BOOL CToolBar::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
  278. {
  279.     if (!CControlBar::OnNcCreate(lpCreateStruct))
  280.         return FALSE;
  281.  
  282.     // if the owner was set before the toolbar was created, set it now
  283.     if (m_hWndOwner != NULL)
  284.         DefWindowProc(TB_SETPARENT, (WPARAM)m_hWndOwner, 0);
  285.  
  286.     DefWindowProc(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
  287.     return TRUE;
  288. }
  289.  
  290. void CToolBar::SetOwner(CWnd* pOwnerWnd)
  291. {
  292.     ASSERT_VALID(this);
  293.     if (m_hWnd != NULL)
  294.     {
  295.         ASSERT(::IsWindow(m_hWnd));
  296.         DefWindowProc(TB_SETPARENT, (WPARAM)pOwnerWnd->GetSafeHwnd(), 0);
  297.     }
  298.     CControlBar::SetOwner(pOwnerWnd);
  299. }
  300.  
  301. void CToolBar::SetSizes(SIZE sizeButton, SIZE sizeImage)
  302. {
  303.     ASSERT_VALID(this);
  304.  
  305.     // sizes must be non-zero and positive
  306.     ASSERT(sizeButton.cx > 0 && sizeButton.cy > 0);
  307.     ASSERT(sizeImage.cx > 0 && sizeImage.cy > 0);
  308.  
  309.     // button must be big enough to hold image
  310.     //   + 7 pixels on x
  311.     //   + 6 pixels on y
  312.     ASSERT(sizeButton.cx >= sizeImage.cx + 7);
  313.     ASSERT(sizeButton.cy >= sizeImage.cy + 6);
  314.  
  315.     if (::IsWindow(m_hWnd))
  316.     {
  317.         // set the sizes via TB_SETBITMAPSIZE and TB_SETBUTTONSIZE
  318.         VERIFY(SendMessage(TB_SETBITMAPSIZE, 0, MAKELONG(sizeImage.cx, sizeImage.cy)));
  319.         VERIFY(SendMessage(TB_SETBUTTONSIZE, 0, MAKELONG(sizeButton.cx, sizeButton.cy)));
  320.  
  321.         Invalidate();   // just to be nice if called when toolbar is visible
  322.     }
  323.     else
  324.     {
  325.         // just set our internal values for later
  326.         m_sizeButton = sizeButton;
  327.         m_sizeImage = sizeImage;
  328.     }
  329. }
  330.  
  331. void CToolBar::SetHeight(int cyHeight)
  332. {
  333.     ASSERT_VALID(this);
  334.  
  335.     int nHeight = cyHeight;
  336.     if (m_dwStyle & CBRS_BORDER_TOP)
  337.         cyHeight -= afxData.cyBorder2;
  338.     if (m_dwStyle & CBRS_BORDER_BOTTOM)
  339.         cyHeight -= afxData.cyBorder2;
  340.     m_cyBottomBorder = (cyHeight - m_sizeButton.cy) / 2;
  341.     // if there is an extra pixel, m_cyTopBorder will get it
  342.     m_cyTopBorder = cyHeight - m_sizeButton.cy - m_cyBottomBorder;
  343.     if (m_cyTopBorder < 0)
  344.     {
  345.         TRACE1("Warning: CToolBar::SetHeight(%d) is smaller than button.\n",
  346.             nHeight);
  347.         m_cyBottomBorder += m_cyTopBorder;
  348.         m_cyTopBorder = 0;  // will clip at bottom
  349.     }
  350.  
  351.     // recalculate the non-client region
  352.     SetWindowPos(NULL, 0, 0, 0, 0,
  353.         SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOZORDER);
  354.     Invalidate();   // just to be nice if called when toolbar is visible
  355. }
  356.  
  357. struct CToolBarData
  358. {
  359.     WORD wVersion;
  360.     WORD wWidth;
  361.     WORD wHeight;
  362.     WORD wItemCount;
  363.     //WORD aItems[wItemCount]
  364.  
  365.     WORD* items()
  366.         { return (WORD*)(this+1); }
  367. };
  368.  
  369. BOOL CToolBar::LoadToolBar(LPCTSTR lpszResourceName)
  370. {
  371.     ASSERT_VALID(this);
  372.     ASSERT(lpszResourceName != NULL);
  373.  
  374.     // determine location of the bitmap in resource fork
  375.     HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR);
  376.     HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR);
  377.     if (hRsrc == NULL)
  378.         return FALSE;
  379.  
  380.     HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
  381.     if (hGlobal == NULL)
  382.         return FALSE;
  383.  
  384.     CToolBarData* pData = (CToolBarData*)LockResource(hGlobal);
  385.     if (pData == NULL)
  386.         return FALSE;
  387.     ASSERT(pData->wVersion == 1);
  388.  
  389.     UINT* pItems = new UINT[pData->wItemCount];
  390.     for (int i = 0; i < pData->wItemCount; i++)
  391.         pItems[i] = pData->items()[i];
  392.     BOOL bResult = SetButtons(pItems, pData->wItemCount);
  393.     delete[] pItems;
  394.  
  395.     if (bResult)
  396.     {
  397.         // set new sizes of the buttons
  398.         CSize sizeImage(pData->wWidth, pData->wHeight);
  399.         CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7);
  400.         SetSizes(sizeButton, sizeImage);
  401.  
  402.         // load bitmap now that sizes are known by the toolbar control
  403.         bResult = LoadBitmap(lpszResourceName);
  404.     }
  405.  
  406.     UnlockResource(hGlobal);
  407.     FreeResource(hGlobal);
  408.  
  409.     return bResult;
  410. }
  411.  
  412. BOOL CToolBar::LoadBitmap(LPCTSTR lpszResourceName)
  413. {
  414.     ASSERT_VALID(this);
  415.     ASSERT(lpszResourceName != NULL);
  416.  
  417.     // determine location of the bitmap in resource fork
  418.     HINSTANCE hInstImageWell = AfxFindResourceHandle(lpszResourceName, RT_BITMAP);
  419.     HRSRC hRsrcImageWell = ::FindResource(hInstImageWell, lpszResourceName, RT_BITMAP);
  420.     if (hRsrcImageWell == NULL)
  421.         return FALSE;
  422.  
  423.     // load the bitmap
  424.     HBITMAP hbmImageWell;
  425.     hbmImageWell = AfxLoadSysColorBitmap(hInstImageWell, hRsrcImageWell);
  426.  
  427.     // tell common control toolbar about the new bitmap
  428.     if (!AddReplaceBitmap(hbmImageWell))
  429.         return FALSE;
  430.  
  431.     // remember the resource handles so the bitmap can be recolored if necessary
  432.     m_hInstImageWell = hInstImageWell;
  433.     m_hRsrcImageWell = hRsrcImageWell;
  434.     return TRUE;
  435. }
  436.  
  437. BOOL CToolBar::SetBitmap(HBITMAP hbmImageWell)
  438. {
  439.     ASSERT_VALID(this);
  440.     ASSERT(hbmImageWell != NULL);
  441.  
  442.     // the caller must manage changing system colors
  443.     m_hInstImageWell = NULL;
  444.     m_hRsrcImageWell = NULL;
  445.  
  446.     // tell common control toolbar about the new bitmap
  447.     return AddReplaceBitmap(hbmImageWell);
  448. }
  449.  
  450. BOOL CToolBar::AddReplaceBitmap(HBITMAP hbmImageWell)
  451. {
  452.     // need complete bitmap size to determine number of images
  453.     BITMAP bitmap;
  454.     VERIFY(::GetObject(hbmImageWell, sizeof(BITMAP), &bitmap));
  455.  
  456.     // add the bitmap to the common control toolbar
  457.     BOOL bResult;
  458.     if (m_hbmImageWell == NULL)
  459.     {
  460.         TBADDBITMAP addBitmap;
  461.         addBitmap.hInst = NULL; // makes TBADDBITMAP::nID behave a HBITMAP
  462.         addBitmap.nID = (UINT)hbmImageWell;
  463.         bResult =  DefWindowProc(TB_ADDBITMAP,
  464.             bitmap.bmWidth / m_sizeImage.cx, (LPARAM)&addBitmap) == 0;
  465.     }
  466.     else
  467.     {
  468.         TBREPLACEBITMAP replaceBitmap;
  469.         replaceBitmap.hInstOld = NULL;
  470.         replaceBitmap.nIDOld = (UINT)m_hbmImageWell;
  471.         replaceBitmap.hInstNew = NULL;
  472.         replaceBitmap.nIDNew = (UINT)hbmImageWell;
  473.         replaceBitmap.nButtons = bitmap.bmWidth / m_sizeImage.cx;
  474.         bResult = (BOOL)DefWindowProc(TB_REPLACEBITMAP, 0,
  475.             (LPARAM)&replaceBitmap);
  476.     }
  477.     // remove old bitmap, if present
  478.     if (bResult)
  479.     {
  480.         AfxDeleteObject((HGDIOBJ*)&m_hbmImageWell);
  481.         m_hbmImageWell = hbmImageWell;
  482.     }
  483.  
  484.     return bResult;
  485. }
  486.  
  487. BOOL CToolBar::SetButtons(const UINT* lpIDArray, int nIDCount)
  488. {
  489.     ASSERT_VALID(this);
  490.     ASSERT(nIDCount >= 1);  // must be at least one of them
  491.     ASSERT(lpIDArray == NULL ||
  492.         AfxIsValidAddress(lpIDArray, sizeof(UINT) * nIDCount, FALSE));
  493.  
  494.     // delete all existing buttons
  495.     int nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0);
  496.     while (nCount--)
  497.         VERIFY(DefWindowProc(TB_DELETEBUTTON, 0, 0));
  498.  
  499.     TBBUTTON button; memset(&button, 0, sizeof(TBBUTTON));
  500.     button.iString = -1;
  501.     if (lpIDArray != NULL)
  502.     {
  503.         // add new buttons to the common control
  504.         int iImage = 0;
  505.         for (int i = 0; i < nIDCount; i++)
  506.         {
  507.             button.fsState = TBSTATE_ENABLED;
  508.             if ((button.idCommand = *lpIDArray++) == 0)
  509.             {
  510.                 // separator
  511.                 button.fsStyle = TBSTYLE_SEP;
  512.                 // width of separator includes 8 pixel overlap
  513.                 ASSERT(_afxComCtlVersion != -1);
  514.                 if ((GetStyle() & TBSTYLE_FLAT) || _afxComCtlVersion == VERSION_IE4)
  515.                     button.iBitmap = 6;
  516.                 else
  517.                     button.iBitmap = 8;
  518.             }
  519.             else
  520.             {
  521.                 // a command button with image
  522.                 button.fsStyle = TBSTYLE_BUTTON;
  523.                 button.iBitmap = iImage++;
  524.             }
  525.             if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button))
  526.                 return FALSE;
  527.         }
  528.     }
  529.     else
  530.     {
  531.         // add 'blank' buttons
  532.         button.fsState = TBSTATE_ENABLED;
  533.         for (int i = 0; i < nIDCount; i++)
  534.         {
  535.             ASSERT(button.fsStyle == TBSTYLE_BUTTON);
  536.             if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button))
  537.                 return FALSE;
  538.         }
  539.     }
  540.     m_nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0);
  541.     m_bDelayedButtonLayout = TRUE;
  542.  
  543.     return TRUE;
  544. }
  545.  
  546. #ifdef AFX_CORE3_SEG
  547. #pragma code_seg(AFX_CORE3_SEG)
  548. #endif
  549.  
  550. /////////////////////////////////////////////////////////////////////////////
  551. // CToolBar attribute access
  552.  
  553. void CToolBar::_GetButton(int nIndex, TBBUTTON* pButton) const
  554. {
  555.     CToolBar* pBar = (CToolBar*)this;
  556.     VERIFY(pBar->DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)pButton));
  557.     // TBSTATE_ENABLED == TBBS_DISABLED so invert it
  558.     pButton->fsState ^= TBSTATE_ENABLED;
  559. }
  560.  
  561. void CToolBar::_SetButton(int nIndex, TBBUTTON* pButton)
  562. {
  563.     // get original button state
  564.     TBBUTTON button;
  565.     VERIFY(DefWindowProc(TB_GETBUTTON, nIndex, (LPARAM)&button));
  566.  
  567.     // prepare for old/new button comparsion
  568.     button.bReserved[0] = 0;
  569.     button.bReserved[1] = 0;
  570.     // TBSTATE_ENABLED == TBBS_DISABLED so invert it
  571.     pButton->fsState ^= TBSTATE_ENABLED;
  572.     pButton->bReserved[0] = 0;
  573.     pButton->bReserved[1] = 0;
  574.  
  575.     // nothing to do if they are the same
  576.     if (memcmp(pButton, &button, sizeof(TBBUTTON)) != 0)
  577.     {
  578.         // don't redraw everything while setting the button
  579.         DWORD dwStyle = GetStyle();
  580.         ModifyStyle(WS_VISIBLE, 0);
  581.         VERIFY(DefWindowProc(TB_DELETEBUTTON, nIndex, 0));
  582.         VERIFY(DefWindowProc(TB_INSERTBUTTON, nIndex, (LPARAM)pButton));
  583.         ModifyStyle(0, dwStyle & WS_VISIBLE);
  584.  
  585.         // invalidate appropriate parts
  586.         if (((pButton->fsStyle ^ button.fsStyle) & TBSTYLE_SEP) ||
  587.             ((pButton->fsStyle & TBSTYLE_SEP) && pButton->iBitmap != button.iBitmap))
  588.         {
  589.             // changing a separator
  590.             Invalidate();
  591.         }
  592.         else
  593.         {
  594.             // invalidate just the button
  595.             CRect rect;
  596.             if (DefWindowProc(TB_GETITEMRECT, nIndex, (LPARAM)&rect))
  597.                 InvalidateRect(rect);
  598.         }
  599.     }
  600. }
  601.  
  602. int CToolBar::CommandToIndex(UINT nIDFind) const
  603. {
  604.     ASSERT_VALID(this);
  605.     ASSERT(::IsWindow(m_hWnd));
  606.  
  607.     CToolBar* pBar = (CToolBar*)this;
  608.     return (int)pBar->DefWindowProc(TB_COMMANDTOINDEX, nIDFind, 0);
  609. }
  610.  
  611. UINT CToolBar::GetItemID(int nIndex) const
  612. {
  613.     ASSERT_VALID(this);
  614.     ASSERT(::IsWindow(m_hWnd));
  615.  
  616.     TBBUTTON button;
  617.     _GetButton(nIndex, &button);
  618.     return button.idCommand;
  619. }
  620.  
  621. void CToolBar::GetItemRect(int nIndex, LPRECT lpRect) const
  622. {
  623.     ASSERT_VALID(this);
  624.     ASSERT(::IsWindow(m_hWnd));
  625.  
  626.     // handle any delayed layout
  627.     if (m_bDelayedButtonLayout)
  628.         ((CToolBar*)this)->Layout();
  629.  
  630.     // now it is safe to get the item rectangle
  631.     CToolBar* pBar = (CToolBar*)this;
  632.     if (!pBar->DefWindowProc(TB_GETITEMRECT, nIndex, (LPARAM)lpRect))
  633.         SetRectEmpty(lpRect);
  634. }
  635.  
  636. void CToolBar::Layout()
  637. {
  638.     ASSERT(m_bDelayedButtonLayout);
  639.  
  640.     m_bDelayedButtonLayout = FALSE;
  641.  
  642.     BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0;
  643.     if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC))
  644.         ((CToolBar*)this)->CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH | LM_COMMIT);
  645.     else if (bHorz)
  646.         ((CToolBar*)this)->CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK | LM_COMMIT);
  647.     else
  648.         ((CToolBar*)this)->CalcDynamicLayout(0, LM_VERTDOCK | LM_COMMIT);
  649. }
  650.  
  651. UINT CToolBar::GetButtonStyle(int nIndex) const
  652. {
  653.     ASSERT_VALID(this);
  654.     ASSERT(::IsWindow(m_hWnd));
  655.  
  656.     TBBUTTON button;
  657.     _GetButton(nIndex, &button);
  658.     return MAKELONG(button.fsStyle, button.fsState);
  659. }
  660.  
  661. void CToolBar::SetButtonStyle(int nIndex, UINT nStyle)
  662. {
  663.     ASSERT_VALID(this);
  664.     ASSERT(::IsWindow(m_hWnd));
  665.  
  666.     TBBUTTON button;
  667.     _GetButton(nIndex, &button);
  668.     if (button.fsStyle != (BYTE)LOWORD(nStyle) || button.fsState != (BYTE)HIWORD(nStyle))
  669.     {
  670.         button.fsStyle = (BYTE)LOWORD(nStyle);
  671.         button.fsState = (BYTE)HIWORD(nStyle);
  672.         _SetButton(nIndex, &button);
  673.         m_bDelayedButtonLayout = TRUE;
  674.     }
  675. }
  676.  
  677. #define CX_OVERLAP  0
  678.  
  679. CSize CToolBar::CalcSize(TBBUTTON* pData, int nCount)
  680. {
  681.     ASSERT(pData != NULL && nCount > 0);
  682.  
  683.     CPoint cur(0,0);
  684.     CSize sizeResult(0,0);
  685.  
  686.     DWORD dwExtendedStyle = DefWindowProc(TB_GETEXTENDEDSTYLE, 0, 0);
  687.  
  688.     for (int i = 0; i < nCount; i++)
  689.     {
  690.         //WINBUG: The IE4 version of COMCTL32.DLL calculates the separation
  691.         //  on a TBSTYLE_WRAP button as 100% of the value in iBitmap compared
  692.         //  to the other versions which calculate it at 2/3 of that value.
  693.         //  This is actually a bug which should be fixed in IE 4.01, so we
  694.         //  only do the 100% calculation specifically for IE4.
  695.         int cySep = pData[i].iBitmap;
  696.         ASSERT(_afxComCtlVersion != -1);
  697.         if (!(GetStyle() & TBSTYLE_FLAT) && _afxComCtlVersion != VERSION_IE4)
  698.             cySep = cySep * 2 / 3;
  699.  
  700.         if (pData[i].fsState & TBSTATE_HIDDEN)
  701.             continue;
  702.  
  703.         int cx = m_sizeButton.cx;
  704.         if (pData[i].fsStyle & TBSTYLE_SEP)
  705.         {
  706.             // a separator represents either a height or width
  707.             if (pData[i].fsState & TBSTATE_WRAP)
  708.                 sizeResult.cy = max(cur.y + m_sizeButton.cy + cySep, sizeResult.cy);
  709.             else
  710.                 sizeResult.cx = max(cur.x + pData[i].iBitmap, sizeResult.cx);
  711.         }
  712.         else
  713.         {
  714.             // check for dropdown style, but only if the buttons are being drawn
  715.             if ((pData[i].fsStyle & TBSTYLE_DROPDOWN) &&
  716.                 (dwExtendedStyle & TBSTYLE_EX_DRAWDDARROWS))
  717.             {
  718.                 // add size of drop down
  719.                 ASSERT(_afxDropDownWidth != -1);
  720.                 cx += _afxDropDownWidth;
  721.             }
  722.             sizeResult.cx = max(cur.x + cx, sizeResult.cx);
  723.             sizeResult.cy = max(cur.y + m_sizeButton.cy, sizeResult.cy);
  724.         }
  725.  
  726.         if (pData[i].fsStyle & TBSTYLE_SEP)
  727.             cur.x += pData[i].iBitmap;
  728.         else
  729.             cur.x += cx - CX_OVERLAP;
  730.  
  731.         if (pData[i].fsState & TBSTATE_WRAP)
  732.         {
  733.             cur.x = 0;
  734.             cur.y += m_sizeButton.cy;
  735.             if (pData[i].fsStyle & TBSTYLE_SEP)
  736.                 cur.y += cySep;
  737.         }
  738.     }
  739.     return sizeResult;
  740. }
  741.  
  742. int CToolBar::WrapToolBar(TBBUTTON* pData, int nCount, int nWidth)
  743. {
  744.     ASSERT(pData != NULL && nCount > 0);
  745.  
  746.     int nResult = 0;
  747.     int x = 0;
  748.     for (int i = 0; i < nCount; i++)
  749.     {
  750.         pData[i].fsState &= ~TBSTATE_WRAP;
  751.  
  752.         if (pData[i].fsState & TBSTATE_HIDDEN)
  753.             continue;
  754.  
  755.         int dx, dxNext;
  756.         if (pData[i].fsStyle & TBSTYLE_SEP)
  757.         {
  758.             dx = pData[i].iBitmap;
  759.             dxNext = dx;
  760.         }
  761.         else
  762.         {
  763.             dx = m_sizeButton.cx;
  764.             dxNext = dx - CX_OVERLAP;
  765.         }
  766.  
  767.         if (x + dx > nWidth)
  768.         {
  769.             BOOL bFound = FALSE;
  770.             for (int j = i; j >= 0  &&  !(pData[j].fsState & TBSTATE_WRAP); j--)
  771.             {
  772.                 // Find last separator that isn't hidden
  773.                 // a separator that has a command ID is not
  774.                 // a separator, but a custom control.
  775.                 if ((pData[j].fsStyle & TBSTYLE_SEP) &&
  776.                     (pData[j].idCommand == 0) &&
  777.                     !(pData[j].fsState & TBSTATE_HIDDEN))
  778.                 {
  779.                     bFound = TRUE; i = j; x = 0;
  780.                     pData[j].fsState |= TBSTATE_WRAP;
  781.                     nResult++;
  782.                     break;
  783.                 }
  784.             }
  785.             if (!bFound)
  786.             {
  787.                 for (int j = i - 1; j >= 0 && !(pData[j].fsState & TBSTATE_WRAP); j--)
  788.                 {
  789.                     // Never wrap anything that is hidden,
  790.                     // or any custom controls
  791.                     if ((pData[j].fsState & TBSTATE_HIDDEN) ||
  792.                         ((pData[j].fsStyle & TBSTYLE_SEP) &&
  793.                         (pData[j].idCommand != 0)))
  794.                         continue;
  795.  
  796.                     bFound = TRUE; i = j; x = 0;
  797.                     pData[j].fsState |= TBSTATE_WRAP;
  798.                     nResult++;
  799.                     break;
  800.                 }
  801.                 if (!bFound)
  802.                     x += dxNext;
  803.             }
  804.         }
  805.         else
  806.             x += dxNext;
  807.     }
  808.     return nResult + 1;
  809. }
  810.  
  811. void  CToolBar::SizeToolBar(TBBUTTON* pData, int nCount, int nLength, BOOL bVert)
  812. {
  813.     ASSERT(pData != NULL && nCount > 0);
  814.  
  815.     if (!bVert)
  816.     {
  817.         int nMin, nMax, nTarget, nCurrent, nMid;
  818.  
  819.         // Wrap ToolBar as specified
  820.         nMax = nLength;
  821.         nTarget = WrapToolBar(pData, nCount, nMax);
  822.  
  823.         // Wrap ToolBar vertically
  824.         nMin = 0;
  825.         nCurrent = WrapToolBar(pData, nCount, nMin);
  826.  
  827.         if (nCurrent != nTarget)
  828.         {
  829.             while (nMin < nMax)
  830.             {
  831.                 nMid = (nMin + nMax) / 2;
  832.                 nCurrent = WrapToolBar(pData, nCount, nMid);
  833.  
  834.                 if (nCurrent == nTarget)
  835.                     nMax = nMid;
  836.                 else
  837.                 {
  838.                     if (nMin == nMid)
  839.                     {
  840.                         WrapToolBar(pData, nCount, nMax);
  841.                         break;
  842.                     }
  843.                     nMin = nMid;
  844.                 }
  845.             }
  846.         }
  847.         CSize size = CalcSize(pData, nCount);
  848.         WrapToolBar(pData, nCount, size.cx);
  849.     }
  850.     else
  851.     {
  852.         CSize sizeMax, sizeMin, sizeMid;
  853.  
  854.         // Wrap ToolBar vertically
  855.         WrapToolBar(pData, nCount, 0);
  856.         sizeMin = CalcSize(pData, nCount);
  857.  
  858.         // Wrap ToolBar horizontally
  859.         WrapToolBar(pData, nCount, 32767);
  860.         sizeMax = CalcSize(pData, nCount);
  861.  
  862.         while (sizeMin.cx < sizeMax.cx)
  863.         {
  864.             sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2;
  865.             WrapToolBar(pData, nCount, sizeMid.cx);
  866.             sizeMid = CalcSize(pData, nCount);
  867.  
  868.             if (nLength < sizeMid.cy)
  869.             {
  870.                 if (sizeMin == sizeMid)
  871.                 {
  872.                     WrapToolBar(pData, nCount, sizeMax.cx);
  873.                     return;
  874.                 }
  875.                 sizeMin = sizeMid;
  876.             }
  877.             else if (nLength > sizeMid.cy)
  878.                 sizeMax = sizeMid;
  879.             else
  880.                 return;
  881.         }
  882.     }
  883. }
  884.  
  885. struct _AFX_CONTROLPOS
  886. {
  887.     int nIndex, nID;
  888.     CRect rectOldPos;
  889. };
  890.  
  891. CSize CToolBar::CalcLayout(DWORD dwMode, int nLength)
  892. {
  893.     ASSERT_VALID(this);
  894.     ASSERT(::IsWindow(m_hWnd));
  895.     if (dwMode & LM_HORZDOCK)
  896.         ASSERT(dwMode & LM_HORZ);
  897.  
  898.     int nCount;
  899.     TBBUTTON* pData = NULL;
  900.     CSize sizeResult(0,0);
  901.  
  902.     //BLOCK: Load Buttons
  903.     {
  904.         nCount = DefWindowProc(TB_BUTTONCOUNT, 0, 0);
  905.         if (nCount != 0)
  906.         {
  907.             int i;
  908.             pData = new TBBUTTON[nCount];
  909.             for (i = 0; i < nCount; i++)
  910.                 _GetButton(i, &pData[i]);
  911.         }
  912.     }
  913.  
  914.     if (nCount > 0)
  915.     {
  916.         if (!(m_dwStyle & CBRS_SIZE_FIXED))
  917.         {
  918.             BOOL bDynamic = m_dwStyle & CBRS_SIZE_DYNAMIC;
  919.  
  920.             if (bDynamic && (dwMode & LM_MRUWIDTH))
  921.                 SizeToolBar(pData, nCount, m_nMRUWidth);
  922.             else if (bDynamic && (dwMode & LM_HORZDOCK))
  923.                 SizeToolBar(pData, nCount, 32767);
  924.             else if (bDynamic && (dwMode & LM_VERTDOCK))
  925.                 SizeToolBar(pData, nCount, 0);
  926.             else if (bDynamic && (nLength != -1))
  927.             {
  928.                 CRect rect; rect.SetRectEmpty();
  929.                 CalcInsideRect(rect, (dwMode & LM_HORZ));
  930.                 BOOL bVert = (dwMode & LM_LENGTHY);
  931.                 int nLen = nLength + (bVert ? rect.Height() : rect.Width());
  932.  
  933.                 SizeToolBar(pData, nCount, nLen, bVert);
  934.             }
  935.             else if (bDynamic && (m_dwStyle & CBRS_FLOATING))
  936.                 SizeToolBar(pData, nCount, m_nMRUWidth);
  937.             else
  938.                 SizeToolBar(pData, nCount, (dwMode & LM_HORZ) ? 32767 : 0);
  939.         }
  940.  
  941.         sizeResult = CalcSize(pData, nCount);
  942.  
  943.         if (dwMode & LM_COMMIT)
  944.         {
  945.             _AFX_CONTROLPOS* pControl = NULL;
  946.             int nControlCount = 0;
  947.             BOOL bIsDelayed = m_bDelayedButtonLayout;
  948.             m_bDelayedButtonLayout = FALSE;
  949.  
  950.             for (int i = 0; i < nCount; i++)
  951.                 if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0))
  952.                     nControlCount++;
  953.  
  954.             if (nControlCount > 0)
  955.             {
  956.                 pControl = new _AFX_CONTROLPOS[nControlCount];
  957.                 nControlCount = 0;
  958.  
  959.                 for(int i = 0; i < nCount; i++)
  960.                 {
  961.                     if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0))
  962.                     {
  963.                         pControl[nControlCount].nIndex = i;
  964.                         pControl[nControlCount].nID = pData[i].idCommand;
  965.  
  966.                         CRect rect;
  967.                         GetItemRect(i, &rect);
  968.                         ClientToScreen(&rect);
  969.                         pControl[nControlCount].rectOldPos = rect;
  970.  
  971.                         nControlCount++;
  972.                     }
  973.                 }
  974.             }
  975.  
  976.             if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC))
  977.                 m_nMRUWidth = sizeResult.cx;
  978.             for (i = 0; i < nCount; i++)
  979.                 _SetButton(i, &pData[i]);
  980.  
  981.             if (nControlCount > 0)
  982.             {
  983.                 for (int i = 0; i < nControlCount; i++)
  984.                 {
  985.                     CWnd* pWnd = GetDlgItem(pControl[i].nID);
  986.                     if (pWnd != NULL)
  987.                     {
  988.                         CRect rect;
  989.                         pWnd->GetWindowRect(&rect);
  990.                         CPoint pt = rect.TopLeft() - pControl[i].rectOldPos.TopLeft();
  991.                         GetItemRect(pControl[i].nIndex, &rect);
  992.                         pt = rect.TopLeft() + pt;
  993.                         pWnd->SetWindowPos(NULL, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  994.                     }
  995.                 }
  996.                 delete[] pControl;
  997.             }
  998.             m_bDelayedButtonLayout = bIsDelayed;
  999.         }
  1000.         delete[] pData;
  1001.     }
  1002.  
  1003.     //BLOCK: Adjust Margins
  1004.     {
  1005.         CRect rect; rect.SetRectEmpty();
  1006.         CalcInsideRect(rect, (dwMode & LM_HORZ));
  1007.         sizeResult.cy -= rect.Height();
  1008.         sizeResult.cx -= rect.Width();
  1009.  
  1010.         CSize size = CControlBar::CalcFixedLayout((dwMode & LM_STRETCH), (dwMode & LM_HORZ));
  1011.         sizeResult.cx = max(sizeResult.cx, size.cx);
  1012.         sizeResult.cy = max(sizeResult.cy, size.cy);
  1013.     }
  1014.     return sizeResult;
  1015. }
  1016.  
  1017. CSize CToolBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
  1018. {
  1019.     DWORD dwMode = bStretch ? LM_STRETCH : 0;
  1020.     dwMode |= bHorz ? LM_HORZ : 0;
  1021.  
  1022.     return CalcLayout(dwMode);
  1023. }
  1024.  
  1025. CSize CToolBar::CalcDynamicLayout(int nLength, DWORD dwMode)
  1026. {
  1027.     if ((nLength == -1) && !(dwMode & LM_MRUWIDTH) && !(dwMode & LM_COMMIT) &&
  1028.         ((dwMode & LM_HORZDOCK) || (dwMode & LM_VERTDOCK)))
  1029.     {
  1030.         return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZDOCK);
  1031.     }
  1032.     return CalcLayout(dwMode, nLength);
  1033. }
  1034.  
  1035. void CToolBar::GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const
  1036. {
  1037.     ASSERT_VALID(this);
  1038.     ASSERT(::IsWindow(m_hWnd));
  1039.  
  1040.     TBBUTTON button;
  1041.     _GetButton(nIndex, &button);
  1042.     nID = button.idCommand;
  1043.     nStyle = MAKELONG(button.fsStyle, button.fsState);
  1044.     iImage = button.iBitmap;
  1045. }
  1046.  
  1047. void CToolBar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage)
  1048. {
  1049.     ASSERT_VALID(this);
  1050.  
  1051.     TBBUTTON button;
  1052.     _GetButton(nIndex, &button);
  1053.     TBBUTTON save;
  1054.     memcpy(&save, &button, sizeof(save));
  1055.     button.idCommand = nID;
  1056.     button.iBitmap = iImage;
  1057.     button.fsStyle = (BYTE)LOWORD(nStyle);
  1058.     button.fsState = (BYTE)HIWORD(nStyle);
  1059.     if (memcmp(&save, &button, sizeof(save)) != 0)
  1060.     {
  1061.         _SetButton(nIndex, &button);
  1062.         m_bDelayedButtonLayout = TRUE;
  1063.     }
  1064. }
  1065.  
  1066. int CToolBar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
  1067. {
  1068.     ASSERT_VALID(this);
  1069.     ASSERT(::IsWindow(m_hWnd));
  1070.  
  1071.     // check child windows first by calling CControlBar
  1072.     int nHit = CControlBar::OnToolHitTest(point, pTI);
  1073.     if (nHit != -1)
  1074.         return nHit;
  1075.  
  1076.     // now hit test against CToolBar buttons
  1077.     CToolBar* pBar = (CToolBar*)this;
  1078.     int nButtons = (int)pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0);
  1079.     for (int i = 0; i < nButtons; i++)
  1080.     {
  1081.         CRect rect;
  1082.         TBBUTTON button;
  1083.         if (pBar->DefWindowProc(TB_GETITEMRECT, i, (LPARAM)&rect))
  1084.         {
  1085.             ++rect.bottom;
  1086.             ++rect.right;
  1087.             if (rect.PtInRect(point) &&
  1088.                 pBar->DefWindowProc(TB_GETBUTTON, i, (LPARAM)&button) &&
  1089.                 !(button.fsStyle & TBSTYLE_SEP))
  1090.             {
  1091.                 int nHit = GetItemID(i);
  1092.                 if (pTI != NULL && pTI->cbSize >= sizeof(AFX_OLDTOOLINFO))
  1093.                 {
  1094.                     pTI->hwnd = m_hWnd;
  1095.                     pTI->rect = rect;
  1096.                     pTI->uId = nHit;
  1097.                     pTI->lpszText = LPSTR_TEXTCALLBACK;
  1098.                 }
  1099.                 // found matching rect, return the ID of the button
  1100.                 return nHit != 0 ? nHit : -1;
  1101.             }
  1102.         }
  1103.     }
  1104.     return -1;
  1105. }
  1106.  
  1107. BOOL CToolBar::SetButtonText(int nIndex, LPCTSTR lpszText)
  1108. {
  1109.     // attempt to lookup string index in map
  1110.     int nString = -1;
  1111.     void* p;
  1112.     if (m_pStringMap != NULL && m_pStringMap->Lookup(lpszText, p))
  1113.         nString = (int)p;
  1114.  
  1115.     // add new string if not already in map
  1116.     if (nString == -1)
  1117.     {
  1118.         // initialize map if necessary
  1119.         if (m_pStringMap == NULL)
  1120.             m_pStringMap = new CMapStringToPtr;
  1121.  
  1122.         // add new string to toolbar list
  1123.         CString strTemp(lpszText, lstrlen(lpszText)+1);
  1124.         nString = (int)DefWindowProc(TB_ADDSTRING, 0, (LPARAM)(LPCTSTR)strTemp);
  1125.         if (nString == -1)
  1126.             return FALSE;
  1127.  
  1128.         // cache string away in string map
  1129.         m_pStringMap->SetAt(lpszText, (void*)nString);
  1130.         ASSERT(m_pStringMap->Lookup(lpszText, p));
  1131.     }
  1132.  
  1133.     // change the toolbar button description
  1134.     TBBUTTON button;
  1135.     _GetButton(nIndex, &button);
  1136.     button.iString = nString;
  1137.     _SetButton(nIndex, &button);
  1138.  
  1139.     return TRUE;
  1140. }
  1141.  
  1142. CString CToolBar::GetButtonText(int nIndex) const
  1143. {
  1144.     CString strResult;
  1145.     GetButtonText(nIndex, strResult);
  1146.     return strResult;
  1147. }
  1148.  
  1149. void CToolBar::GetButtonText(int nIndex, CString& rString) const
  1150. {
  1151.     if (m_pStringMap != NULL)
  1152.     {
  1153.         // get button information (need button.iString)
  1154.         TBBUTTON button;
  1155.         _GetButton(nIndex, &button);
  1156.  
  1157.         // look in map for matching iString
  1158.         POSITION pos = m_pStringMap->GetStartPosition();
  1159.         CString str; void* p;
  1160.         while (pos)
  1161.         {
  1162.             m_pStringMap->GetNextAssoc(pos, str, p);
  1163.             if ((int)p == button.iString)
  1164.             {
  1165.                 rString = str;
  1166.                 return;
  1167.             }
  1168.         }
  1169.     }
  1170.     rString.Empty();
  1171. }
  1172.  
  1173. /////////////////////////////////////////////////////////////////////////////
  1174. // CToolBar message handlers
  1175.  
  1176. BEGIN_MESSAGE_MAP(CToolBar, CControlBar)
  1177.     //{{AFX_MSG_MAP(CToolBar)
  1178.     ON_WM_NCHITTEST()
  1179.     ON_WM_NCPAINT()
  1180.     ON_WM_PAINT()
  1181.     ON_WM_ERASEBKGND()
  1182.     ON_WM_NCCALCSIZE()
  1183.     ON_WM_WINDOWPOSCHANGING()
  1184.     ON_WM_NCCREATE()
  1185.     ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize)
  1186.     ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize)
  1187.     ON_MESSAGE(WM_SETTINGCHANGE, OnPreserveZeroBorderHelper)
  1188.     ON_MESSAGE(WM_SETFONT, OnPreserveZeroBorderHelper)
  1189.     ON_WM_SYSCOLORCHANGE()
  1190.     //}}AFX_MSG_MAP
  1191. END_MESSAGE_MAP()
  1192.  
  1193. BOOL CToolBar::OnEraseBkgnd(CDC*)
  1194. {
  1195.     return (BOOL)Default();
  1196. }
  1197.  
  1198. UINT CToolBar::OnNcHitTest(CPoint)
  1199. {
  1200.     return HTCLIENT;
  1201. }
  1202.  
  1203. void CToolBar::OnNcCalcSize(BOOL /*bCalcValidRects*/, NCCALCSIZE_PARAMS* lpncsp)
  1204. {
  1205.     // calculate border space (will add to top/bottom, subtract from right/bottom)
  1206.     CRect rect; rect.SetRectEmpty();
  1207.     BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0;
  1208.     CControlBar::CalcInsideRect(rect, bHorz);
  1209.     ASSERT(_afxComCtlVersion != -1);
  1210.     ASSERT(_afxComCtlVersion >= VERSION_IE4 || rect.top >= 2);
  1211.  
  1212.     // adjust non-client area for border space
  1213.     lpncsp->rgrc[0].left += rect.left;
  1214.     lpncsp->rgrc[0].top += rect.top;
  1215.     // previous versions of COMCTL32.DLL had a built-in 2 pixel border
  1216.     if (_afxComCtlVersion < VERSION_IE4)
  1217.         lpncsp->rgrc[0].top -= 2;
  1218.     lpncsp->rgrc[0].right += rect.right;
  1219.     lpncsp->rgrc[0].bottom += rect.bottom;
  1220. }
  1221.  
  1222. void CToolBar::OnBarStyleChange(DWORD dwOldStyle, DWORD dwNewStyle)
  1223. {
  1224.     // a dynamically resizeable toolbar can not have the CBRS_FLOAT_MULTI
  1225.     ASSERT(!((dwNewStyle & CBRS_SIZE_DYNAMIC) &&
  1226.             (m_dwDockStyle & CBRS_FLOAT_MULTI)));
  1227.  
  1228.     // a toolbar can not be both dynamic and fixed in size
  1229.     ASSERT (!((dwNewStyle & CBRS_SIZE_FIXED) &&
  1230.         (dwNewStyle & CBRS_SIZE_DYNAMIC)));
  1231.  
  1232.     // CBRS_SIZE_DYNAMIC can not be disabled once it has been enabled
  1233.     ASSERT (((dwOldStyle & CBRS_SIZE_DYNAMIC) == 0) ||
  1234.         ((dwNewStyle & CBRS_SIZE_DYNAMIC) != 0));
  1235.  
  1236.     if (m_hWnd != NULL &&
  1237.         ((dwOldStyle & CBRS_BORDER_ANY) != (dwNewStyle & CBRS_BORDER_ANY)))
  1238.     {
  1239.         // recalc non-client area when border styles change
  1240.         SetWindowPos(NULL, 0, 0, 0, 0,
  1241.             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_DRAWFRAME);
  1242.     }
  1243.     m_bDelayedButtonLayout = TRUE;
  1244. }
  1245.  
  1246. void CToolBar::OnNcPaint()
  1247. {
  1248.     EraseNonClient();
  1249. }
  1250.  
  1251. void CToolBar::OnWindowPosChanging(LPWINDOWPOS lpWndPos)
  1252. {
  1253.     // not necessary to invalidate the borders
  1254.     DWORD dwStyle = m_dwStyle;
  1255.     m_dwStyle &= ~(CBRS_BORDER_ANY);
  1256.     CControlBar::OnWindowPosChanging(lpWndPos);
  1257.     m_dwStyle = dwStyle;
  1258.  
  1259.     // If we can resize while floating
  1260.     if (dwStyle & CBRS_SIZE_DYNAMIC)
  1261.     {
  1262.         // And we are resizing
  1263.         if (lpWndPos->flags & SWP_NOSIZE)
  1264.             return;
  1265.  
  1266.         // Then redraw the buttons
  1267.         Invalidate();
  1268.     }
  1269. }
  1270.  
  1271. void CToolBar::OnPaint()
  1272. {
  1273.     if (m_bDelayedButtonLayout)
  1274.         Layout();
  1275.  
  1276.     Default();
  1277. }
  1278.  
  1279. LRESULT CToolBar::OnSetButtonSize(WPARAM, LPARAM lParam)
  1280. {
  1281.     return OnSetSizeHelper(m_sizeButton, lParam);
  1282. }
  1283.  
  1284. LRESULT CToolBar::OnSetBitmapSize(WPARAM, LPARAM lParam)
  1285. {
  1286.     return OnSetSizeHelper(m_sizeImage, lParam);
  1287. }
  1288.  
  1289. LRESULT CToolBar::OnSetSizeHelper(CSize& size, LPARAM lParam)
  1290. {
  1291.     //WINBUG: The IE4 version of COMCTL32.DLL supports a zero border, but
  1292.     //  only if TBSTYLE_TRANSPARENT is on during the the TB_SETBITMAPSIZE
  1293.     //  and/or TB_SETBUTTONSIZE messages.  In order to enable this feature
  1294.     //  all the time (so we get consistent border behavior, dependent only
  1295.     //  on the version of COMCTL32.DLL) we turn on TBSTYLE_TRANSPARENT
  1296.     //  whenever these messages go through.  It would be nice that in a
  1297.     //  future version, the system toolbar would just allow you to set
  1298.     //  the top and left borders to anything you please.
  1299.  
  1300.     BOOL bModify = FALSE;
  1301.     ASSERT(_afxComCtlVersion != -1);
  1302.     DWORD dwStyle = 0;
  1303.     if (_afxComCtlVersion >= VERSION_IE4)
  1304.     {
  1305.         dwStyle = GetStyle();
  1306.         bModify = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT);
  1307.     }
  1308.  
  1309.     LRESULT lResult = Default();
  1310.     if (lResult)
  1311.         size = lParam;
  1312.  
  1313.     if (bModify)
  1314.         SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
  1315.  
  1316.     return lResult;
  1317. }
  1318.  
  1319. LRESULT CToolBar::OnPreserveZeroBorderHelper(WPARAM, LPARAM)
  1320. {
  1321.     BOOL bModify = FALSE;
  1322.     ASSERT(_afxComCtlVersion != -1);
  1323.     DWORD dwStyle = 0;
  1324.     if (_afxComCtlVersion >= VERSION_IE4)
  1325.     {
  1326.         dwStyle = GetStyle();
  1327.         bModify = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT);
  1328.     }
  1329.  
  1330.     LRESULT lResult = Default();
  1331.  
  1332.     if (bModify)
  1333.         SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
  1334.  
  1335.     return lResult;
  1336. }
  1337.  
  1338. void CToolBar::OnSysColorChange()
  1339. {
  1340.     // re-color bitmap for toolbar
  1341.     if (m_hInstImageWell != NULL && m_hbmImageWell != NULL)
  1342.     {
  1343.         HBITMAP hbmNew;
  1344.         hbmNew = AfxLoadSysColorBitmap(m_hInstImageWell, m_hRsrcImageWell);
  1345.         if (hbmNew != NULL)
  1346.             AddReplaceBitmap(hbmNew);
  1347.     }
  1348. }
  1349.  
  1350. /////////////////////////////////////////////////////////////////////////////
  1351. // CToolBar idle update through CToolCmdUI class
  1352.  
  1353. class CToolCmdUI : public CCmdUI        // class private to this file !
  1354. {
  1355. public: // re-implementations only
  1356.     virtual void Enable(BOOL bOn);
  1357.     virtual void SetCheck(int nCheck);
  1358.     virtual void SetText(LPCTSTR lpszText);
  1359. };
  1360.  
  1361. void CToolCmdUI::Enable(BOOL bOn)
  1362. {
  1363.     m_bEnableChanged = TRUE;
  1364.     CToolBar* pToolBar = (CToolBar*)m_pOther;
  1365.     ASSERT(pToolBar != NULL);
  1366.     ASSERT_KINDOF(CToolBar, pToolBar);
  1367.     ASSERT(m_nIndex < m_nIndexMax);
  1368.  
  1369.     UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) & ~TBBS_DISABLED;
  1370.     if (!bOn)
  1371.     {
  1372.         nNewStyle |= TBBS_DISABLED;
  1373.         // WINBUG: If a button is currently pressed and then is disabled
  1374.         // COMCTL32.DLL does not unpress the button, even after the mouse
  1375.         // button goes up!  We work around this bug by forcing TBBS_PRESSED
  1376.         // off when a button is disabled.
  1377.         nNewStyle &= ~TBBS_PRESSED;
  1378.     }
  1379.     ASSERT(!(nNewStyle & TBBS_SEPARATOR));
  1380.     pToolBar->SetButtonStyle(m_nIndex, nNewStyle);
  1381. }
  1382.  
  1383. void CToolCmdUI::SetCheck(int nCheck)
  1384. {
  1385.     ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate
  1386.     CToolBar* pToolBar = (CToolBar*)m_pOther;
  1387.     ASSERT(pToolBar != NULL);
  1388.     ASSERT_KINDOF(CToolBar, pToolBar);
  1389.     ASSERT(m_nIndex < m_nIndexMax);
  1390.  
  1391.     UINT nNewStyle = pToolBar->GetButtonStyle(m_nIndex) &
  1392.                 ~(TBBS_CHECKED | TBBS_INDETERMINATE);
  1393.     if (nCheck == 1)
  1394.         nNewStyle |= TBBS_CHECKED;
  1395.     else if (nCheck == 2)
  1396.         nNewStyle |= TBBS_INDETERMINATE;
  1397.     ASSERT(!(nNewStyle & TBBS_SEPARATOR));
  1398.     pToolBar->SetButtonStyle(m_nIndex, nNewStyle | TBBS_CHECKBOX);
  1399. }
  1400.  
  1401. void CToolCmdUI::SetText(LPCTSTR)
  1402. {
  1403.     // ignore it
  1404. }
  1405.  
  1406. void CToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
  1407. {
  1408.     CToolCmdUI state;
  1409.     state.m_pOther = this;
  1410.  
  1411.     state.m_nIndexMax = (UINT)DefWindowProc(TB_BUTTONCOUNT, 0, 0);
  1412.     for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
  1413.     {
  1414.         // get buttons state
  1415.         TBBUTTON button;
  1416.         _GetButton(state.m_nIndex, &button);
  1417.         state.m_nID = button.idCommand;
  1418.  
  1419.         // ignore separators
  1420.         if (!(button.fsStyle & TBSTYLE_SEP))
  1421.         {
  1422.             // allow reflections
  1423.             if (CWnd::OnCmdMsg(0,
  1424.                 MAKELONG((int)CN_UPDATE_COMMAND_UI, WM_COMMAND+WM_REFLECT_BASE),
  1425.                 &state, NULL))
  1426.                 continue;
  1427.  
  1428.             // allow the toolbar itself to have update handlers
  1429.             if (CWnd::OnCmdMsg(state.m_nID, CN_UPDATE_COMMAND_UI, &state, NULL))
  1430.                 continue;
  1431.  
  1432.             // allow the owner to process the update
  1433.             state.DoUpdate(pTarget, bDisableIfNoHndler);
  1434.         }
  1435.     }
  1436.  
  1437.     // update the dialog controls added to the toolbar
  1438.     UpdateDialogControls(pTarget, bDisableIfNoHndler);
  1439. }
  1440.  
  1441. /////////////////////////////////////////////////////////////////////////////
  1442. // CToolBar diagnostics
  1443.  
  1444. #ifdef _DEBUG
  1445. void CToolBar::AssertValid() const
  1446. {
  1447.     // Note: CControlBar::AssertValid is not called because it checks for
  1448.     //  m_nCount and m_pData to be in sync, which they are not in CToolBar.
  1449.  
  1450.     ASSERT(m_hbmImageWell == NULL ||
  1451.         (afxData.bWin95 || ::GetObjectType(m_hbmImageWell) == OBJ_BITMAP));
  1452.  
  1453.     if (m_hInstImageWell != NULL && m_hbmImageWell != NULL)
  1454.         ASSERT(m_hRsrcImageWell != NULL);
  1455. }
  1456.  
  1457. void CToolBar::Dump(CDumpContext& dc) const
  1458. {
  1459.     CControlBar::Dump(dc);
  1460.  
  1461.     dc << "m_hbmImageWell = " << (UINT)m_hbmImageWell;
  1462.     dc << "\nm_hInstImageWell = " << (UINT)m_hInstImageWell;
  1463.     dc << "\nm_hRsrcImageWell = " << (UINT)m_hRsrcImageWell;
  1464.     dc << "\nm_sizeButton = " << m_sizeButton;
  1465.     dc << "\nm_sizeImage = " << m_sizeImage;
  1466.  
  1467.     if (dc.GetDepth() > 0)
  1468.     {
  1469.         CToolBar* pBar = (CToolBar*)this;
  1470.         int nCount = pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0);
  1471.         for (int i = 0; i < nCount; i++)
  1472.         {
  1473.             TBBUTTON button;
  1474.             _GetButton(i, &button);
  1475.             dc << "\ntoolbar button[" << i << "] = {";
  1476.             dc << "\n\tnID = " << button.idCommand;
  1477.             dc << "\n\tnStyle = " << MAKELONG(button.fsStyle, button.fsState);
  1478.             if (button.fsStyle & TBSTYLE_SEP)
  1479.                 dc << "\n\tiImage (separator width) = " << button.iBitmap;
  1480.             else
  1481.                 dc <<"\n\tiImage (bitmap image index) = " << button.iBitmap;
  1482.             dc << "\n}";
  1483.         }
  1484.     }
  1485.  
  1486.     dc << "\n";
  1487. }
  1488. #endif
  1489.  
  1490. #ifdef AFX_INIT_SEG
  1491. #pragma code_seg(AFX_INIT_SEG)
  1492. #endif
  1493.  
  1494. IMPLEMENT_DYNAMIC(CToolBar, CControlBar)
  1495.  
  1496. /////////////////////////////////////////////////////////////////////////////
  1497.