ToolBarEx.cpp
上传用户:xywutai
上传日期:2007-01-02
资源大小:72k
文件大小:39k
源码类别:

工具条

开发平台:

Visual C++

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