toolbar.c.1475
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 103k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /*
  2. ** Toolbar.c
  3. **
  4. ** This is it, the incredibly famous toolbar control.  Most of
  5. ** the customization stuff is in another file.
  6. */
  7. #include "ctlspriv.h"
  8. #include "toolbar.h"
  9. #include "image.h"
  10. #include <limits.h>
  11. #define LPARAM_TO_POINT(lParam, pt)       ((pt).x = LOWORD(lParam), 
  12.                                            (pt).y = HIWORD(lParam))
  13. #define TBIMAGELIST
  14. // these values are defined by the UI gods...
  15. #define DEFAULTBITMAPX 16
  16. #define DEFAULTBITMAPY 15
  17. #define LIST_GAP        g_cxEdge * 2
  18. #define SMALL_DXYBITMAP 16 // new dx dy for sdt images
  19. #define LARGE_DXYBITMAP 24
  20. #define DEFAULTBUTTONX 24
  21. #define DEFAULTBUTTONY 22
  22. // horizontal/vertical space taken up by button chisel, sides,
  23. // and a 1 pixel margin.  used in GrowToolbar.
  24. #define XSLOP 7
  25. #define YSLOP 6
  26. const int g_dxButtonSep = 8;
  27. const int s_xFirstButton = 0; // was 8 in 3.1
  28. #define s_dxOverlap 0   // was 1 in 3.1
  29. // Globals - since all of these globals are used durring a paint we have to
  30. // take a criticial section around all toolbar paints.  this sucks.
  31. //
  32. const UINT wStateMasks[] = {
  33.     TBSTATE_ENABLED,
  34.     TBSTATE_CHECKED,
  35.     TBSTATE_PRESSED,
  36.     TBSTATE_HIDDEN,
  37.     TBSTATE_INDETERMINATE,
  38.     TBSTATE_HIGHLIGHTED
  39. };
  40. #define TBISSTRINGPTR(iString)  (((iString) != -1) && (HIWORD(iString)))
  41. LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
  42. void NEAR PASCAL TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize);
  43. BOOL NEAR PASCAL SetBitmapSize(PTBSTATE ptb, int width, int height);
  44. int  NEAR PASCAL AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT wBMID);
  45. BOOL NEAR PASCAL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, BOOL bInside);
  46. void NEAR PASCAL TBBuildImageList(PTBSTATE ptb);
  47. BOOL NEAR PASCAL GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect);
  48. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTON pTBButton);
  49. #define TBInvalidateImageList(ptb)  ((ptb)->fHimlValid = FALSE)
  50. #define TBHasStrings(ptb)  ((ptb)->nStrings || (ptb)->fNoStringPool)
  51. LRESULT ToolbarDragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
  52. {
  53.     PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
  54.     LRESULT lres;
  55.     switch (code)
  56.     {
  57.     case DPX_DRAGHIT:
  58.         if (lp)
  59.         {
  60.             POINT pt = {((POINTL *)lp)->x, ((POINTL *)lp)->y};
  61.             int item;
  62.             MapWindowPoints(NULL, ptb->ci.hwnd, &pt, 1);
  63.             item = TBHitTest(ptb, pt.x, pt.y);
  64.             lres = (LRESULT)((item >= 0)? ptb->Buttons[item].idCommand : -1);
  65.         }
  66.         else
  67.             lres = -1;
  68.         break;
  69.     case DPX_GETOBJECT:
  70.         lres = (LRESULT)GetItemObject(&ptb->ci, TBN_GETOBJECT, &IID_IDropTarget, (LPNMOBJECTNOTIFY)lp); 
  71.         break;
  72.     case DPX_SELECT:
  73.         if ((int)wp >= 0)
  74.         {
  75.             SendMessage(ptb->ci.hwnd, TB_HIGHLIGHTBUTTON, wp,
  76.                 MAKELPARAM((lp != DROPEFFECT_NONE), 0));
  77.         }
  78.         lres = 0;
  79.         break;
  80.     default:
  81.         lres = -1;
  82.         break;
  83.     }
  84.     return lres;
  85. }
  86. //#define HeightWithString(ptb, h) (h + ptb->dyIconFont + 1)
  87. int HeightWithString(PTBSTATE ptb, int h)
  88. {
  89.     if (ptb->ci.style & TBSTYLE_LIST)
  90.         return (max(h, ptb->dyIconFont));
  91.     else if (ptb->dyIconFont)
  92.         return (h + ptb->dyIconFont + 1);
  93.     else
  94.         return (h);
  95. }
  96. int TBWidthOfButton(PTBSTATE ptb, PTBBUTTON pButton)
  97. {
  98.     if (pButton->fsStyle & TBSTYLE_SEP)
  99.         return pButton->iBitmap;
  100.     else if ((pButton->fsStyle & TBSTYLE_DROPDOWN) && !(ptb->ci.style & TBSTYLE_FLAT))
  101.         return ptb->iButWidth + (ptb->iButWidth /2);
  102.     else
  103.         return ptb->iButWidth;
  104. }
  105. BOOL NEAR PASCAL TBRecalc(PTBSTATE ptb)
  106. {
  107.     TEXTMETRIC tm;
  108.     int i;
  109.     HDC hdc;
  110.     UINT uiStyle = 0;
  111.     int cxMax;
  112.     HFONT hOldFont;
  113.     ptb->dyIconFont = 0;
  114.     if (!TBHasStrings(ptb)) {
  115.         cxMax = ptb->iDxBitmap;
  116.     } else {
  117.         SIZE size;
  118.         LPCTSTR pstr;
  119.         RECT rcText = {0,0,0,0};
  120.         int cxExtra = XSLOP;
  121.         hdc = GetDC(ptb->ci.hwnd);
  122.         if (!hdc)
  123.             return(FALSE);
  124.         hOldFont = SelectObject(hdc, ptb->hfontIcon);
  125.         GetTextMetrics(hdc, &tm);
  126.         if (ptb->nTextRows)
  127.             ptb->dyIconFont = (tm.tmHeight * ptb->nTextRows) +
  128.                 (tm.tmExternalLeading * (ptb->nTextRows - 1)); // add an edge ?
  129.         if (ptb->ci.style & TBSTYLE_LIST)
  130.             cxExtra += ptb->iDxBitmap + LIST_GAP;
  131.         cxMax = 0;
  132.         
  133.         // walk strings to find max width
  134.         for (i = 0; i < ptb->iNumButtons; i++) 
  135.         {
  136.             pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
  137.             if (pstr) {
  138.                 GetTextExtentPoint(hdc, pstr, lstrlen(pstr), &size);
  139.                 if (cxMax < size.cx)
  140.                     cxMax = size.cx;
  141.             }
  142.         }
  143.         // if cxMax is less than the iButMinWidth - dxBitmap (if LIST) then
  144.         // cxMax = iButMinWidth
  145.         if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
  146.             cxMax = ptb->iButMinWidth - cxExtra;
  147.         // Is the cxMax +  dxBitmap (if LIST) more than the max width ?
  148.         if (ptb->iButMaxWidth && (ptb->iButMaxWidth < (cxMax + cxExtra)))
  149.         {
  150.             int cyMax = 0;
  151.             cxMax = ptb->iButMaxWidth - cxExtra;
  152.             uiStyle = DT_CALCRECT;
  153.             if (ptb->nTextRows > 1)
  154.                 uiStyle |= DT_WORDBREAK | DT_EDITCONTROL;
  155.             else
  156.                 uiStyle |= DT_SINGLELINE;
  157.             // walk strings to set the TBSTATE_ELLIPSES
  158.             for (i = 0; i < ptb->iNumButtons; i++)
  159.             {
  160.                 BOOL fEllipsed = FALSE;
  161.                 
  162.                 pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
  163.                 if (pstr) {
  164.                     rcText.bottom = ptb->dyIconFont;
  165.                     rcText.right = cxMax;
  166.                     DrawText(hdc, pstr, lstrlen(pstr), &rcText, uiStyle);
  167.                     if (ptb->nTextRows > 1)
  168.                         fEllipsed = (BOOL)(rcText.bottom > ptb->dyIconFont);
  169.                     else
  170.                         fEllipsed = (BOOL)(rcText.right > cxMax);
  171.                     if (cyMax < rcText.bottom)
  172.                         cyMax = rcText.bottom;
  173.                 }
  174.                 
  175.                 if (fEllipsed)
  176.                     ptb->Buttons[i].fsState |= TBSTATE_ELLIPSES;
  177.                 else
  178.                     ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
  179.             }
  180.             // Set the text height to the tallest text, with the top end being the number
  181.             // of rows specified by MAXTEXTROWS
  182.             if (ptb->dyIconFont > cyMax)
  183.                 ptb->dyIconFont = cyMax;
  184.         }
  185.         else
  186.         {
  187.             for (i = 0; i < ptb->iNumButtons; i++)
  188.                 ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
  189.             if ((ptb->nTextRows) && (ptb->dyIconFont > size.cy))
  190.                 ptb->dyIconFont = size.cy;
  191.         }
  192.         if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
  193.             cxMax = ptb->iButMinWidth - cxExtra;
  194.         if (hOldFont)
  195.             SelectObject(hdc, hOldFont);
  196.         ReleaseDC(ptb->ci.hwnd, hdc);
  197.     }
  198.     return(GrowToolbar(ptb, cxMax, HeightWithString(ptb, ptb->iDyBitmap), TRUE));
  199. }
  200. BOOL NEAR PASCAL TBChangeFont(PTBSTATE ptb, WPARAM wParam, HFONT hFont)
  201. {
  202.     LOGFONT lf;
  203.     BOOL fWasFontCreated = ptb->fFontCreated;
  204.     if ((wParam != 0) && (wParam != SPI_SETICONTITLELOGFONT) && (wParam != SPI_SETNONCLIENTMETRICS))
  205.         return(FALSE);
  206.     if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0))
  207.         return(FALSE);
  208.     if (!hFont) {
  209.         if (!(hFont = CreateFontIndirect(&lf)))
  210.             return(FALSE);
  211.         ptb->fFontCreated = TRUE;
  212.     }
  213.     if (ptb->hfontIcon && fWasFontCreated)
  214.         DeleteObject(ptb->hfontIcon);
  215.     ptb->hfontIcon = hFont;
  216.     return(TBRecalc(ptb));
  217. }
  218. void TBSetFont(PTBSTATE ptb, HFONT hFont, BOOL fInval)
  219. {
  220.     TBChangeFont(ptb, 0, hFont);
  221.     if (fInval)
  222.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  223.         
  224. }
  225. HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, UINT wID, int nBitmaps,
  226.             HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons,
  227.             int iNumButtons, int dxButton, int dyButton,
  228.             int dxBitmap, int dyBitmap, UINT uStructSize)
  229. {
  230.     HWND hwndToolbar = CreateWindow(c_szToolbarClass, NULL, WS_CHILD | ws,
  231.           0, 0, 100, 30, hwnd, (HMENU)wID, HINST_THISDLL, NULL);
  232.     if (hwndToolbar)
  233.     {
  234.         PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwndToolbar, 0);
  235.         TBOnButtonStructSize(ptb, uStructSize);
  236.         if ((dxBitmap && dyBitmap && !SetBitmapSize(ptb, dxBitmap, dyBitmap)) ||
  237.             (dxButton && dyButton && !SetBitmapSize(ptb,dxButton, dyButton)))
  238.         {
  239.             //!!!! do we actually need to deal with this?
  240.             DestroyWindow(hwndToolbar);
  241.             hwndToolbar = NULL;
  242.             goto Error;
  243.         }
  244.         AddBitmap(ptb, nBitmaps, hBMInst, wBMID);
  245.         InsertButtons(ptb, (UINT)-1, iNumButtons, (LPTBBUTTON)lpButtons, TRUE);
  246.         // ptb may be bogus now after above button insert
  247.     }
  248. Error:
  249.     return hwndToolbar;
  250. }
  251. /* This is no longer declared in COMMCTRL.H.  It only exists for compatibility
  252. ** with existing apps; new apps must use CreateToolbarEx.
  253. */
  254. HWND WINAPI CreateToolbar(HWND hwnd, DWORD ws, UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons, int iNumButtons)
  255. {
  256.     // old-style toolbar, so no divider.
  257.     return CreateToolbarEx(hwnd, ws | CCS_NODIVIDER, wID, nBitmaps, hBMInst, wBMID,
  258.                 lpButtons, iNumButtons, 0, 0, 0, 0, sizeof(OLDTBBUTTON));
  259. }
  260. #pragma code_seg(CODESEG_INIT)
  261. BOOL FAR PASCAL InitToolbarClass(HINSTANCE hInstance)
  262. {
  263.     WNDCLASS wc;
  264.     if (!GetClassInfo(hInstance, c_szToolbarClass, &wc)) {
  265. #ifndef WIN32
  266.     extern LRESULT CALLBACK _ToolbarWndProc(HWND, UINT, WPARAM, LPARAM);
  267.     wc.lpfnWndProc  = _ToolbarWndProc;
  268. #else
  269.     wc.lpfnWndProc  = (WNDPROC)ToolbarWndProc;
  270. #endif
  271.     wc.lpszClassName = c_szToolbarClass;
  272.     wc.style     = CS_DBLCLKS | CS_GLOBALCLASS;
  273.     wc.cbClsExtra    = 0;
  274.     wc.cbWndExtra    = sizeof(PTBSTATE);
  275.     wc.hInstance     = hInstance; // use DLL instance if in DLL
  276.     wc.hIcon     = NULL;
  277.     wc.hCursor  = LoadCursor(NULL, IDC_ARROW);
  278.     wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  279.     wc.lpszMenuName  = NULL;
  280.     if (!RegisterClass(&wc))
  281.         return FALSE;
  282.     }
  283.     return TRUE;
  284. }
  285. #pragma code_seg()
  286. #define BEVEL   2
  287. #define FRAME   1
  288. void NEAR PASCAL PatB(HDC hdc,int x,int y,int dx,int dy, DWORD rgb)
  289. {
  290.     RECT    rc;
  291.     SetBkColor(hdc,rgb);
  292.     rc.left   = x;
  293.     rc.top    = y;
  294.     rc.right  = x + dx;
  295.     rc.bottom = y + dy;
  296.     ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  297. }
  298. // Parameter fHighlight determines whether to draw text highlighted, for
  299. // new TBSTATE_HIGHLIGHTED
  300. //
  301. void NEAR PASCAL DrawString(PTBSTATE ptb, HDC hdc, int x, int y, int dx, int dy, PTSTR pszString,
  302.     BOOL fHighlight)
  303. {
  304.     int oldMode;
  305.     COLORREF oldBkColor;
  306.     COLORREF oldTextColor;
  307.     int len;
  308.     RECT rcText;
  309.     UINT uiStyle = 0;
  310.     if (!(ptb->ci.style & TBSTYLE_LIST) && ((ptb->iDyBitmap + YSLOP + g_cyEdge) >= ptb->iButHeight))
  311.         // there's no room to show the text -- bail out
  312.         return;
  313.     if (fHighlight)
  314.     {
  315.         oldMode = SetBkMode (hdc, OPAQUE);
  316.         oldBkColor = SetBkColor (hdc, g_clrHighlight);
  317.         oldTextColor = SetTextColor (hdc, g_clrHighlightText);
  318.     }
  319.     else
  320.         oldMode = SetBkMode(hdc, TRANSPARENT);
  321.     len = lstrlen(pszString);
  322.     uiStyle = DT_END_ELLIPSIS;
  323.     
  324.     
  325.     if (ptb->nTextRows > 1)
  326.         uiStyle |= DT_WORDBREAK | DT_EDITCONTROL;
  327.     if (ptb->ci.style & TBSTYLE_LIST)
  328.     {
  329.         uiStyle |= DT_LEFT | DT_VCENTER | DT_SINGLELINE;
  330.         dy = max(ptb->dyIconFont, ptb->iDyBitmap);
  331.     }
  332.     else
  333.     {
  334.         uiStyle |= DT_CENTER;
  335.         if (!dy || ptb->dyIconFont < dy)
  336.             dy = ptb->dyIconFont;
  337.     }
  338.     SetRect( &rcText, x, y, x + dx, y + dy);
  339.     DrawTextEx(hdc, (LPTSTR)pszString, len, &rcText, uiStyle, NULL);
  340.     SetBkMode(hdc, oldMode);
  341.     if (fHighlight)
  342.     {
  343.         SetBkColor (hdc, oldBkColor);
  344.         SetTextColor (hdc, oldTextColor);
  345.     }
  346. }
  347. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTON pTBButton)
  348. {
  349.     if (TBISSTRINGPTR(pTBButton->iString))
  350.         return (LPTSTR)pTBButton->iString;
  351.     else {
  352.         if (pTBButton->iString != -1 && 
  353.             pTBButton->iString < ptb->nStrings)
  354.             return ptb->pStrings[pTBButton->iString];
  355.         return NULL;
  356.     }
  357. }
  358. // create a mono bitmap mask:
  359. //   1's where color == COLOR_BTNFACE || COLOR_3DHILIGHT
  360. //   0's everywhere else
  361. void NEAR PASCAL CreateMask(PTBSTATE ptb, LPTBBUTTON pTBButton, int xoffset, int yoffset, int dx, int dy, BOOL fDrawGlyph)
  362. {
  363.     IMAGELISTDRAWPARAMS imldp;
  364.     // initalize whole area with 1's
  365.     PatBlt(ptb->hdcMono, 0, 0, dx, dy, WHITENESS);
  366.     // create mask based on color bitmap
  367.     // convert this to 1's
  368.     if (fDrawGlyph)
  369.     {
  370.         imldp.cbSize = sizeof(imldp);
  371.         imldp.himl   = ptb->himl;
  372.         imldp.i      = pTBButton->iBitmap;
  373.         imldp.hdcDst = ptb->hdcMono;
  374.         imldp.x      = xoffset;
  375.         imldp.y      = yoffset;
  376.         imldp.cx     = 0;
  377.         imldp.cy     = 0;
  378.         imldp.xBitmap= 0;
  379.         imldp.yBitmap= 0;
  380.         imldp.rgbBk  = g_clrBtnFace;
  381.         imldp.rgbFg  = CLR_DEFAULT;
  382.         imldp.fStyle = ILD_ROP | ILD_MASK;
  383.         imldp.dwRop  = SRCCOPY;
  384.         ImageList_DrawIndirect(&imldp);
  385.         imldp.fStyle = ILD_ROP | ILD_IMAGE;
  386.         imldp.rgbBk  = g_clrBtnHighlight;
  387.         imldp.dwRop  = SRCPAINT;
  388.         ImageList_DrawIndirect(&imldp);
  389.     }
  390.     if (pTBButton->iString != -1 && (pTBButton->iString < ptb->nStrings))
  391.     {
  392.         xoffset = 1;
  393.         if (ptb->ci.style & TBSTYLE_LIST)
  394.         {
  395.             xoffset += ptb->iDxBitmap + LIST_GAP;
  396.             dx -= ptb->iDxBitmap + LIST_GAP;
  397.         }
  398.         else {
  399.             yoffset += ptb->iDyBitmap + 1;
  400.             dy -= ptb->iDyBitmap + 1;
  401.         }
  402.         // The FALSE in 4th param is so we don't get a box in the mask.
  403.         DrawString(ptb, ptb->hdcMono, xoffset, yoffset, dx - g_cxEdge, dy - g_cyEdge, TB_StrForButton(ptb, pTBButton),
  404.             FALSE);
  405.     }
  406. }
  407. void FAR PASCAL DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, UINT state)
  408. {
  409.     RECT r1;
  410.     // face color
  411.     // The Office toolbar sends us bitmaps that are smaller than they claim they are
  412.     // So we need to do the PatB or the window background shows through around the
  413.     // edges of the button bitmap  -jjk
  414.     if (!(state & TBSTATE_CHECKED))
  415.         PatB(hdc, x, y, dx, dy, g_clrBtnFace);
  416.     r1.left = x;
  417.     r1.top = y;
  418.     r1.right = x + dx;
  419.     r1.bottom = y + dy;
  420.     DrawEdge(hdc, &r1, (state & (TBSTATE_CHECKED | TBSTATE_PRESSED)) ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT | BF_SOFT);
  421. }
  422. #define DSPDxax  0x00E20746
  423. #define PSDPxax  0x00B8074A
  424. void NEAR PASCAL DrawFace(PTBSTATE ptb, LPTBBUTTON ptButton, HDC hdc, int x, int y,
  425.             int offx, int offy, int dx, int dy, UINT state)
  426. {
  427.     LPTSTR psz;
  428.     IMAGELISTDRAWPARAMS imldp;
  429.     BOOL fHotTrack = FALSE;
  430.     if (state & TBSTATE_ENABLED)
  431.     {
  432.         if ((ptb->ci.style & TBSTYLE_FLAT) && (&ptb->Buttons[ptb->nCurHTButton]==ptButton))
  433.             fHotTrack = TRUE;
  434.         // The following is in place to prevent hot tracking during the following conds:
  435.         //  - drag & drop toolbar customization
  436.         //  - when the mouse capture is on a particular button-press.
  437.         // This does _not_ drop out of the loop because we don't want to break update
  438.         // behavior; thus we'll have a little flickering on refresh as we pass over
  439.         // these buttons.
  440.         if (!(state & TBSTATE_PRESSED) && (GetKeyState (VK_LBUTTON) < 0))
  441.             fHotTrack = FALSE;
  442.         
  443.         if (ptb->ci.style & TBSTYLE_FLAT)
  444.         {
  445.             UINT bdr = 0;
  446.             
  447.             if (state & (TBSTATE_CHECKED | TBSTATE_PRESSED))
  448.                 bdr = BDR_SUNKENOUTER;
  449.             else if (fHotTrack)
  450.                 bdr = BDR_RAISEDINNER;
  451.             if (bdr)
  452.             {
  453.                 RECT rect;
  454.                 GetItemRect(ptb, ((DWORD) ptButton - (DWORD) ptb->Buttons) / sizeof(TBBUTTON), &rect);
  455.                 DrawEdge(hdc, &rect, bdr, BF_RECT);
  456.             }
  457.         }
  458.     }
  459.     imldp.himl = NULL;
  460.     if (fHotTrack)
  461.         imldp.himl   = ptb->himlHot ? ptb->himlHot : ptb->himl;
  462.     else if (!(state & TBSTATE_ENABLED) && ptb->himlDisabled)
  463.         imldp.himl   = ptb->himlDisabled;
  464.     else if (ptb->himl)
  465.         imldp.himl   = ptb->himl;
  466.     if (imldp.himl)
  467.     {
  468.         imldp.cbSize = sizeof(imldp);
  469.         imldp.i      = ptButton->iBitmap;
  470.         imldp.hdcDst = hdc;
  471.         imldp.x      = x + offx;
  472.         imldp.y      = y + offy;
  473.         imldp.cx     = 0;
  474.         imldp.cy     = 0;
  475.         imldp.xBitmap= 0;
  476.         imldp.yBitmap= 0;
  477.         imldp.rgbBk  = (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace;
  478.         imldp.rgbFg  = CLR_DEFAULT;
  479.         imldp.fStyle = ILD_NORMAL;
  480.         if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE))
  481.             imldp.fStyle = ILD_TRANSPARENT;
  482. #ifdef TBHIGHLIGHT_GLYPH
  483.         if (state & TBSTATE_HIGHLIGHTED)
  484.             imldp.fStyle = ILD_TRANSPARENT | ILD_BLEND50;
  485. #endif
  486.         ImageList_DrawIndirect(&imldp);
  487.     }
  488.     psz = TB_StrForButton(ptb, ptButton);
  489.     if (psz)
  490.     {
  491.         if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
  492.         {
  493.             x++;
  494.             if (ptb->ci.style & TBSTYLE_LIST)
  495.                 y++;
  496.         }
  497.         if (ptb->ci.style & TBSTYLE_LIST)
  498.         {
  499.             x += ptb->iDxBitmap + LIST_GAP;
  500.             dx -= ptb->iDxBitmap + LIST_GAP;
  501.         }
  502.         else {
  503.             
  504.             y += offy + ptb->iDyBitmap;
  505.             dy -= offy + ptb->iDyBitmap;
  506.         }
  507.         DrawString(ptb, hdc, x + 1, y + 1, dx - g_cxEdge, dy - g_cyEdge, 
  508.                    psz,
  509.                    (state & (TBSTATE_HIGHLIGHTED)) && (ptb->ci.style & TBSTYLE_LIST));
  510.     }
  511. }
  512. void FAR PASCAL DrawButton(HDC hdc, int x, int y, PTBSTATE ptb, LPTBBUTTON ptButton, BOOL fActive)
  513. {
  514.     int yOffset;
  515.     HBRUSH hbrOld;
  516.     UINT state;
  517.     int dxFace, dyFace;
  518.     int xCenterOffset;
  519.     int dx = TBWidthOfButton(ptb, ptButton);
  520.     HFONT oldhFont;
  521.     int dy = ptb->iButHeight;
  522.     NMCUSTOMDRAW    nmcd;
  523.     DWORD           dwRet;
  524.     COLORREF        clrSave = SetTextColor(hdc, g_clrBtnText);
  525.     state = (UINT)ptButton->fsState;
  526.     // make local copy of state and do proper overriding
  527.     if (state & TBSTATE_INDETERMINATE) {
  528.         if (state & TBSTATE_PRESSED)
  529.             state &= ~TBSTATE_INDETERMINATE;
  530.         else if (state & TBSTATE_ENABLED)
  531.             state = TBSTATE_INDETERMINATE;
  532.         else
  533.             state &= ~TBSTATE_INDETERMINATE;
  534.     }
  535.     if (!fActive) {
  536.         state &= ~TBSTATE_ENABLED;
  537.     }
  538.     oldhFont = SelectObject(hdc, ptb->hfontIcon);
  539.     nmcd.hdc = hdc;
  540.     nmcd.dwItemSpec = ptButton->idCommand;
  541.     nmcd.uItemState = 0;
  542.     if (state & TBSTATE_CHECKED)
  543.         nmcd.uItemState |= CDIS_CHECKED;
  544.     if (state & TBSTATE_PRESSED)
  545.         nmcd.uItemState |= CDIS_SELECTED;
  546.     if (!(state & TBSTATE_ENABLED))
  547.         nmcd.uItemState |= CDIS_DISABLED;
  548.     if ((ptb->ci.style & TBSTYLE_FLAT) && (&ptb->Buttons[ptb->nCurHTButton]==ptButton))
  549.         nmcd.uItemState |= CDIS_HOT;
  550.     nmcd.lItemlParam = 0;
  551.     dwRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, &nmcd);
  552.     if (!(dwRet & CDRF_SKIPDEFAULT))
  553.     {
  554.         dxFace = ptb->iButWidth - (2 * g_cxEdge);// this the witdh of the face, not the entire button (dropdown case)
  555.         dyFace = dy - (2 * g_cyEdge);
  556.         if (!(ptb->ci.style & TBSTYLE_FLAT))
  557.             DrawBlankButton(hdc, x, y, dx, dy, state);
  558.         // move coordinates inside border and away from upper left highlight.
  559.         // the extents change accordingly.
  560.         x += g_cxEdge;
  561.         y += g_cyEdge;
  562.         // calculate offset of face from (x,y).  y is always from the top,
  563.         // so the offset is easy.  x needs to be centered in face.
  564.         yOffset = 1;
  565.         if (ptb->ci.style & TBSTYLE_LIST)
  566.              xCenterOffset = XSLOP / 2;
  567.         else
  568.              xCenterOffset = (dxFace - ptb->iDxBitmap)/2;
  569.         if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
  570.         {
  571.         // pressed state moves down and to the right
  572.             xCenterOffset++;
  573.             yOffset++;
  574.         }
  575.         // draw the dithered background
  576.         if ((state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) || ((state & TBSTATE_HIGHLIGHTED)
  577.             && !(ptb->ci.style & TBSTYLE_FLAT)))
  578.         {
  579.             hbrOld = SelectObject(hdc, g_hbrMonoDither);
  580.             if (hbrOld)
  581.             {
  582.                 COLORREF clrText, clrBack;
  583. #ifdef TBHIGHLIGHT_BACK
  584.                 if (state & TBSTATE_HIGHLIGHTED)
  585.                     clrText = SetTextColor(hdc, g_clrHighlight);
  586.                 else
  587. #endif
  588.                     clrText = SetTextColor(hdc, g_clrBtnHighlight); // 0 -> 0
  589.                 clrBack = SetBkColor(hdc, g_clrBtnFace);        // 1 -> 1
  590.                 // only draw the dither brush where the mask is 1's
  591.                 PatBlt(hdc, x, y, dxFace, dyFace, PATCOPY);
  592.                 SelectObject(hdc, hbrOld);
  593.                 SetTextColor(hdc, clrText);
  594.                 SetBkColor(hdc, clrBack);
  595.             }
  596.         }
  597.         // now put on the face
  598.         // TODO: Validate himlDisabled and ensure that the index is in range
  599.         if ((state & TBSTATE_ENABLED) || ptb->himlDisabled)
  600.         {
  601.             // regular version
  602.             DrawFace(ptb, ptButton, hdc, x, y, xCenterOffset, yOffset, dxFace, dyFace, state);
  603.         }
  604.         if (!(state & TBSTATE_ENABLED))
  605.         {
  606.             HBITMAP hbmOld;
  607.             //initialize the monochrome dc
  608.             if (!ptb->hdcMono) {
  609.                 ptb->hdcMono = CreateCompatibleDC(hdc);
  610.                 if (!ptb->hdcMono)
  611.                     return;
  612.                 SetTextColor(ptb->hdcMono, 0L);
  613.                 SelectObject(ptb->hdcMono, ptb->hfontIcon);
  614.             }
  615.             hbmOld = SelectObject(ptb->hdcMono, ptb->hbmMono);
  616.             // disabled version (or indeterminate)
  617.             CreateMask(ptb, ptButton, xCenterOffset, yOffset, dxFace, dyFace, (ptb->himlDisabled == NULL));
  618.             SetTextColor(hdc, 0L);  // 0's in mono -> 0 (for ROP)
  619.             SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
  620.             // draw glyph's white understrike
  621.             if (!(state & TBSTATE_INDETERMINATE)) {
  622.                 hbrOld = SelectObject(hdc, g_hbrBtnHighlight);
  623.                 if (hbrOld) {
  624.                     // draw hilight color where we have 0's in the mask
  625.                     BitBlt(hdc, x + 1, y + 1, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
  626.                     SelectObject(hdc, hbrOld);
  627.                 }
  628.             }
  629.             // gray out glyph
  630.             hbrOld = SelectObject(hdc, g_hbrBtnShadow);
  631.             if (hbrOld) {
  632.                 // draw the shadow color where we have 0's in the mask
  633.                 BitBlt(hdc, x, y, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
  634.                 SelectObject(hdc, hbrOld);
  635.             }
  636.             if (state & TBSTATE_CHECKED) {
  637.                 BitBlt(ptb->hdcMono, 1, 1, dxFace - 1, dyFace - 1, ptb->hdcMono, 0, 0, SRCAND);
  638.             }
  639.             SelectObject(ptb->hdcMono, hbmOld);
  640.         }
  641.         if ((ptButton->fsStyle & TBSTYLE_DROPDOWN) && !(ptb->ci.style & TBSTYLE_FLAT))
  642.         {
  643.             RECT rc;
  644.             POINT pts[3];
  645.             int iHeight;
  646.             int iWidth;
  647.             HBRUSH hbr;
  648.             HPEN hpen;
  649.             rc.left = x - (2*g_cxEdge) + ptb->iButWidth + (yOffset-1);
  650.             rc.top = y + g_cyBorder;
  651.             rc.bottom = y + dy - (2 * g_cyEdge) - (2*g_cyBorder);
  652.             rc.right = rc.left - ptb->iButWidth + dx;
  653.             DrawEdge(hdc, &rc, EDGE_ETCHED, BF_LEFT);
  654.             rc.left += g_cxEdge;
  655.             rc.right -= g_cxEdge;
  656.             rc.top += yOffset;
  657.             iWidth = RECTWIDTH(rc);
  658.             iWidth -= g_cxEdge;  // make it a little smaller than the rect
  659.             if (iWidth < 3) iWidth = 3;
  660.             iWidth &= (~1); // make it even
  661.             iHeight = (iWidth) / 2;
  662.             pts[0].y = pts[1].y = (RECTHEIGHT(rc) - iHeight + 1)/2 + rc.top; // +1 to bias lower
  663.             pts[2].y = pts[0].y + iHeight;
  664.             pts[0].x = (RECTWIDTH(rc) - iWidth + 1) / 2 + rc.left; // +1 to bias right
  665.             pts[1].x = pts[0].x + iWidth;
  666.             pts[2].x = pts[0].x + iWidth/2;
  667.             hbr = GetStockObject(BLACK_BRUSH);
  668.             hpen = GetStockObject(BLACK_PEN);
  669.             hbr = SelectObject(hdc, hbr);
  670.             hpen = SelectObject(hdc, hpen);
  671.             Polygon(hdc, pts, 3);
  672.             SelectObject(hdc, hbr);
  673.             SelectObject(hdc, hpen);
  674.         }
  675.     }
  676.     if (dwRet & CDRF_NOTIFYPOSTPAINT)
  677.         CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, &nmcd);
  678.     SetTextColor(hdc, clrSave);
  679.     if (oldhFont)
  680.         SelectObject(hdc, oldhFont);
  681. }
  682. // make sure that g_hbmMono is big enough to do masks for this
  683. // size of button.  if not, fail.
  684. BOOL NEAR PASCAL CheckMonoMask(PTBSTATE ptb, int width, int height)
  685. {
  686.     BITMAP bm;
  687.     HBITMAP hbmTemp;
  688.     if (ptb->hbmMono) {
  689.         GetObject(ptb->hbmMono, sizeof(BITMAP), &bm);
  690.         if (width <= bm.bmWidth && height <= bm.bmHeight) {
  691.             return TRUE;
  692.         }
  693.     }
  694.     hbmTemp = CreateMonoBitmap(width, height);
  695.     if (!hbmTemp)
  696.         return FALSE;
  697.     if (ptb->hbmMono)
  698.         DeleteObject(ptb->hbmMono);
  699.     ptb->hbmMono = hbmTemp;
  700.     return TRUE;
  701. }
  702. /*
  703. ** GrowToolbar
  704. **
  705. ** Attempt to grow the button size.
  706. **
  707. ** The calling function can either specify a new internal measurement
  708. ** or a new external measurement.
  709. */
  710. BOOL NEAR PASCAL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, BOOL bInside)
  711. {
  712.     if (!newButWidth)
  713.         newButWidth = DEFAULTBUTTONX;
  714.     if (!newButHeight)
  715.         newButHeight = DEFAULTBUTTONY;
  716.     // if growing based on inside measurement, get full size
  717.     if (bInside)
  718.     {
  719.         if (ptb->ci.style & TBSTYLE_LIST)
  720.             newButWidth += ptb->iDxBitmap + LIST_GAP;
  721.         newButHeight += YSLOP;
  722.         newButWidth += XSLOP;
  723.         // if toolbar already has strings, don't shrink width it because it
  724.         // might clip room for the string
  725.         if ((newButWidth < ptb->iButWidth) && ptb->nStrings)
  726.             newButWidth = ptb->iButWidth;
  727.     }
  728.     else {
  729.         if (newButHeight == -1)
  730.             newButHeight = ptb->iButHeight;
  731.         if (newButWidth == -1)
  732.             newButWidth = ptb->iButWidth;
  733.         if (newButHeight < ptb->iDyBitmap + YSLOP)
  734.             newButHeight = ptb->iDyBitmap + YSLOP;
  735.         if (newButWidth < ptb->iDxBitmap + XSLOP)
  736.             newButWidth = ptb->iDxBitmap + XSLOP;
  737.     }
  738.     // if the size of the toolbar is actually growing, see if shadow
  739.     // bitmaps can be made sufficiently large.
  740.     if (!ptb->hbmMono || (newButWidth > ptb->iButWidth) || (newButHeight > ptb->iButHeight)) {
  741.         if (!CheckMonoMask(ptb, newButWidth, newButHeight))
  742.             return(FALSE);
  743.     }
  744.     if (!bInside && (ptb->iButWidth != newButWidth) || (ptb->iButHeight != newButHeight))
  745.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  746.     ptb->iButWidth = newButWidth;
  747.     ptb->iButHeight = newButHeight;
  748.     // bar height has 2 pixels above, 2 below
  749.     if (ptb->ci.style & TBSTYLE_TRANSPARENT)
  750.         ptb->iYPos = 0;
  751.     else
  752.         ptb->iYPos = 2;
  753.     FlushToolTipsMgr(ptb);
  754.     return TRUE;
  755. }
  756. BOOL NEAR PASCAL SetBitmapSize(PTBSTATE ptb, int width, int height)
  757. {
  758.     int realh;
  759.     if (!width)
  760.         width = 1;
  761.     if (!height)
  762.         height = 1;
  763.     if (width == -1)
  764.         width = ptb->iDxBitmap;
  765.     if (height == -1)
  766.         height = ptb->iDyBitmap;
  767.     realh = height;
  768.     if ((ptb->iDxBitmap == width) && (ptb->iDyBitmap == height))
  769.         return TRUE;
  770.     if (TBHasStrings(ptb))
  771.         realh = HeightWithString(ptb, height);
  772.     if (GrowToolbar(ptb, width, realh, TRUE)) {
  773.         ptb->iDxBitmap = width;
  774.         ptb->iDyBitmap = height;
  775.         // the size changed, we need to rebuild the imagelist
  776.         TBInvalidateImageList(ptb);
  777.         return TRUE;
  778.     }
  779.     return FALSE;
  780. }
  781. void NEAR PASCAL TB_OnSysColorChange(PTBSTATE ptb)
  782. {
  783.     InitGlobalColors();
  784.     //  Reset all of the bitmaps
  785.     if (ptb->himl)
  786.         ImageList_SetBkColor(ptb->himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  787.     if (ptb->himlHot)
  788.         ImageList_SetBkColor(ptb->himlHot, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  789. }
  790. #define CACHE 0x01
  791. #define BUILD 0x02
  792. void PASCAL ReleaseMonoDC(PTBSTATE ptb)
  793. {
  794.     if (ptb->hdcMono) {
  795.         SelectObject(ptb->hdcMono, g_hfontSystem);
  796.         DeleteDC(ptb->hdcMono);
  797.         ptb->hdcMono = NULL;
  798.     }
  799. }
  800. void TB_OnEraseBkgnd(PTBSTATE ptb, HDC hdc)
  801. {
  802.     NMCUSTOMDRAW    nmcd;
  803.     DWORD           dwRes = FALSE;
  804.     nmcd.hdc = hdc;
  805.     nmcd.uItemState = 0;
  806.     nmcd.lItemlParam = 0;
  807.     if (ptb->ci.style & TBSTYLE_CUSTOMERASE) {
  808.         ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREERASE, &nmcd);
  809.     } else {
  810.         ptb->ci.dwCustom = CDRF_DODEFAULT;
  811.     }
  812.     if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
  813.     {
  814.         // for transparent toolbars, forward erase background to parent
  815.         // but handle thru DefWindowProc in the event parent doesn't paint
  816.         if (!(ptb->ci.style & TBSTYLE_TRANSPARENT) ||
  817.             !CCForwardEraseBackground(ptb->ci.hwnd, hdc))
  818.             DefWindowProc(ptb->ci.hwnd, WM_ERASEBKGND, (WPARAM) hdc, 0);
  819.     }
  820.     if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
  821.         CICustomDrawNotify(&ptb->ci, CDDS_POSTERASE, &nmcd);
  822. }
  823. void NEAR PASCAL ToolbarPaint(PTBSTATE ptb, HDC hdcIn)
  824. {
  825.     RECT rc;
  826.     HDC hdc;
  827.     PAINTSTRUCT ps;
  828.     int iButton, xButton, yButton, cxBar;
  829.     PTBBUTTON pAllButtons = ptb->Buttons;
  830.     NMCUSTOMDRAW    nmcd;
  831.     GetClientRect(ptb->ci.hwnd, &rc);
  832.     cxBar = rc.right - rc.left;
  833.     
  834.     if (hdcIn)
  835.     {
  836.         hdc = hdcIn;
  837.     }
  838.     else
  839.         hdc = BeginPaint(ptb->ci.hwnd, &ps);
  840.     if (!rc.right)
  841.         goto Error1;
  842.     nmcd.hdc = hdc;
  843.     nmcd.uItemState = 0;
  844.     nmcd.lItemlParam = 0;
  845.     ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREPAINT, &nmcd);
  846.     if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
  847.     {
  848.         if (!ptb->fHimlValid)
  849.             TBBuildImageList(ptb);
  850.         yButton   = ptb->iYPos;
  851.         rc.top    = ptb->iYPos;
  852.         rc.bottom = ptb->iYPos + ptb->iButHeight;
  853.         for (iButton = 0, xButton = ptb->xFirstButton;
  854.                 iButton < ptb->iNumButtons; iButton++)
  855.         {
  856.             PTBBUTTON pButton = &pAllButtons[iButton];
  857.             if (!(pButton->fsState & TBSTATE_HIDDEN))
  858.             {
  859.                 int cxButton = TBWidthOfButton(ptb, pButton);
  860.                 if (!(pButton->fsStyle & TBSTYLE_SEP) || (ptb->ci.style & TBSTYLE_FLAT))
  861.                 {
  862.                     // is there anything to draw?
  863.                     rc.left = xButton;
  864.                     rc.right = xButton + cxButton;
  865.                     if (RectVisible(hdc, &rc))
  866.                     {
  867.                         if (pButton->fsStyle & TBSTYLE_SEP)
  868.                         {
  869.                             // must be a flat separator
  870.                             if (ptb->ci.style & CCS_VERT)
  871.                             {
  872.                                 int iSave = rc.top;
  873.                                 rc.top += ((rc.bottom - rc.top) - 1) / 2;
  874.                                 rc.top = iSave;
  875.                                 InflateRect(&rc, -g_cxEdge, 0);
  876.                                 DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP);
  877.                                 InflateRect(&rc, g_cxEdge, 0);
  878.                             }
  879.                             else
  880.                             {
  881.                                 rc.left += (cxButton - 1) / 2;
  882.                                 InflateRect(&rc, 0, -g_cyEdge);
  883.                                 DrawEdge(hdc, &rc, EDGE_ETCHED, BF_LEFT);
  884.                                 InflateRect(&rc, 0, g_cyEdge);
  885.                             }
  886.                         }
  887.                         else
  888.                             DrawButton(hdc, xButton, yButton, ptb, pButton, ptb->fActive);
  889.                     }
  890.                 }
  891.                 xButton += (cxButton - s_dxOverlap);
  892.                 if (pButton->fsState & TBSTATE_WRAP)
  893.                 {
  894.                     int dy;
  895.                     if (pButton->fsStyle & TBSTYLE_SEP)
  896.                     {
  897.                         if (ptb->ci.style & TBSTYLE_FLAT)
  898.                         {
  899.                             RECT rcMid;
  900.                             rcMid.top = rc.top + ptb->iButHeight + ((pButton->iBitmap - 1) / 2);
  901.                             rcMid.bottom = rcMid.top + g_cxEdge;
  902.                             rcMid.left = g_cxEdge;
  903.                             rcMid.right = cxBar - g_cxEdge;
  904.                             DrawEdge(hdc, &rcMid, EDGE_ETCHED, BF_TOP);
  905.                             dy = ptb->iButHeight + pButton->iBitmap;
  906.                         }
  907.                         else
  908.                             dy = ptb->iButHeight + pButton->iBitmap * 2 / 3;
  909.                     }
  910.                     else
  911.                         dy = ptb->iButHeight;
  912.                     
  913.                     xButton = ptb->xFirstButton;
  914.                     yButton   += dy;
  915.                     rc.top    += dy;
  916.                     rc.bottom += dy;
  917.                 }
  918.             }
  919.         }
  920.         ReleaseMonoDC(ptb);
  921.     }
  922.     if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  923.     {
  924.         nmcd.hdc = hdc;
  925.         nmcd.uItemState = 0;
  926.         nmcd.lItemlParam = 0;
  927.         CICustomDrawNotify(&ptb->ci, CDDS_POSTPAINT, &nmcd);
  928.     }
  929. Error1:
  930.     if (hdcIn == NULL)
  931.         EndPaint(ptb->ci.hwnd, &ps);
  932. }
  933. BOOL NEAR PASCAL GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
  934. {
  935.     UINT iButton, xPos, yPos;
  936.     PTBBUTTON pButton;
  937.     if (uButton >= (UINT)ptb->iNumButtons
  938.         || (ptb->Buttons[uButton].fsState & TBSTATE_HIDDEN))
  939.     {
  940.         return FALSE;
  941.     }
  942.     xPos = ptb->xFirstButton;
  943.     yPos = ptb->iYPos;
  944.     for (iButton = 0, pButton = ptb->Buttons; iButton < uButton; iButton++, pButton++)
  945.     {
  946.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  947.         {
  948.             xPos += TBWidthOfButton(ptb, pButton) - s_dxOverlap;
  949.             if (pButton->fsState & TBSTATE_WRAP)
  950.             {
  951.                     yPos += ptb->iButHeight;
  952.                     if (pButton->fsStyle & TBSTYLE_SEP)
  953.                     {
  954.                         if (ptb->ci.style & TBSTYLE_FLAT)
  955.                             yPos += pButton->iBitmap;
  956.                         else
  957.                             yPos += pButton->iBitmap * 2 / 3;
  958.                     }
  959.                     xPos = ptb->xFirstButton;
  960.             }
  961.         }
  962.     }
  963.     // pButton should now point at the required button, and xPos should be
  964.     // its left edge.  Note that we already checked if the button was hidden above
  965.     lpRect->left   = xPos;
  966.     lpRect->right  = xPos + TBWidthOfButton(ptb, pButton);
  967.     lpRect->top    = yPos;
  968.     lpRect->bottom = yPos + ptb->iButHeight;
  969.     return TRUE;
  970. }
  971. void NEAR PASCAL InvalidateButton(PTBSTATE ptb, PTBBUTTON pButtonToPaint, BOOL fErase)
  972. {
  973.     RECT rc;
  974.     if (GetItemRect(ptb, pButtonToPaint-ptb->Buttons, &rc))
  975.     {
  976.         InvalidateRect(ptb->ci.hwnd, &rc, fErase);
  977.     }
  978. }
  979. // do hit testing by sliding the origin of the supplied point
  980. //
  981. // returns:
  982. //  >= 0    index of non sperator item hit
  983. //  < 0 index of seperator or nearest non seperator item (area just below and to the left)
  984. //
  985. // +--------------------------------------
  986. // |      -1    -1    -1    -1
  987. // |      btn   sep   btn
  988. // |    +-----+     +-----+
  989. // |    |     |     |     |
  990. // | -1 |  0  | -1  |  2  | -3
  991. // |    |     |     |     |
  992. // |    +-----+     +-----+
  993. // |
  994. // | -1   -1    -1    -2    -3
  995. //
  996. int FAR PASCAL TBHitTest(PTBSTATE ptb, int xPos, int yPos)
  997. {
  998.     int prev = 0;
  999.     int last = 0;
  1000.     int i;
  1001.     RECT rc;
  1002.     if (ptb->iNumButtons == 0)
  1003.         return(-1);
  1004.     for (i=0; i<ptb->iNumButtons; i++)
  1005.     {
  1006.         // BUGBUG.. this makes it n**2
  1007.         if (GetItemRect(ptb, i, &rc))
  1008.         {
  1009.             if (yPos >= rc.top && yPos <= rc.bottom)
  1010.             {
  1011.                 if (xPos >= rc.left && xPos <= rc.right)
  1012.                 {
  1013.                     if (ptb->Buttons[i].fsStyle & TBSTYLE_SEP)
  1014.                         return - i - 1;
  1015.                     else
  1016.                         return i;
  1017.                 }
  1018.                 else
  1019.                 {
  1020.                     prev = i + 1;
  1021.                 }
  1022.              }
  1023.              else
  1024.              {
  1025.                 last = i;
  1026.              }
  1027.         }
  1028.     }
  1029.     if (prev)
  1030.         return -1 - prev;
  1031.     else if (yPos > rc.bottom)
  1032.         // this means that we are off the bottom of the toolbar
  1033.         return(- i - 1);
  1034.     return last + 1;
  1035. }
  1036. int NEAR PASCAL CountRows(PTBSTATE ptb)
  1037. {
  1038.     PTBBUTTON pButton, pBtnLast;
  1039.     int rows = 1;
  1040.     pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  1041.     for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++) {
  1042.         if (pButton->fsState & TBSTATE_WRAP) {
  1043.             rows++;
  1044.             if (pButton->fsStyle & TBSTYLE_SEP)
  1045.                 rows++;
  1046.         }
  1047.     }
  1048.     return rows;
  1049. }
  1050. /**** WrapToolbar:
  1051.  * The buttons in the toolbar is layed out from left to right,
  1052.  * top to bottom. If adding another button to the current row,
  1053.  * while computing the layout, would cause that button to extend
  1054.  * beyond the right edge or the client area, then locate a break-
  1055.  * point (marked with the TBSTATE_WRAP flag). A break-point is:
  1056.  *
  1057.  * a) The right-most separator on the current row.
  1058.  *
  1059.  * b) The right-most button if there is no separator on the current row.
  1060.  *
  1061.  * A new row is also started at the end of any button group (sequence
  1062.  * of buttons that are dlimited by separators) that are taller than
  1063.  * or equal to two rows.
  1064.  */
  1065. void NEAR PASCAL WrapToolbar(PTBSTATE ptb, int dx, LPRECT lpRect, int FAR *pRows)
  1066. {
  1067.     BOOL fInvalidate = FALSE;
  1068.     PTBBUTTON pButton, pBtnT, pBtnLast;
  1069.     int xPos, yPos, xMax;
  1070.     BOOL bFoundIt;
  1071.     BOOL bNextBreak = FALSE;
  1072.     xMax = ptb->iButWidth;
  1073.     xPos = ptb->xFirstButton;
  1074.     yPos = ptb->iYPos;
  1075.     pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  1076.     if (pRows)
  1077.         (*pRows)=1;
  1078.     for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++)
  1079.     {
  1080.         pButton->fsState &= ~TBSTATE_WRAP;
  1081.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  1082.         {
  1083.             xPos += TBWidthOfButton(ptb, pButton) - s_dxOverlap;
  1084.             // The current row exceeds the right edge. Wrap it.
  1085.             if (!(pButton->fsStyle&TBSTYLE_SEP) && (xPos > dx)) {
  1086.                 for (pBtnT=pButton, bFoundIt = FALSE;
  1087.                      pBtnT>ptb->Buttons && !(pBtnT->fsState & TBSTATE_WRAP);
  1088.                      pBtnT--)
  1089.                 {
  1090.                     if ((pBtnT->fsStyle & TBSTYLE_SEP) &&
  1091.                         !(pBtnT->fsState & TBSTATE_HIDDEN))
  1092.                     {
  1093.                         fInvalidate = TRUE;
  1094.                         pBtnT->fsState |= TBSTATE_WRAP;
  1095.                         xPos = ptb->xFirstButton;
  1096.                         if (ptb->ci.style & TBSTYLE_FLAT)
  1097.                             yPos += pBtnT->iBitmap + ptb->iButHeight;
  1098.                         else
  1099.                             yPos += pBtnT->iBitmap * 2 / 3 + ptb->iButHeight;
  1100.                         bFoundIt = TRUE;
  1101.                         pButton = pBtnT;
  1102.                         bNextBreak = FALSE;
  1103.                         if (pRows)
  1104.                             (*pRows)++;
  1105.                         break;
  1106.                     }
  1107.                 }
  1108.                 // Did we find a separator? Force a wrap anyway!
  1109.                 if (bFoundIt==FALSE)
  1110.                 {
  1111.                     pBtnT = pButton;
  1112.                     if (pButton!=ptb->Buttons) {
  1113.                         /* Back-up to first non-hidden button. */
  1114.                         do {
  1115.                             pBtnT--;
  1116.                         } while ((pBtnT>ptb->Buttons) &&
  1117.                                  (pBtnT->fsState & TBSTATE_HIDDEN));
  1118.                         
  1119.                         /* Is it already wrapped? */
  1120.                         if (pBtnT->fsState & TBSTATE_WRAP)
  1121.                             pBtnT = pButton;
  1122.                     }
  1123.                     fInvalidate = TRUE;
  1124.                     pBtnT->fsState |= TBSTATE_WRAP;
  1125.                     xPos = ptb->xFirstButton;
  1126.                     yPos += ptb->iButHeight;
  1127.                     pButton = pBtnT;
  1128.                     bNextBreak = TRUE;
  1129.                 }
  1130.                 // Count another row.
  1131.                 if (pRows)
  1132.                     (*pRows)++;
  1133.             }
  1134.             else
  1135.             {
  1136.                 UINT fOldState = pButton->fsState;
  1137.                 
  1138.                 pButton->fsState &= ~TBSTATE_WRAP;
  1139.                 if ((pButton->fsStyle&TBSTYLE_SEP) && (bNextBreak))
  1140.                 {
  1141.                         bNextBreak = FALSE;
  1142.                         pButton->fsState |= TBSTATE_WRAP;
  1143.                         xPos = ptb->xFirstButton;
  1144.                         if (ptb->ci.style & TBSTYLE_FLAT)
  1145.                             yPos += ptb->iButHeight + pButton->iBitmap;
  1146.                         else
  1147.                             yPos += ptb->iButHeight + pButton->iBitmap * 2 / 3 ;
  1148.                         if (pRows)
  1149.                                 (*pRows)+=2;
  1150.                 }
  1151.                 if (fOldState != pButton->fsState) {
  1152.                     fInvalidate = TRUE;
  1153.                 }
  1154.             }
  1155.             if (!(pButton->fsStyle&TBSTYLE_SEP))
  1156.                 xMax = max(xPos, xMax);
  1157.         }
  1158.     }
  1159.     if (lpRect)
  1160.     {
  1161.         lpRect->left = 0;
  1162.         lpRect->right = xMax;
  1163.         lpRect->top = 0;
  1164.         lpRect->bottom = yPos + ptb->iYPos + ptb->iButHeight;
  1165.     }
  1166.     
  1167.     if (fInvalidate)
  1168.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1169. }
  1170. BOOL NEAR PASCAL BoxIt(PTBSTATE ptb, int height, BOOL fLarger, LPRECT lpRect)
  1171. {
  1172.     int dx, bwidth;
  1173.     int rows, prevRows, prevWidth;
  1174.     RECT rcCur;
  1175.     
  1176.     if (height<1)
  1177.         height = 1;
  1178.     rows = CountRows(ptb);
  1179.     if (height==rows || ptb->iNumButtons==0)
  1180.     {
  1181.         GetClientRect(ptb->ci.hwnd, lpRect);
  1182.         return FALSE;
  1183.     }
  1184.     bwidth = ptb->iButWidth-s_dxOverlap;
  1185.     prevRows = ptb->iNumButtons+1;
  1186.         prevWidth = bwidth;
  1187.     for (rows=height+1, dx = bwidth; rows>height;dx+=bwidth/4)
  1188.     {
  1189.         WrapToolbar(ptb, dx, &rcCur, &rows);
  1190.         if (rows<prevRows && rows>height)
  1191.         {
  1192.             prevWidth = dx;
  1193.             prevRows = rows;
  1194.         }
  1195.     }
  1196.     if (rows<height && fLarger)
  1197.     {
  1198.         WrapToolbar(ptb, prevWidth, &rcCur, NULL);
  1199.     }
  1200.     if (lpRect)
  1201.         *lpRect = rcCur;
  1202.         return TRUE;
  1203. }
  1204. int FAR PASCAL PositionFromID(PTBSTATE ptb, int id)
  1205. {
  1206.     int i;
  1207.     // Handle case where this is sent at the wrong time..
  1208.     if (ptb == NULL)
  1209.         return -1;
  1210.     // note, we don't skip seperators, so you better not have conflicting
  1211.     // cmd ids and seperator ids.
  1212.     for (i = 0; i < ptb->iNumButtons; i++)
  1213.         if (ptb->Buttons[i].idCommand == id)
  1214.             return i;       // position found
  1215.     return -1;      // ID not found!
  1216. }
  1217. // check a radio button by button index.
  1218. // the button matching idCommand was just pressed down.  this forces
  1219. // up all other buttons in the group.
  1220. // this does not work with buttons that are forced up with
  1221. void NEAR PASCAL MakeGroupConsistant(PTBSTATE ptb, int idCommand)
  1222. {
  1223.     int i, iFirst, iLast, iButton;
  1224.     int cButtons = ptb->iNumButtons;
  1225.     PTBBUTTON pAllButtons = ptb->Buttons;
  1226.     iButton = PositionFromID(ptb, idCommand);
  1227.     if (iButton < 0)
  1228.         return;
  1229.     // assertion
  1230. //    if (!(pAllButtons[iButton].fsStyle & TBSTYLE_CHECK))
  1231. // return;
  1232.     // did the pressed button just go down?
  1233.     if (!(pAllButtons[iButton].fsState & TBSTATE_CHECKED))
  1234.         return;         // no, can't do anything
  1235.     // find the limits of this radio group
  1236.     for (iFirst = iButton; (iFirst > 0) && (pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP); iFirst--)
  1237.         if (!(pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP))
  1238.             iFirst++;
  1239.     cButtons--;
  1240.     for (iLast = iButton; (iLast < cButtons) && (pAllButtons[iLast].fsStyle & TBSTYLE_GROUP); iLast++);
  1241.     if (!(pAllButtons[iLast].fsStyle & TBSTYLE_GROUP))
  1242.         iLast--;
  1243.     // search for the currently down button and pop it up
  1244.     for (i = iFirst; i <= iLast; i++) {
  1245.         if (i != iButton) {
  1246.             // is this button down?
  1247.             if (pAllButtons[i].fsState & TBSTATE_CHECKED) {
  1248.                 pAllButtons[i].fsState &= ~TBSTATE_CHECKED;     // pop it up
  1249.                 InvalidateButton(ptb, &pAllButtons[i], TRUE);
  1250.                 break;          // only one button is down right?
  1251.             }
  1252.         }
  1253.     }
  1254. }
  1255. void NEAR PASCAL DestroyStrings(PTBSTATE ptb)
  1256. {
  1257.     PTSTR *p;
  1258.     PTSTR end = 0, start = 0;
  1259.     int i;
  1260.     p = ptb->pStrings;
  1261.     for (i = 0; i < ptb->nStrings; i++) {
  1262.         if (!((*p < end) && (*p > start))) {
  1263.             start = (*p);
  1264.             end = start + (LocalSize((HANDLE)*p) / sizeof(TCHAR));
  1265.             LocalFree((HANDLE)*p);
  1266.         }
  1267.     p++;
  1268.     }
  1269.     LocalFree((HANDLE)ptb->pStrings);
  1270. }
  1271. #define MAXSTRINGSIZE 1024
  1272. int NEAR PASCAL AddStrings(PTBSTATE ptb, WPARAM wParam, LPARAM lParam)
  1273. {
  1274.     int i = 0,j = 0, cxMax = 0;
  1275.     LPTSTR lpsz;
  1276.     PTSTR  pString, psz;
  1277.     int numstr;
  1278.     PTSTR *pFoo;
  1279.     PTSTR *pOffset;
  1280.     TCHAR cSeparator;
  1281.     int len;
  1282.     // read the string as a resource
  1283.     if (wParam != 0) {
  1284.         pString = (PTSTR)LocalAlloc(LPTR, (MAXSTRINGSIZE * sizeof (TCHAR)));
  1285.         if (!pString)
  1286.             return -1;
  1287.         i = LoadString((HINSTANCE)wParam, LOWORD(lParam), (LPTSTR)pString, MAXSTRINGSIZE);
  1288.         if (!i) {
  1289.             LocalFree(pString);
  1290.             return -1;
  1291.         }
  1292.         // realloc string buffer to actual needed size
  1293.         LocalReAlloc(pString, (i+1) * sizeof (TCHAR), LMEM_MOVEABLE);
  1294.         
  1295.         // convert separators to '' and count number of strings
  1296.         cSeparator = *pString;
  1297. #if !defined(UNICODE) // && defined(DBCS)
  1298.         for (numstr = 0, psz = pString + 1, i--; i; i--, psz++ ) {
  1299.             if (*psz == cSeparator) {
  1300.                 if (i != 1)     // We don't want to count the second terminator as another string
  1301.                     numstr++;
  1302.                 *psz = 0;    // terminate with 0
  1303.             }
  1304.             // extra i-- if DBCS
  1305.             if (IsDBCSLeadByte(*psz))
  1306.             {
  1307.                 *(WORD *)(psz-1) = *(WORD *)psz;
  1308.                 psz++;
  1309.                 i--;
  1310.             }
  1311.             else
  1312.             {
  1313.                 // shift string to the left to overwrite separator identifier
  1314.                 *(psz - 1) = *psz;
  1315.             }
  1316.         }
  1317. #else
  1318.         for (numstr = 0, psz = pString + 1, i--; i; i--, psz++) {
  1319.             if (*psz == cSeparator) {
  1320.                 if (i != 1)     // We don't want to count the second terminator as another string
  1321.                 numstr++;
  1322.                 
  1323.                 *psz = 0; // terminate with 0
  1324.             }
  1325.             // shift string to the left to overwrite separator identifier
  1326.             *(psz - 1) = *psz;
  1327.         }
  1328. #endif
  1329.     }
  1330.     // read explicit string.  copy it into local memory, too.
  1331.     else {
  1332.         // find total length and number of strings
  1333.         for (i = 0, numstr = 0, lpsz = (LPTSTR)lParam;;) {
  1334.             i++;
  1335.             if (*lpsz == 0) {
  1336.                 numstr++;
  1337.                 if (*(lpsz + 1) == 0)
  1338.                     break;
  1339.             }
  1340.             lpsz++;
  1341.         }
  1342.         pString = (PTSTR)LocalAlloc(LPTR, (i * sizeof (TCHAR)));
  1343.         if (!pString)
  1344.             return -1;
  1345.         hmemcpy(pString, (void FAR *)lParam, i * sizeof(TCHAR));
  1346.     }
  1347.     // make room for increased string pointer table
  1348.     pFoo = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
  1349.             (ptb->nStrings + numstr) * sizeof(PTSTR));
  1350.     if (!pFoo) {
  1351.         goto Failure;
  1352.     }
  1353.     ptb->pStrings = pFoo;
  1354.     // pointer to next open slot in string index table.
  1355.     pOffset = ptb->pStrings + ptb->nStrings;
  1356.     for (i = 0; i < numstr; i++, pOffset++)
  1357.     {
  1358.         *pOffset = pString;
  1359.         len = lstrlen(pString);
  1360.         pString += len + 1;
  1361.     }
  1362.     // is the world big enough to handle the larger buttons?
  1363.     i = ptb->nStrings;
  1364.     ptb->nStrings += numstr;
  1365.     if (!TBRecalc(ptb))
  1366.     {
  1367.         ptb->nStrings -= numstr;
  1368.         // back out changes.
  1369.         ptb->pStrings = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
  1370.                     ptb->nStrings * sizeof(PTSTR));
  1371. Failure:
  1372.         LocalFree(pString);
  1373.         return -1;
  1374.     }
  1375.     return i;               // index of first added string
  1376. }
  1377. #ifdef WIN32
  1378. void NEAR PASCAL MapToStandardBitmaps(HINSTANCE FAR *phinst, UINT FAR * pidBM, int FAR *pnButtons)
  1379. {
  1380.     if (*phinst == HINST_COMMCTRL) {
  1381.         *phinst = g_hinst;
  1382.         // low 2 bits are coded M(mono == ~color) L(large == ~small)
  1383.         //  0 0   -> color small
  1384.         //  0 1   -> color large
  1385.         //  ...
  1386.         //  1 1   -> mono  large
  1387.         switch (*pidBM)
  1388.         {
  1389.         case IDB_STD_SMALL_COLOR:
  1390.         case IDB_STD_LARGE_COLOR:
  1391.         case IDB_STD_SMALL_MONO:
  1392.         case IDB_STD_LARGE_MONO:
  1393.             *pidBM = IDB_STDTB_SMALL_COLOR + (*pidBM & 1);
  1394.             *pnButtons = STD_PRINT + 1;
  1395.             break;
  1396.         case IDB_HIST_SMALL_COLOR:
  1397.         case IDB_HIST_LARGE_COLOR:
  1398.         //case IDB_HIST_SMALL_MONO:
  1399.         //case IDB_HIST_LARGE_MONO:
  1400.             *pidBM = IDB_HISTTB_SMALL_COLOR + (*pidBM & 1);
  1401.             *pnButtons = HIST_LAST + 1;
  1402.             break;
  1403.         case IDB_VIEW_SMALL_COLOR:
  1404.         case IDB_VIEW_LARGE_COLOR:
  1405.         case IDB_VIEW_SMALL_MONO:
  1406.         case IDB_VIEW_LARGE_MONO:
  1407.             *pidBM = IDB_VIEWTB_SMALL_COLOR + (*pidBM & 1);
  1408.             *pnButtons = VIEW_NEWFOLDER + 1;
  1409.             break;
  1410.         }
  1411.     }
  1412. }
  1413. #endif
  1414. HBITMAP _CopyBitmap(PTBSTATE ptb, HBITMAP hbm, int cx, int cy)
  1415. {
  1416.     HBITMAP hbmCopy;
  1417.     RECT rc = {0 ,0, cx, cy};
  1418.     HDC hdcWin;
  1419.     HDC hdcSrc, hdcDest;
  1420.     hbmCopy = CreateColorBitmap(cx, cy);
  1421.     hdcWin = GetDC(ptb->ci.hwnd);
  1422.     hdcSrc = CreateCompatibleDC(hdcWin);
  1423.     hdcDest = CreateCompatibleDC(hdcWin);
  1424.     if (hdcWin && hdcSrc && hdcDest) {
  1425.         SelectObject(hdcSrc, hbm);
  1426.         SelectObject(hdcDest, hbmCopy);
  1427.         // fill the background
  1428.         PatB(hdcDest, 0, 0, cx, cy, g_clrBtnFace);
  1429.         BitBlt(hdcDest, 0, 0, cx, cy,
  1430.                hdcSrc, 0, 0, SRCCOPY);
  1431.     }
  1432.     if (hdcWin)
  1433.         ReleaseDC(ptb->ci.hwnd, hdcWin);
  1434.     if (hdcSrc)
  1435.         DeleteDC(hdcSrc);
  1436.     if (hdcDest)
  1437.         DeleteDC(hdcDest);
  1438.     return hbmCopy;
  1439. }
  1440. BOOL TBAddBitmapToImageList(PTBSTATE ptb, PTBBMINFO pTemp)
  1441. {
  1442.     HBITMAP hbm = NULL, hbmTemp = NULL;
  1443.     if (!ptb->himl) {
  1444.         ptb->himl = ImageList_Create(ptb->iDxBitmap, ptb->iDyBitmap, ILC_MASK | ILC_COLORDDB, 4, 4);
  1445.         if (!ptb->himl)
  1446.             return(FALSE);
  1447.         ImageList_SetBkColor(ptb->himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  1448.     }
  1449.     if (pTemp->hInst) {
  1450.         hbm = hbmTemp = CreateMappedBitmap(pTemp->hInst, pTemp->wID, 0, NULL, 0);
  1451.     } else if (pTemp->wID) {
  1452.         hbm = (HBITMAP)pTemp->wID;
  1453.     }
  1454.     if (hbm) {
  1455.         //
  1456.         // Fix up bitmaps that aren't iDxBitmap x iDyBitmap
  1457.         //
  1458.         BITMAP bm;
  1459.         GetObject( hbm, sizeof(bm), &bm);
  1460.         
  1461.         if (bm.bmWidth < ptb->iDxBitmap) {
  1462.             bm.bmWidth = ptb->iDxBitmap;
  1463.         }
  1464.         
  1465.         if (bm.bmHeight < ptb->iDyBitmap) {
  1466.             bm.bmHeight = ptb->iDyBitmap; 
  1467.         }    
  1468.         // Handle case when the bitmaps is smaller or larger than what it is supposed to be
  1469.         // ptb->iDxBitmaps specifies the width a bitmap should be.
  1470.         // The error cases we are catching are:
  1471.         // If the pTemp->nButtons is 0 then we assume there is one button
  1472.         // If the pTemp->nButtons specifies a number but the width of the bitmap is not right,
  1473.         //    then we assume that nButtons is right and change the width of the bitmap
  1474.         if (!pTemp->nButtons)
  1475.             bm.bmWidth = ptb->iDxBitmap;
  1476.         else
  1477.             bm.bmWidth = ptb->iDxBitmap * pTemp->nButtons;
  1478.         hbm = (HBITMAP)_CopyBitmap(ptb, hbm, bm.bmWidth, bm.bmHeight);
  1479.     }
  1480.     // AddMasked parties on the bitmap, so we want to use a local copy
  1481.     if (hbm) {
  1482.         ImageList_AddMasked(ptb->himl, hbm, g_clrBtnFace);
  1483.         DeleteObject(hbm);
  1484.     }
  1485.     if (hbmTemp) {
  1486.         DeleteObject(hbmTemp);
  1487.     }
  1488.     return(TRUE);
  1489. }
  1490. void NEAR PASCAL TBBuildImageList(PTBSTATE ptb)
  1491. {
  1492.     int i;
  1493.     PTBBMINFO pTemp;
  1494.     ptb->fHimlValid = TRUE;
  1495.     // is the parent dealing natively with imagelists?  if so,
  1496.     // don't do this back compat building
  1497.     if (ptb->fHimlNative)
  1498.         return;
  1499.     if (ptb->himl) {
  1500.         ImageList_Destroy(ptb->himl);
  1501.         ptb->himl = NULL;
  1502.     }
  1503.     for (i = 0, pTemp = ptb->pBitmaps; i < ptb->nBitmaps; i++, pTemp++) {
  1504.         TBAddBitmapToImageList(ptb, pTemp);
  1505.     }
  1506. }
  1507. /* Adds a new bitmap to the list of BMs available for this toolbar.
  1508.  * Returns the index of the first button in the bitmap or -1 if there
  1509.  * was an error.
  1510.  */
  1511. int NEAR PASCAL AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT idBM)
  1512. {
  1513.     PTBBMINFO pTemp;
  1514.     int nBM, nIndex;
  1515.     // map things to the standard toolbar images
  1516. #ifdef WIN32
  1517.     if (hBMInst == HINST_COMMCTRL)        // -1
  1518.     {
  1519.         // set the proper dimensions...
  1520.         if (idBM & 1)
  1521.             SetBitmapSize(ptb, LARGE_DXYBITMAP, LARGE_DXYBITMAP);
  1522.         else
  1523.             SetBitmapSize(ptb, SMALL_DXYBITMAP, SMALL_DXYBITMAP);
  1524.         MapToStandardBitmaps(&hBMInst, &idBM, &nButtons);
  1525.     }
  1526. #endif
  1527.     if (ptb->pBitmaps)
  1528.     {
  1529.       /* Check if the bitmap has already been added
  1530.        */
  1531.         for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps, nIndex=0;
  1532.             nBM>0; --nBM, ++pTemp)
  1533.         {
  1534.             if (pTemp->hInst==hBMInst && pTemp->wID==idBM)
  1535.             {
  1536.                 /* We already have this bitmap, but have we "registered" all
  1537.                  * the buttons in it?
  1538.                  */
  1539.                 if (pTemp->nButtons >= nButtons)
  1540.                 return(nIndex);
  1541.                 if (nBM == 1)
  1542.                 {
  1543.                 /* If this is the last bitmap, we can easily increase the
  1544.                  * number of buttons without messing anything up.
  1545.                  */
  1546.                     pTemp->nButtons = nButtons;
  1547.                     return(nIndex);
  1548.                 }
  1549.             }
  1550.             nIndex += pTemp->nButtons;
  1551.         }
  1552.     }
  1553.     pTemp = (PTBBMINFO)CCLocalReAlloc(ptb->pBitmaps,
  1554.             (ptb->nBitmaps + 1)*sizeof(TBBMINFO));
  1555.     if (!pTemp)
  1556.         return(-1);
  1557.     ptb->pBitmaps = pTemp;
  1558.     pTemp = ptb->pBitmaps + ptb->nBitmaps;
  1559.     pTemp->hInst = hBMInst;
  1560.     pTemp->wID = idBM;
  1561.     pTemp->nButtons = nButtons;
  1562.     if (!TBAddBitmapToImageList(ptb, pTemp))
  1563.         return(-1);
  1564.     ++ptb->nBitmaps;
  1565.     for (nButtons=0, --pTemp; pTemp>=ptb->pBitmaps; --pTemp)
  1566.         nButtons += pTemp->nButtons;
  1567.     return(nButtons);
  1568. }
  1569. /* Adds a bitmap to the list of  BMs available for this
  1570.  * toolbar. Returns the index of the first button in the bitmap or -1 if there
  1571.  * was an error.
  1572.  */
  1573. int PASCAL TBLoadImages(PTBSTATE ptb, int id, HINSTANCE hinst)
  1574. {
  1575.     int iTemp;
  1576.     TBBMINFO bmi;
  1577.     MapToStandardBitmaps(&hinst, &id, &iTemp);
  1578.     bmi.hInst = hinst;
  1579.     bmi.wID = id;
  1580.     bmi.nButtons = iTemp;
  1581.     if (ptb->himl)
  1582.         iTemp = ImageList_GetImageCount(ptb->himl);
  1583.     else
  1584.         iTemp = 0;
  1585.     if (!TBAddBitmapToImageList(ptb, &bmi))
  1586.         return(-1);
  1587.     ptb->fHimlNative = TRUE;
  1588.     return iTemp;
  1589. }
  1590. BOOL NEAR PASCAL ReplaceBitmap(PTBSTATE ptb, LPTBREPLACEBITMAP lprb)
  1591. {
  1592.     int nBM;
  1593.     PTBBMINFO pTemp;
  1594. #ifdef WIN32
  1595.     int iTemp;
  1596.     MapToStandardBitmaps(&lprb->hInstOld, &lprb->nIDOld, &iTemp);
  1597.     MapToStandardBitmaps(&lprb->hInstNew, &lprb->nIDNew, &lprb->nButtons);
  1598. #endif
  1599.     for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps;
  1600.          nBM>0; --nBM, ++pTemp)
  1601.     {
  1602.         if (pTemp->hInst==lprb->hInstOld && pTemp->wID==lprb->nIDOld)
  1603.         {
  1604.             // number of buttons must match
  1605.             pTemp->hInst = lprb->hInstNew;
  1606.             pTemp->wID = lprb->nIDNew;
  1607.             pTemp->nButtons = lprb->nButtons;
  1608.             TBInvalidateImageList(ptb);
  1609.             return TRUE;
  1610.         }
  1611.     }
  1612.     return FALSE;
  1613. }
  1614. void FAR PASCAL FlushToolTipsMgr(PTBSTATE ptb) {
  1615.     // change all the rects for the tool tips mgr.  this is
  1616.     // cheap, and we don't do it often, so go ahead
  1617.     // and do them all.
  1618.     if(ptb->hwndToolTips) {
  1619.         UINT i;
  1620.         TOOLINFO ti;
  1621.         PTBBUTTON pButton;
  1622.     
  1623.         ti.cbSize = sizeof(ti);
  1624.         ti.hwnd = ptb->ci.hwnd;
  1625.         ti.lpszText = LPSTR_TEXTCALLBACK;
  1626.         for ( i = 0, pButton = ptb->Buttons;
  1627.              i < (UINT)ptb->iNumButtons;
  1628.              i++, pButton++) {
  1629.             if (!(pButton->fsStyle & TBSTYLE_SEP)) {
  1630.                 ti.uId = pButton->idCommand;
  1631.                 if (!GetItemRect(ptb, i, &ti.rect))
  1632.                     ti.rect.left = ti.rect.right = ti.rect.top = ti.rect.bottom = 0;
  1633.                 SendMessage(ptb->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  1634.             }
  1635.         }
  1636.     }
  1637. }
  1638. BOOL FAR PASCAL InsertButtons(PTBSTATE ptb, UINT uWhere, UINT uButtons, LPTBBUTTON lpButtons, BOOL fNative)
  1639. {
  1640.     PTBBUTTON pIn, pOut;
  1641.     PTBSTATE ptbNew;
  1642.     UINT    uAdded;
  1643.     UINT    uStart;
  1644.     BOOL fRecalc;
  1645.     // comments by chee (not the original author) so they not be
  1646.     // exactly right... be warned.
  1647.     if (!ptb || !ptb->uStructSize)
  1648.         return FALSE;
  1649.     // enlarge the main structure
  1650.     ptbNew = (PTBSTATE)CCLocalReAlloc(ptb, sizeof(TBSTATE) - sizeof(TBBUTTON)
  1651.         + (ptb->iNumButtons + uButtons) * sizeof(TBBUTTON));
  1652.     if (!ptbNew)
  1653.         return FALSE;
  1654.     ptb = ptbNew;
  1655.     SetWindowInt(ptb->ci.hwnd, 0, (int)ptb);
  1656.     // if where points beyond the end, set it at the end
  1657.     if (uWhere > (UINT)ptb->iNumButtons)
  1658.         uWhere = ptb->iNumButtons;
  1659.     // Need to save these since the values gues toasted.
  1660.     uAdded = uButtons;
  1661.     uStart = uWhere;
  1662.     // move buttons above uWhere up uButton spaces
  1663.     // the uWhere gets inverted and counts to zero..
  1664.     for (pIn=ptb->Buttons+ptb->iNumButtons-1, pOut=pIn+uButtons,
  1665.      uWhere=(UINT)ptb->iNumButtons-uWhere; uWhere>0;
  1666.      --pIn, --pOut, --uWhere)
  1667.         *pOut = *pIn;
  1668.     // only need to recalc if there are strings & room enough to actually show them
  1669.     fRecalc = (TBHasStrings(ptb) && ((ptb->ci.style & TBSTYLE_LIST) || ((ptb->iDyBitmap + YSLOP + g_cyEdge) < ptb->iButHeight)));
  1670.     
  1671.     // now do the copy.
  1672.     for (lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons+ptb->uStructSize*(uButtons-1)),
  1673.         ptb->iNumButtons+=(int)uButtons;  // init
  1674.         uButtons>0; //test
  1675.         --pOut, lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons-ptb->uStructSize), --uButtons)
  1676.     {
  1677.         TBInputStruct(ptb, pOut, lpButtons);
  1678.         
  1679.         if (TBISSTRINGPTR(pOut->iString)) {
  1680.             LPTSTR psz = (LPTSTR)pOut->iString;
  1681. #ifdef UNICODE
  1682.             if (!fNative) {
  1683.                 psz = ProduceWFromA(ptb->ci.uiCodePage, (LPSTR)psz);
  1684.             }
  1685. #endif
  1686.             pOut->iString = 0;
  1687.             Str_Set((LPTSTR*)&pOut->iString, psz);
  1688. #ifdef UNICODE
  1689.             if (!fNative) 
  1690.                 FreeProducedString(psz);
  1691. #endif
  1692.             if (!ptb->fNoStringPool)
  1693.                 fRecalc = TRUE;
  1694.             ptb->fNoStringPool = TRUE;
  1695.         }
  1696.     
  1697.         if(ptb->hwndToolTips && !(lpButtons->fsStyle & TBSTYLE_SEP)) {
  1698.             TOOLINFO ti;
  1699.             // don't bother setting the rect because we'll do it below
  1700.             // in FlushToolTipsMgr;
  1701.             ti.cbSize = sizeof(ti);
  1702.             ti.uFlags = 0;
  1703.             ti.hwnd = ptb->ci.hwnd;
  1704.             ti.uId = lpButtons->idCommand;
  1705.             ti.lpszText = LPSTR_TEXTCALLBACK;
  1706.             SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  1707.                 (LPARAM)(LPTOOLINFO)&ti);
  1708.         }
  1709.         if ((pOut->fsStyle & TBSTYLE_SEP) && pOut->iBitmap <=0)
  1710.             pOut->iBitmap = g_dxButtonSep;
  1711.     }
  1712.     // Re-compute layout if toolbar is wrapable.
  1713.     if (ptb->ci.style & TBSTYLE_WRAPABLE) {
  1714.         SendMessage(ptb->ci.hwnd, TB_AUTOSIZE, 0, 0);
  1715.     }
  1716.     FlushToolTipsMgr(ptb);
  1717.     if (fRecalc)
  1718.         TBRecalc(ptb);
  1719.     //
  1720.     // Reorder notification so apps can go requery what's on the toolbar if
  1721.     // more than 1 button was added; otherwise, just say create.
  1722.     //
  1723.     if (uAdded == 1)
  1724.         MyNotifyWinEvent(EVENT_OBJECT_CREATE, ptb->ci.hwnd, OBJID_CLIENT,
  1725.             uWhere+1);
  1726.     else
  1727.         MyNotifyWinEvent(EVENT_OBJECT_REORDER, ptb->ci.hwnd, OBJID_CLIENT, 0);
  1728.     // We need to completely redraw the toolbar at this point.
  1729.     // this MUST be done last!
  1730.     // tbrecalc and others will nuke out invalid area and we won't paint if this isn't last
  1731.     InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1732.     return(TRUE);
  1733. }
  1734. /* Notice that the state structure is not realloc'ed smaller at this
  1735.  * point.  This is a time optimization, and the fact that the structure
  1736.  * will not move is used in other places.
  1737.  */
  1738. BOOL FAR PASCAL DeleteButton(PTBSTATE ptb, UINT uIndex)
  1739. {
  1740.     PTBBUTTON pIn, pOut;
  1741.     BOOL fRecalc;
  1742.     if (uIndex >= (UINT)ptb->iNumButtons)
  1743.         return FALSE;
  1744.     MyNotifyWinEvent(EVENT_OBJECT_DESTROY, ptb->ci.hwnd, OBJID_CLIENT, uIndex+1);
  1745.     if (ptb->hwndToolTips) {
  1746.         TOOLINFO ti;
  1747.         ti.cbSize = sizeof(ti);
  1748.         ti.hwnd = ptb->ci.hwnd;
  1749.         ti.uId = ptb->Buttons[uIndex].idCommand;
  1750.         SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1751.     }
  1752.     --ptb->iNumButtons;
  1753.     
  1754.     pOut = ptb->Buttons + uIndex;
  1755.     
  1756.     fRecalc = (pOut->fsState & TBSTATE_WRAP);
  1757.         
  1758.     for (pIn = pOut + 1; uIndex<(UINT)ptb->iNumButtons; ++uIndex, ++pIn, ++pOut)
  1759.     {
  1760.         fRecalc |= (pIn->fsState & TBSTATE_WRAP);
  1761.         *pOut = *pIn;
  1762.     }
  1763.     // We need to completely recalc or redraw the toolbar at this point.
  1764.     if ((ptb->ci.style & TBSTYLE_WRAPABLE) && fRecalc)
  1765.     {
  1766.         RECT rc;
  1767.         HWND hwnd = ptb->ci.hwnd;
  1768.         
  1769.         if (!(ptb->ci.style & CCS_NORESIZE) && !(ptb->ci.style & CCS_NOPARENTALIGN))
  1770.             hwnd = GetParent(hwnd);
  1771.         
  1772.         GetWindowRect(hwnd, &rc);
  1773.         WrapToolbar(ptb, rc.right - rc.left, &rc, NULL);
  1774.         FlushToolTipsMgr(ptb);
  1775.     }
  1776.     
  1777.     InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1778.     
  1779.     FlushToolTipsMgr(ptb);
  1780.     return TRUE;
  1781. }
  1782. // deal with old TBBUTON structs for compatibility
  1783. void FAR PASCAL TBInputStruct(PTBSTATE ptb, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
  1784. {
  1785.     if (ptb->uStructSize >= sizeof(TBBUTTON))
  1786.     {
  1787.         *pButtonInt = *pButtonExt;
  1788.     }
  1789.     else
  1790.     /* It is assumed the only other possibility is the OLDBUTTON struct */
  1791.     {
  1792.         *(LPOLDTBBUTTON)pButtonInt = *(LPOLDTBBUTTON)pButtonExt;
  1793.         /* We don't care about dwData */
  1794.         pButtonInt->dwData = 0;
  1795.         pButtonInt->iString = -1;
  1796.     }
  1797. }
  1798. void NEAR PASCAL TBOutputStruct(PTBSTATE ptb, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
  1799. {
  1800.     if (ptb->uStructSize >= sizeof(TBBUTTON))
  1801.     {
  1802.         LPBYTE pOut;
  1803.         int i;
  1804.         /* Fill the part we know about and fill the rest with 0's
  1805.         */
  1806.         *pButtonExt = *pButtonInt;
  1807.         for (i = ptb->uStructSize - sizeof(TBBUTTON), pOut = (LPBYTE)(pButtonExt + 1);
  1808.             i > 0; --i, ++pOut)
  1809.         {
  1810.             *pOut = 0;
  1811.         }
  1812.     }
  1813.     else
  1814.     /* It is assumed the only other possibility is the OLDBUTTON struct */
  1815.     {
  1816.         *(LPOLDTBBUTTON)pButtonExt = *(LPOLDTBBUTTON)pButtonInt;
  1817.     }
  1818. }
  1819. void NEAR PASCAL TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize)
  1820. {
  1821.     /* You are not allowed to change this after adding buttons.
  1822.     */
  1823.     if (ptb && !ptb->iNumButtons)
  1824.     {
  1825.             ptb->uStructSize = uStructSize;
  1826.     }
  1827. }
  1828. void TBAutoSize(PTBSTATE ptb)
  1829. {
  1830.     if (ptb)
  1831.     {
  1832.         HWND hwndParent;
  1833.         RECT rc;
  1834.         hwndParent = GetParent(ptb->ci.hwnd);
  1835.         if (!hwndParent)
  1836.             return;
  1837.         if (ptb->ci.style & TBSTYLE_WRAPABLE)
  1838.         {
  1839.             RECT rcNew;
  1840.             if ((ptb->ci.style & CCS_NORESIZE) || (ptb->ci.style & CCS_NOPARENTALIGN)) {
  1841.                 GetWindowRect(ptb->ci.hwnd, &rc);
  1842.             } else {
  1843.                 GetWindowRect(hwndParent, &rc);
  1844.             }
  1845.             WrapToolbar(ptb, rc.right - rc.left, &rcNew, NULL);
  1846.             FlushToolTipsMgr(ptb);
  1847.         }
  1848.         GetWindowRect(ptb->ci.hwnd, &rc);
  1849.         MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 2);
  1850.         NewSize(ptb->ci.hwnd, ptb->iButHeight * CountRows(ptb) + g_cxEdge * 2, ptb->ci.style,
  1851.                 rc.left, rc.top, rc.right, rc.bottom);
  1852.     }
  1853. }
  1854. void TBSetStyle(PTBSTATE ptb, DWORD dwStyle)
  1855. {
  1856.     if (ptb)
  1857.     {
  1858.         BOOL fSizeChanged = FALSE;
  1859.         if ((BOOL)(ptb->ci.style & TBSTYLE_WRAPABLE) != (BOOL)(dwStyle & TBSTYLE_WRAPABLE))
  1860.         {
  1861.             int i;
  1862.             fSizeChanged = TRUE;
  1863.             for (i=0; i<ptb->iNumButtons; i++)
  1864.                 ptb->Buttons[i].fsState &= ~TBSTATE_WRAP;
  1865.         }
  1866.         ptb->ci.style = dwStyle;
  1867.         if (fSizeChanged)
  1868.             TBRecalc(ptb);
  1869.         TBAutoSize(ptb);
  1870. #ifndef WINNT
  1871.         DebugMsg(DM_TRACE, TEXT("toolbar window style changed %x"), ptb->ci.style);
  1872. #endif
  1873.     }
  1874. }
  1875. LRESULT TB_OnSetImage(PTBSTATE ptb, PTBBUTTON ptbButton, int iImage)
  1876. {
  1877.     if (!ptb->fHimlNative) {
  1878.         if (ptb->fHimlValid) {
  1879.             if (!ptb->himl ||
  1880.                 iImage >= ImageList_GetImageCount(ptb->himl))
  1881.                 return FALSE;
  1882.         } else {
  1883.             PTBBMINFO pTemp;
  1884.             int nBitmap;
  1885.             UINT nTot;
  1886.             // we're not natively himl and we've got some invalid
  1887.             // image state, so we need to count the bitmaps ourselvesa
  1888.             pTemp = ptb->pBitmaps;
  1889.             nTot = 0;
  1890.             for (nBitmap=0; nBitmap < ptb->nBitmaps; nBitmap++) {
  1891.                 nTot += pTemp->nButtons;
  1892.                 pTemp++;
  1893.             }
  1894.             if (iImage >= (int)nTot)
  1895.                 return FALSE;
  1896.         }
  1897.     }
  1898.     
  1899.     ptbButton->iBitmap = iImage;
  1900.     InvalidateButton(ptb, ptbButton, FALSE);
  1901.     return TRUE;
  1902. }
  1903. void TB_OnDestroy(PTBSTATE ptb)
  1904. {
  1905.     if (ptb)
  1906.     {
  1907.         HWND hwnd = ptb->ci.hwnd;
  1908.         int i;
  1909.         for (i = 0; i < ptb->iNumButtons; i++) {
  1910.             if (TBISSTRINGPTR(ptb->Buttons[i].iString))
  1911.                 Str_Set((LPTSTR*)&ptb->Buttons[i].iString, NULL);
  1912.         }
  1913.         //
  1914.         // If the toolbar created tooltips, then destroy them.
  1915.         //
  1916.         if ((ptb->ci.style & TBSTYLE_TOOLTIPS) && IsWindow(ptb->hwndToolTips)) {
  1917.             DestroyWindow (ptb->hwndToolTips);
  1918.             ptb->hwndToolTips = NULL;
  1919.         }
  1920.         if (ptb->hDragProxy)
  1921.             DestroyDragProxy(ptb->hDragProxy);
  1922.         if (ptb->hbmMono)
  1923.             DeleteObject(ptb->hbmMono);
  1924.         ReleaseMonoDC(ptb);
  1925.         if (ptb->nStrings > 0)
  1926.             DestroyStrings(ptb);
  1927.         if (ptb->hfontIcon && ptb->fFontCreated)
  1928.             DeleteObject(ptb->hfontIcon);
  1929.         // only do this destroy if pBitmaps exists..
  1930.         // this is our signal that it was from an old style toolba
  1931.         // and we created it ourselves.
  1932.         if (ptb->pBitmaps && ptb->himl)
  1933.             ImageList_Destroy(ptb->himl);
  1934.         if (ptb->pBitmaps)
  1935.             LocalFree(ptb->pBitmaps);
  1936.         LocalFree((HLOCAL)ptb);
  1937.         SetWindowInt(hwnd, 0, 0);
  1938.     }
  1939.     TerminateDitherBrush();
  1940. }
  1941. void TB_OnSetState(PTBSTATE ptb, PTBBUTTON ptbButton, BYTE bState, int iPos)
  1942. {
  1943.     BYTE fsState;
  1944.     fsState = bState ^ ptbButton->fsState;
  1945.     ptbButton->fsState = bState;
  1946.     if (fsState)
  1947.     {
  1948.         if (fsState & TBSTATE_HIDDEN)
  1949.             InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1950.         else
  1951.             InvalidateButton(ptb, ptbButton, TRUE);
  1952.         MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT,
  1953.                          iPos+1);
  1954.     }
  1955. }
  1956. void TB_OnSetCmdID(PTBSTATE ptb, PTBBUTTON ptbButton, UINT idCommand)
  1957. {
  1958.     UINT uiOldID;
  1959.     uiOldID = ptbButton->idCommand;
  1960.     ptbButton->idCommand = idCommand;
  1961.     
  1962.     //
  1963.     // If the app was using tooltips, then
  1964.     // we need to update the command id there also.
  1965.     //
  1966.     
  1967.     if(ptb->hwndToolTips) {
  1968.         TOOLINFO ti;
  1969.     
  1970.         //
  1971.         // Query the old information
  1972.         //
  1973.     
  1974.         ti.cbSize = sizeof(ti);
  1975.         ti.hwnd = ptb->ci.hwnd;
  1976.         ti.uId = uiOldID;
  1977.         SendMessage(ptb->hwndToolTips, TTM_GETTOOLINFO, 0,
  1978.                     (LPARAM)(LPTOOLINFO)&ti);
  1979.     
  1980.         //
  1981.         // Delete the old tool since we can't just
  1982.         // change the command id.
  1983.         //
  1984.     
  1985.         SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0,
  1986.                     (LPARAM)(LPTOOLINFO)&ti);
  1987.     
  1988.         //
  1989.         // Add the new tool with the new command id.
  1990.         //
  1991.     
  1992.         ti.uId = idCommand;
  1993.         SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  1994.                     (LPARAM)(LPTOOLINFO)&ti);
  1995.     }
  1996. }
  1997. LRESULT TB_OnSetButtonInfo(PTBSTATE ptb, int idBtn, LPTBBUTTONINFO ptbbi)
  1998. {
  1999.     int iPos;
  2000.     
  2001.     if (ptbbi->cbSize != SIZEOF(TBBUTTONINFO))
  2002.         return 0;
  2003.     iPos = PositionFromID(ptb, idBtn);
  2004.     if (iPos != -1)
  2005.     {
  2006.         PTBBUTTON ptbButton;
  2007.         BOOL fInvalidate = FALSE;
  2008.     
  2009.         ptbButton = ptb->Buttons + iPos;
  2010.     
  2011.         if (ptbbi->dwMask & TBIF_STYLE) {
  2012.             ptbbi->fsStyle = ptbbi->fsStyle;
  2013.             fInvalidate = TRUE;
  2014.         }
  2015.             
  2016.         if (ptbbi->dwMask & TBIF_STATE) {
  2017.             TB_OnSetState(ptb, ptbButton, ptbbi->fsState, iPos);
  2018.         }
  2019.         
  2020.         if (ptbbi->dwMask & TBIF_IMAGE) {
  2021.             TB_OnSetImage(ptb, ptbButton, ptbbi->iImage);
  2022.         }
  2023.         
  2024.         if (ptbbi->dwMask & TBIF_TEXT) { 
  2025.             ptb->fNoStringPool = TRUE;
  2026.             if (!TBISSTRINGPTR(ptbButton->iString)) {
  2027.                 ptbButton->iString = 0;
  2028.             }
  2029.     
  2030.             Str_Set((LPTSTR*)&ptbButton->iString, ptbbi->pszText);
  2031.             fInvalidate = TRUE;
  2032.         }
  2033.     
  2034.         if (ptbbi->dwMask & TBIF_LPARAM) {
  2035.             ptbButton->dwData = ptbbi->lParam;
  2036.         }
  2037.     
  2038.         if (ptbbi->dwMask & TBIF_COMMAND) {
  2039.             TB_OnSetCmdID(ptb, ptbButton, ptbbi->idCommand);
  2040.         }
  2041.     
  2042.         if (fInvalidate)
  2043.             InvalidateButton(ptb, ptbButton, FALSE);
  2044.     
  2045.         return TRUE;
  2046.     }
  2047.     return FALSE;
  2048. }
  2049. LRESULT TB_OnGetButtonInfo(PTBSTATE ptb, int idBtn, LPTBBUTTONINFO ptbbi)
  2050. {
  2051.     int iPos;
  2052.     
  2053.     if (ptbbi->cbSize != SIZEOF(TBBUTTONINFO))
  2054.         return 0;
  2055.     iPos = PositionFromID(ptb, idBtn);
  2056.     if (iPos != -1)
  2057.     {
  2058.         PTBBUTTON ptbButton;
  2059.         ptbButton = ptb->Buttons + iPos;
  2060.     
  2061.         if (ptbbi->dwMask & TBIF_STYLE) {
  2062.             ptbbi->fsStyle = ptbButton->fsStyle;
  2063.         }
  2064.             
  2065.         if (ptbbi->dwMask & TBIF_STATE) {
  2066.             ptbbi->fsState = ptbButton->fsState;
  2067.         }
  2068.         
  2069.         if (ptbbi->dwMask & TBIF_IMAGE) {
  2070.             ptbbi->iImage = ptbButton->iBitmap;
  2071.         }
  2072.         
  2073.         if (ptbbi->dwMask & TBIF_TEXT) { 
  2074.     
  2075.             if (TBISSTRINGPTR(ptbButton->iString)) {
  2076.                 lstrcpyn(ptbbi->pszText, (LPCTSTR)ptbButton->iString, ptbbi->cchText);
  2077.             } 
  2078.         }
  2079.     
  2080.         if (ptbbi->dwMask & TBIF_LPARAM) {
  2081.             ptbbi->lParam = ptbButton->dwData;
  2082.         }
  2083.     
  2084.         if (ptbbi->dwMask & TBIF_COMMAND) {
  2085.             ptbbi->idCommand = ptbButton->idCommand;
  2086.         }
  2087.     
  2088.         return TRUE;
  2089.     }
  2090.     return FALSE;
  2091. }
  2092. #ifdef UNICODE    
  2093. LRESULT TB_OnSetButtonInfoA(PTBSTATE ptb, int idBtn, LPTBBUTTONINFOA ptbbiA)
  2094. {
  2095.     TBBUTTONINFO tbbi = *(LPTBBUTTONINFO)ptbbiA;
  2096.     WCHAR szText[256];
  2097.     
  2098.     if (ptbbiA->dwMask & TBIF_TEXT) {
  2099.         tbbi.pszText = szText;
  2100.         tbbi.cchText = ARRAYSIZE(szText);
  2101.         MultiByteToWideChar(CP_ACP, 0, (LPCSTR) ptbbiA->pszText, -1,
  2102.                             szText, ARRAYSIZE(szText));
  2103.     }
  2104.     
  2105.     return TB_OnSetButtonInfo(ptb, idBtn, (LPTBBUTTONINFO)&tbbi);
  2106. }
  2107. LRESULT TB_OnGetButtonInfoA(PTBSTATE ptb, int idBtn, LPTBBUTTONINFOA ptbbiA)
  2108. {
  2109.     PTBBUTTON ptbButton;
  2110.     int iPos;
  2111.     DWORD dwMask = ptbbiA->dwMask;
  2112.     
  2113.     ptbbiA->dwMask &= ~TBIF_TEXT;
  2114.     
  2115.     if (!TB_OnGetButtonInfo(ptb, idBtn, (LPTBBUTTONINFO)ptbbiA))
  2116.         return 0;
  2117.     iPos = PositionFromID(ptb, (int)ptbbiA->idCommand);
  2118.     ptbButton = ptb->Buttons + iPos;
  2119.     
  2120.     ptbbiA->dwMask = dwMask;
  2121.     if (ptbbiA->dwMask & TBIF_TEXT) {
  2122.         if (TBISSTRINGPTR(ptbButton->iString)) {
  2123.             WideCharToMultiByte (CP_ACP, 0, (LPCTSTR)ptbButton->iString,
  2124.                                  -1, ptbbiA->pszText , ptbbiA->cchText, NULL, NULL);
  2125.         } else {
  2126.             ptbbiA->pszText[0] = 0;
  2127.         }
  2128.     }
  2129.     return 1;
  2130. }
  2131. #endif
  2132. LRESULT CALLBACK ToolbarWndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  2133. {
  2134.     BOOL fSameButton;
  2135.     PTBBUTTON ptbButton;
  2136.     int iPos;
  2137.     DWORD dw;
  2138.     PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
  2139.     if (!ptb && wMsg > WM_USER) 
  2140.         goto DoDefault;
  2141.     
  2142.     switch (wMsg) {
  2143.     case WM_NCCREATE:
  2144. #define lpcs ((LPCREATESTRUCT)lParam)
  2145.         InitDitherBrush();
  2146.         InitGlobalColors();
  2147.     /* create the state data for this toolbar */
  2148.         ptb = (PTBSTATE)LocalAlloc(LPTR, sizeof(TBSTATE) - sizeof(TBBUTTON));
  2149.         if (!ptb)
  2150.             return 0; // WM_NCCREATE failure is 0
  2151.     // note, zero init memory from above
  2152.         CIInitialize(&ptb->ci, hwnd, lpcs);
  2153.         ptb->xFirstButton = s_xFirstButton;
  2154.         ptb->nCurHTButton = -1;
  2155.         Assert(ptb->uStructSize == 0);
  2156.         Assert(ptb->hfontIcon == NULL);  // initialize to null.
  2157.         Assert(ptb->iButMinWidth == 0);
  2158.         Assert(ptb->iButMaxWidth == 0);
  2159.         
  2160.         ptb->nTextRows = 1;
  2161.         ptb->fActive = TRUE;
  2162.         // IE 3 passes in TBSTYLE_FLAT, but they really
  2163.         // wanted TBSTYLE_TRANSPARENT also.
  2164.         //
  2165.         if (ptb->ci.style & TBSTYLE_FLAT) {
  2166.             ptb->ci.style |= TBSTYLE_TRANSPARENT;
  2167.         }
  2168. #ifdef DEBUG
  2169.         if (IsFlagSet(g_dwPrototype, PTF_FLATLOOK))
  2170.         {
  2171.             TraceMsg(TF_GENERAL, "Using flat look for toolbars.");
  2172.             ptb->ci.style |= TBSTYLE_FLAT;
  2173.         }
  2174. #endif
  2175.         // Now Initialize the hfont we will use.
  2176.         TBChangeFont(ptb, 0, NULL);
  2177.         // grow the button size to the appropriate girth
  2178.         if (!SetBitmapSize(ptb, DEFAULTBITMAPX, DEFAULTBITMAPX))
  2179.         {
  2180.             LocalFree((HLOCAL)ptb);
  2181.             return 0; // WM_NCCREATE failure is 0
  2182.         }
  2183.         SetWindowInt(hwnd, 0, (int)ptb);
  2184.         if (!(ptb->ci.style & (CCS_TOP | CCS_NOMOVEY | CCS_BOTTOM)))
  2185.         {
  2186.             ptb->ci.style |= CCS_TOP;
  2187.             SetWindowLong(hwnd, GWL_STYLE, ptb->ci.style);
  2188.         }
  2189.         if (ptb->ci.style & TBSTYLE_TOOLTIPS)
  2190.         {
  2191.             TOOLINFO ti;
  2192.             // don't bother setting the rect because we'll do it below
  2193.             // in FlushToolTipsMgr;
  2194.             ti.cbSize = sizeof(ti);
  2195.             ti.uFlags = TTF_IDISHWND;
  2196.             ti.hwnd = hwnd;
  2197.             ti.uId = (UINT)hwnd;
  2198.             ti.lpszText = 0;
  2199.             ptb->hwndToolTips = CreateWindow(c_szSToolTipsClass, NULL,
  2200.             WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  2201.             hwnd, NULL, lpcs->hInstance, NULL);
  2202.             SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  2203.                 (LPARAM)(LPTOOLINFO)&ti);
  2204.         }
  2205.         return TRUE;
  2206.     case WM_CREATE:
  2207.         if (ptb->ci.style & TBSTYLE_REGISTERDROP)
  2208.         {
  2209.             ptb->hDragProxy = CreateDragProxy(ptb->ci.hwnd, ToolbarDragCallback, TRUE);
  2210.         }
  2211.         goto DoDefault;
  2212.     case WM_DESTROY:
  2213.         TB_OnDestroy(ptb);
  2214.         break;
  2215.         
  2216.     case WM_SETFONT:
  2217.         if (ptb)
  2218.             TBSetFont(ptb, (HFONT)wParam, (BOOL)lParam);
  2219.         return TRUE;
  2220.     case WM_NCCALCSIZE:
  2221.         // let defwindowproc handle the standard borders etc...
  2222.         dw = DefWindowProc(hwnd, wMsg, wParam, lParam ) ;
  2223.         // add the extra edge at the top of the toolbar to seperate from the menu bar
  2224.         if (ptb && !(ptb->ci.style & CCS_NODIVIDER))
  2225.         {
  2226.                 ((NCCALCSIZE_PARAMS FAR *)lParam)->rgrc[0].top += g_cyEdge;
  2227.         }
  2228.         return dw;
  2229.     case WM_NCHITTEST:
  2230.         return HTCLIENT;
  2231.     case WM_NCACTIVATE:
  2232.         if (ptb && (ptb->fActive != (BOOL)wParam)) {
  2233.             int iButton;
  2234.             PTBBUTTON pButton;
  2235.             ptb->fActive = (BOOL) wParam;
  2236.             for (iButton = 0; iButton < ptb->iNumButtons; iButton++) {
  2237.                 pButton = &ptb->Buttons[iButton];
  2238.                 InvalidateButton(ptb, pButton, FALSE);
  2239.             }
  2240.         }
  2241.         // fall through...
  2242.     case WM_NCPAINT:
  2243.         // old-style toolbars are forced to be without dividers above
  2244.         if (ptb && !(ptb->ci.style & CCS_NODIVIDER))
  2245.         {
  2246.             RECT rc;
  2247.             HDC hdc = GetWindowDC(hwnd);
  2248.             GetWindowRect(hwnd, &rc);
  2249.             MapWindowRect(NULL, hwnd, &rc); // screen -> client
  2250.                 rc.bottom = -rc.top;                // bottom of NC area
  2251.                 rc.top = rc.bottom - g_cyEdge;
  2252.             DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM);
  2253.             ReleaseDC(hwnd, hdc);
  2254.         }
  2255.         goto DoDefault;
  2256.     case WM_PRINTCLIENT:
  2257.     case WM_PAINT:
  2258.         if (ptb)
  2259.             ToolbarPaint(ptb, (HDC)wParam);
  2260.         break;
  2261.     case WM_ERASEBKGND:
  2262.         if (ptb)
  2263.             TB_OnEraseBkgnd(ptb, (HDC) wParam);
  2264.         return(TRUE);
  2265.     case WM_SYSCOLORCHANGE:
  2266.         if (ptb)
  2267.         {
  2268.             TB_OnSysColorChange(ptb);
  2269.             if (ptb->hwndToolTips)
  2270.                 SendMessage(ptb->hwndToolTips, wMsg, wParam, lParam);
  2271.         }
  2272.         break;
  2273.     case TB_GETROWS:
  2274.         if (ptb)
  2275.             return CountRows(ptb);
  2276.         break;
  2277.     
  2278.     case TB_SETROWS:
  2279.         if (ptb)
  2280.         {
  2281.             RECT rc;
  2282.             if (BoxIt(ptb, LOWORD(wParam), HIWORD(wParam), &rc))
  2283.             {
  2284.                 FlushToolTipsMgr(ptb);
  2285.                 SetWindowPos(hwnd, NULL, 0, 0, rc.right, rc.bottom,
  2286.                              SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
  2287.                 InvalidateRect(hwnd, NULL, TRUE);
  2288.             }
  2289.             if (lParam)
  2290.                 *((RECT FAR *)lParam) = rc;
  2291.         }
  2292.         break;
  2293.     case WM_WINDOWPOSCHANGING:
  2294.         if (ptb && (ptb->ci.style & TBSTYLE_FLAT))
  2295.         {
  2296.             LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
  2297.             RECT rc;
  2298.             HWND hwndParent;
  2299.             if (!(lpwp->flags & SWP_NOMOVE) && (hwndParent = GetParent(hwnd)))
  2300.             {
  2301.                 GetWindowRect(hwnd, &rc);
  2302.                 MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 1);
  2303.                 if ((rc.left != lpwp->x) || (rc.top != lpwp->y))
  2304.                     lpwp->flags |= SWP_NOREDRAW;
  2305.             }
  2306.         }
  2307.         goto DoDefault;
  2308.     case WM_MOVE:
  2309.         // JJK TODO: This needs to be double buffered to get rid of the flicker
  2310.         if (ptb && (ptb->ci.style & TBSTYLE_FLAT))
  2311.             InvalidateRect(hwnd, NULL, TRUE);
  2312.         goto DoDefault;
  2313.     case TB_AUTOSIZE:
  2314.     case WM_SIZE:
  2315.         TBAutoSize(ptb);
  2316.         break;
  2317.     case WM_COMMAND:
  2318.     case WM_DRAWITEM:
  2319.     case WM_MEASUREITEM:
  2320.     case WM_VKEYTOITEM:
  2321.     case WM_CHARTOITEM:
  2322.         if (ptb)
  2323.             SendMessage(ptb->ci.hwndParent, wMsg, wParam, lParam);
  2324.         break;
  2325.     case WM_RBUTTONDBLCLK:
  2326.         if (ptb && !CCSendNotify(&ptb->ci, NM_RDBLCLK, NULL))
  2327.             goto DoDefault;
  2328.         break;
  2329.     case WM_RBUTTONUP:
  2330.         if (ptb) {
  2331.             NMTBRCLICK nm;
  2332.             HWND hwndParent = NULL;
  2333.             
  2334.             nm.uiIndex = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2335.             if (nm.uiIndex >= 0)
  2336.                 nm.uiCmd = ptb->Buttons[nm.uiIndex].idCommand;
  2337.             else    
  2338.                 nm.uiCmd = (UINT) -1;
  2339.             LPARAM_TO_POINT(lParam, nm.ptMouse);
  2340.             if (hwndParent = GetParent(hwnd))    
  2341.                 MapWindowPoints(hwndParent, HWND_DESKTOP, &nm.ptMouse, 1);
  2342.             
  2343.             if (!CCSendNotify(&ptb->ci, NM_RCLICK, (LPNMHDR )&nm))
  2344.                 goto DoDefault;
  2345.         }        
  2346.         break;
  2347.     case WM_LBUTTONDBLCLK:
  2348.         if (!ptb) {
  2349.             break;
  2350.         }
  2351.         iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2352.     if (iPos < 0 && (ptb->ci.style & CCS_ADJUSTABLE))
  2353.     {
  2354.         iPos = -1 - iPos;
  2355.         CustomizeTB(ptb, iPos);
  2356.         } else {
  2357.             goto HandleLButtonDown;
  2358.     }
  2359.     break;
  2360.     case WM_LBUTTONDOWN:
  2361.         if (!ptb) {
  2362.             break;
  2363.         }
  2364.         RelayToToolTips(ptb->hwndToolTips, hwnd, wMsg, wParam, lParam);
  2365.         iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2366.         if ((ptb->ci.style & CCS_ADJUSTABLE) &&
  2367.                  (((wParam & MK_SHIFT) && !(ptb->ci.style & TBSTYLE_ALTDRAG)) ||
  2368.                   ((GetKeyState(VK_MENU) & ~1) && (ptb->ci.style & TBSTYLE_ALTDRAG))))
  2369.         {
  2370.             MoveButton(ptb, iPos);
  2371.         }
  2372.         else {
  2373. HandleLButtonDown:
  2374.             if (iPos >= 0 && iPos < ptb->iNumButtons)
  2375.             {
  2376.                 // should this check for the size of the button struct?
  2377.                 ptbButton = ptb->Buttons + iPos;
  2378.                 if (ptbButton->fsStyle & TBSTYLE_DROPDOWN) {
  2379.                     if (ptbButton->fsState & TBSTATE_ENABLED) {
  2380.                         ptbButton->fsState ^= TBSTATE_PRESSED;
  2381.                         InvalidateButton(ptb, ptbButton, TRUE);
  2382.                         UpdateWindow(hwnd);
  2383.                         MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
  2384.                                 OBJID_CLIENT, iPos+1);
  2385.                         if (!SendItemNotify(ptb, ptbButton->idCommand, TBN_DROPDOWN)) {
  2386.                             MSG msg;
  2387.                             PeekMessage(&msg, ptb->ci.hwnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
  2388.                             ptbButton->fsState ^= TBSTATE_PRESSED;
  2389.                             InvalidateButton(ptb, ptbButton, TRUE);
  2390.                             UpdateWindow(hwnd);
  2391.                             MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
  2392.                                 OBJID_CLIENT, iPos+1);
  2393.                         }
  2394.                     }
  2395.                 } else {
  2396.                     ptb->pCaptureButton = ptbButton;
  2397.                     SetCapture(hwnd);
  2398.                     if (ptbButton->fsState & TBSTATE_ENABLED)
  2399.                     {
  2400.                         ptbButton->fsState |= TBSTATE_PRESSED;
  2401.                         InvalidateButton(ptb, ptbButton, TRUE);
  2402.                         UpdateWindow(hwnd);         // imedeate feedback
  2403.                         MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
  2404.                             OBJID_CLIENT, iPos+1);
  2405.                     }
  2406.                     SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_BEGINDRAG);
  2407.                 }
  2408.             }
  2409.     }
  2410.     break;
  2411.     case WM_MOUSELEAVE:
  2412.         if (ptb)
  2413.         {
  2414.             RECT rectButton;
  2415.             TRACKMOUSEEVENT tme;
  2416.             ASSERT(ptb->ci.style & TBSTYLE_FLAT);
  2417.             tme.cbSize = sizeof(TRACKMOUSEEVENT);
  2418.             tme.dwFlags = TME_CANCEL | TME_LEAVE;
  2419.             tme.hwndTrack = hwnd;
  2420.             TrackMouseEvent(&tme);
  2421.             ptb->fMouseTrack = FALSE;
  2422.             if (ptb->nCurHTButton != -1)
  2423.                 if (GetItemRect(ptb, ptb->nCurHTButton, &rectButton) )
  2424.                     InvalidateRect(hwnd, &rectButton, TRUE);
  2425.             ptb->nCurHTButton = -1;
  2426.         }
  2427.         break;
  2428.     case WM_MOUSEMOVE:
  2429.         if (ptb && ptb->fActive)
  2430.         {
  2431.             RECT rectButton;
  2432.             if ((ptb->ci.style & TBSTYLE_FLAT) )
  2433.             {
  2434.                 iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2435.                 if (!ptb->fMouseTrack)
  2436.                 {
  2437.                     TRACKMOUSEEVENT tme;
  2438.                     tme.cbSize = sizeof(TRACKMOUSEEVENT);
  2439.                     tme.dwFlags = TME_LEAVE;
  2440.                     tme.hwndTrack = hwnd;
  2441.                     ptb->fMouseTrack = TRUE;
  2442.                     TrackMouseEvent(&tme);
  2443.                 }
  2444.                 if ((ptb->nCurHTButton != iPos) && (ptb->nCurHTButton != -1))
  2445.                 {
  2446.                     if (GetItemRect(ptb, ptb->nCurHTButton, &rectButton) )
  2447.                         InvalidateRect(hwnd, &rectButton, TRUE);
  2448.                     ptb->nCurHTButton = -1;
  2449.                 }
  2450.                 if ((iPos >= 0) && (ptb->nCurHTButton != iPos))
  2451.                 {
  2452.                     if ((ptb->Buttons[iPos].fsState & TBSTATE_ENABLED) )
  2453.                     {
  2454.                         ptb->nCurHTButton = iPos;
  2455.                         if (GetItemRect(ptb, iPos, &rectButton) )
  2456.                             InvalidateRect(hwnd, &rectButton, TRUE);
  2457.                     }
  2458.                 }
  2459.             }
  2460.             RelayToToolTips(ptb->hwndToolTips, hwnd, wMsg, wParam, lParam);
  2461.             // if the toolbar has lost the capture for some reason, stop
  2462.             if (ptb->pCaptureButton == NULL) {
  2463.                 //DebugMsg(DM_TRACE, TEXT("Bail because pCaptureButton == NULL"));
  2464.                 break;
  2465.             }
  2466.             if (hwnd != GetCapture())
  2467.             {
  2468.                 //DebugMsg(DM_TRACE, TEXT("capture isn't us"));
  2469.                 SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_ENDDRAG);
  2470.                 // if the button is still pressed, unpress it.
  2471.                 if (ptb->pCaptureButton->fsState & TBSTATE_PRESSED)
  2472.                     SendMessage(hwnd, TB_PRESSBUTTON, ptb->pCaptureButton->idCommand, 0L);
  2473.                 ptb->pCaptureButton = NULL;
  2474.             }
  2475.             else if (ptb->pCaptureButton->fsState & TBSTATE_ENABLED)
  2476.             {
  2477.                 //DebugMsg(DM_TRACE, TEXT("capture IS us, and state is enabled"));
  2478.                 iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2479.                 fSameButton = (iPos >= 0 && ptb->pCaptureButton == ptb->Buttons + iPos);
  2480.                 if (fSameButton == !(ptb->pCaptureButton->fsState & TBSTATE_PRESSED))
  2481.                 {
  2482.                      //DebugMsg(DM_TRACE, TEXT("capture IS us, and Button is different"));
  2483.                     ptb->pCaptureButton->fsState ^= TBSTATE_PRESSED;
  2484.                     InvalidateButton(ptb, ptb->pCaptureButton, TRUE);
  2485.                     MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
  2486.                         OBJID_CLIENT, (ptb->pCaptureButton - ptb->Buttons) + 1);
  2487.                 }
  2488.             }
  2489.         }
  2490.         break;
  2491.     case WM_LBUTTONUP:
  2492.         if (!ptb) {
  2493.             break;
  2494.         }
  2495.         RelayToToolTips(ptb->hwndToolTips, hwnd, wMsg, wParam, lParam);
  2496.         if (ptb->pCaptureButton != NULL) {
  2497.             int idCommand = ptb->pCaptureButton->idCommand;
  2498.             ReleaseCapture();
  2499.             SendItemNotify(ptb, idCommand, TBN_ENDDRAG);
  2500.             iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2501.             if ((ptb->pCaptureButton->fsState & TBSTATE_ENABLED) && iPos >=0
  2502.                 && (ptb->pCaptureButton == ptb->Buttons+iPos)) {
  2503.                 ptb->pCaptureButton->fsState &= ~TBSTATE_PRESSED;
  2504.                 if (ptb->pCaptureButton->fsStyle & TBSTYLE_CHECK) {
  2505.                     if (ptb->pCaptureButton->fsStyle & TBSTYLE_GROUP) {
  2506.                         // group buttons already checked can't be force
  2507.                         // up by the user.
  2508.                         if (ptb->pCaptureButton->fsState & TBSTATE_CHECKED) {
  2509.                             ptb->pCaptureButton = NULL;
  2510.                             break; // bail!
  2511.                         }
  2512.                         ptb->pCaptureButton->fsState |= TBSTATE_CHECKED;
  2513.                         MakeGroupConsistant(ptb, idCommand);
  2514.                     } else {
  2515.                         ptb->pCaptureButton->fsState ^= TBSTATE_CHECKED; // toggle
  2516.                     }
  2517.                 }
  2518.                 InvalidateButton(ptb, ptb->pCaptureButton, TRUE);
  2519.                 ptb->pCaptureButton = NULL;
  2520.                 MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,  OBJID_CLIENT,
  2521.                     iPos+1);
  2522.                 FORWARD_WM_COMMAND(ptb->ci.hwndParent, idCommand, hwnd, BN_CLICKED, SendMessage);
  2523.             }
  2524.             else {
  2525.                 ptb->pCaptureButton = NULL;
  2526.             }
  2527.         }
  2528.         break;
  2529.     case WM_WININICHANGE:
  2530.         if (!ptb) {
  2531.             break;
  2532.         }
  2533.         InitGlobalMetrics(wParam);
  2534.         if (ptb->fFontCreated)
  2535.             TBChangeFont(ptb, wParam, NULL);
  2536.         if (ptb->hwndToolTips)
  2537.             SendMessage(ptb->hwndToolTips, wMsg, wParam, lParam);
  2538.         break;
  2539.     case WM_NOTIFYFORMAT:
  2540.         if (ptb)
  2541.             return CIHandleNotifyFormat(&ptb->ci, lParam);
  2542.         break;
  2543.     case WM_NOTIFY:
  2544. #define lpNmhdr ((LPNMHDR)(lParam))
  2545.         if (ptb)
  2546.         {
  2547.             int i = PositionFromID(ptb, lpNmhdr->idFrom);
  2548.             BOOL fEllipsied = FALSE;
  2549.             //
  2550.             // We are just going to pass this on to the
  2551.             // real parent.  Note that -1 is used as
  2552.             // the hwndFrom.  This prevents SendNotifyEx
  2553.             // from updating the NMHDR structure.
  2554.             //
  2555.             LRESULT lres = SendNotifyEx(ptb->ci.hwndParent, (HWND) -1,
  2556.                          lpNmhdr->code, lpNmhdr, ptb->ci.bUnicode);
  2557. #define lpnmTT ((LPTOOLTIPTEXT) lParam)
  2558.             if (i != -1) {
  2559.                 fEllipsied = (BOOL)(ptb->Buttons[i].fsState & TBSTATE_ELLIPSES);
  2560.                 if ((lpNmhdr->code == TTN_NEEDTEXT) && 
  2561.                     (!ptb->nTextRows || fEllipsied) &&
  2562.                     lpnmTT->lpszText && !lpnmTT->lpszText[0])
  2563.                 {
  2564.                     LPCTSTR psz = TB_StrForButton(ptb, &ptb->Buttons[i]);
  2565.                     if (psz)
  2566.                         lpnmTT->lpszText = (LPTSTR)psz;
  2567.                 }
  2568.             }
  2569.             return(lres);
  2570.         }
  2571.         break;
  2572.     case WM_STYLECHANGING:
  2573.         if (!ptb)
  2574.             break;
  2575.         if (wParam == GWL_STYLE)
  2576.         {
  2577.             LPSTYLESTRUCT lpStyle = (LPSTYLESTRUCT) lParam;
  2578.             if ((lpStyle->styleOld ^ lpStyle->styleNew) == WS_VISIBLE)
  2579.                 // MFC can't hide a window to save their lives -- and they
  2580.                 // think that flipping bits directly will actually give them
  2581.                 // something -- don't let 'em yank the visible bit directly
  2582.                 // ... morons ... jeffbog 9/13/96
  2583.                 lpStyle->styleNew |= WS_VISIBLE;
  2584.         }
  2585.         break;
  2586.     case WM_STYLECHANGED:
  2587.         if (wParam == GWL_STYLE)
  2588.         {
  2589.             TBSetStyle(ptb, ((LPSTYLESTRUCT)lParam)->styleNew);
  2590.         }
  2591.         return 0;
  2592.     case TB_SETSTYLE:
  2593.         TBSetStyle(ptb, lParam);
  2594.         break;
  2595.     case TB_GETSTYLE:
  2596.         return (ptb->ci.style);
  2597.     case TB_GETBUTTONSIZE:
  2598.         return (MAKELONG(ptb->iButWidth,ptb->iButHeight));
  2599.     case TB_SETBUTTONWIDTH:
  2600.         ptb->iButMinWidth  = LOWORD(lParam);
  2601.         ptb->iButMaxWidth = HIWORD(lParam);
  2602.         ptb->iButWidth = 0;
  2603.         TBRecalc(ptb);
  2604.         return TRUE;
  2605.     case TB_SETSTATE:
  2606.         iPos = PositionFromID(ptb, (int)wParam);
  2607.         if (iPos < 0)
  2608.             return FALSE;
  2609.         ptbButton = ptb->Buttons + iPos;
  2610.         TB_OnSetState(ptb, ptbButton, (BYTE)(LOWORD(lParam)), iPos);
  2611.         return TRUE;
  2612.     // set the cmd ID of a button based on its position
  2613.     case TB_SETCMDID:
  2614.         if (wParam >= (UINT)ptb->iNumButtons)
  2615.             return FALSE;
  2616.         TB_OnSetCmdID(ptb, &ptb->Buttons[wParam], (UINT)lParam);
  2617.         return TRUE;
  2618.     case TB_GETSTATE:
  2619.         iPos = PositionFromID(ptb, (int)wParam);
  2620.         if (iPos < 0)
  2621.             return -1L;
  2622.         return ptb->Buttons[iPos].fsState;
  2623.     case TB_ENABLEBUTTON:
  2624.     case TB_CHECKBUTTON:
  2625.     case TB_PRESSBUTTON:
  2626.     case TB_HIDEBUTTON:
  2627.     case TB_INDETERMINATE:
  2628.     case TB_HIGHLIGHTBUTTON:
  2629.     {
  2630.         BYTE fsState;
  2631.         
  2632.         iPos = PositionFromID(ptb, (int)wParam);
  2633.         if (iPos < 0)
  2634.             return FALSE;
  2635.         ptbButton = &ptb->Buttons[iPos];
  2636.         fsState = ptbButton->fsState;
  2637.         if (LOWORD(lParam))
  2638.             ptbButton->fsState |= wStateMasks[wMsg - TB_ENABLEBUTTON];
  2639.         else
  2640.             ptbButton->fsState &= ~wStateMasks[wMsg - TB_ENABLEBUTTON];
  2641.         // did this actually change the state?
  2642.         if (fsState != ptbButton->fsState) {
  2643.             // is this button a member of a group?
  2644.             if ((wMsg == TB_CHECKBUTTON) && (ptbButton->fsStyle & TBSTYLE_GROUP))
  2645.                 MakeGroupConsistant(ptb, (int)wParam);
  2646.             if (wMsg == TB_HIDEBUTTON) {
  2647.                 InvalidateRect(hwnd, NULL, TRUE);
  2648.                 FlushToolTipsMgr(ptb);
  2649.             } else
  2650.                 InvalidateButton(ptb, ptbButton, TRUE);
  2651.             MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, iPos+1);
  2652.         }
  2653.         return(TRUE);
  2654.     }
  2655.     case TB_ISBUTTONENABLED:
  2656.     case TB_ISBUTTONCHECKED:
  2657.     case TB_ISBUTTONPRESSED:
  2658.     case TB_ISBUTTONHIDDEN:
  2659.     case TB_ISBUTTONINDETERMINATE:
  2660.     case TB_ISBUTTONHIGHLIGHTED:
  2661.         iPos = PositionFromID(ptb, (int)wParam);
  2662.         if (iPos < 0)
  2663.             return(-1L);
  2664.         return (LRESULT)ptb->Buttons[iPos].fsState & wStateMasks[wMsg - TB_ISBUTTONENABLED];
  2665.     case TB_ADDBITMAP:
  2666. #ifdef WIN32
  2667.     case TB_ADDBITMAP32:    // only for compatibility with mail
  2668.     #define pab ((LPTBADDBITMAP)lParam)
  2669.         return AddBitmap(ptb, wParam, pab->hInst, pab->nID);
  2670.     #undef pab
  2671. #else
  2672.         return AddBitmap(ptb, wParam, (HINSTANCE)LOWORD(lParam), HIWORD(lParam));
  2673. #endif
  2674.     case TB_REPLACEBITMAP:
  2675.         return ReplaceBitmap(ptb, (LPTBREPLACEBITMAP)lParam);
  2676. #ifdef UNICODE
  2677.     case TB_ADDSTRINGA:
  2678.         {
  2679.         LPWSTR lpStrings;
  2680.         UINT   uiCount;
  2681.         LPSTR  lpAnsiString = (LPSTR) lParam;
  2682.         int    iResult;
  2683.         BOOL   bAllocatedMem = FALSE;
  2684.         if (!wParam && lpAnsiString) {
  2685.             //
  2686.             // We have to figure out how many characters
  2687.             // are in this string.
  2688.             //
  2689.             uiCount = 0;
  2690.             while (uiCount < MAXSTRINGSIZE) {
  2691.                uiCount++;
  2692.                if ((*lpAnsiString == 0) && (*(lpAnsiString+1) == 0)) {
  2693.                   uiCount++;  // needed for double null
  2694.                   break;
  2695.                }
  2696.                lpAnsiString++;
  2697.             }
  2698.             lpStrings = GlobalAlloc (GPTR, uiCount * sizeof(TCHAR));
  2699.             if (!lpStrings)
  2700.                 return -1;
  2701.             bAllocatedMem = TRUE;
  2702.             MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lParam, uiCount,
  2703.                                 lpStrings, uiCount);
  2704.         } else {
  2705.             lpStrings = (LPWSTR)lParam;
  2706.         }
  2707.         iResult = AddStrings(ptb, wParam, (LPARAM)lpStrings);
  2708.         if (bAllocatedMem)
  2709.             GlobalFree(lpStrings);
  2710.         return iResult;
  2711.         }
  2712. #endif
  2713.     case TB_ADDSTRING:
  2714.         return AddStrings(ptb, wParam, lParam);
  2715. #ifdef UNICODE
  2716.     case TB_ADDBUTTONSA:
  2717.         return InsertButtons(ptb, (UINT)-1, wParam, (LPTBBUTTON)lParam, FALSE);
  2718.         
  2719.     case TB_INSERTBUTTONA:
  2720.         return InsertButtons(ptb, wParam, 1, (LPTBBUTTON)lParam, FALSE);
  2721. #endif
  2722.         
  2723.     case TB_ADDBUTTONS:
  2724.         return InsertButtons(ptb, (UINT)-1, wParam, (LPTBBUTTON)lParam, TRUE);
  2725.         
  2726.     case TB_INSERTBUTTON:
  2727.         return InsertButtons(ptb, wParam, 1, (LPTBBUTTON)lParam, TRUE);
  2728.     case TB_DELETEBUTTON:
  2729.         return DeleteButton(ptb, wParam);
  2730.     case TB_GETBUTTON:
  2731.         if (wParam >= (UINT)ptb->iNumButtons)
  2732.             return(FALSE);
  2733.         TBOutputStruct(ptb, ptb->Buttons + wParam, (LPTBBUTTON)lParam);
  2734.         return TRUE;
  2735.     case TB_BUTTONCOUNT:
  2736.         return ptb->iNumButtons;
  2737.     case TB_COMMANDTOINDEX:
  2738.         return PositionFromID(ptb, (int)wParam);
  2739. #ifdef UNICODE
  2740.     case TB_SAVERESTOREA:
  2741.         {
  2742.         LPWSTR lpSubKeyW, lpValueNameW;
  2743.         TBSAVEPARAMSA * lpSaveA = (TBSAVEPARAMSA *) lParam;
  2744.         BOOL bResult;
  2745.         lpSubKeyW = ProduceWFromA (CP_ACP, lpSaveA->pszSubKey);
  2746.         lpValueNameW = ProduceWFromA (CP_ACP, lpSaveA->pszValueName);
  2747.         bResult = SaveRestoreFromReg(ptb, wParam, lpSaveA->hkr, lpSubKeyW, lpValueNameW);
  2748.         FreeProducedString(lpSubKeyW);
  2749.         FreeProducedString(lpValueNameW);
  2750.         return bResult;
  2751.         }
  2752. #endif
  2753.     case TB_SAVERESTORE:
  2754. #ifdef WIN32
  2755.     #define psr ((TBSAVEPARAMS *)lParam)
  2756.         return SaveRestoreFromReg(ptb, wParam, psr->hkr, psr->pszSubKey, psr->pszValueName);
  2757.     #undef psr
  2758. #else
  2759.         return SaveRestore(ptb, wParam, (LPTSTR FAR *)lParam);
  2760. #endif
  2761.     case TB_CUSTOMIZE:
  2762.         CustomizeTB(ptb, ptb->iNumButtons);
  2763.         break;
  2764.     case TB_GETRECT:
  2765.         // PositionFromID() accepts NULL ptbs!
  2766.         wParam = PositionFromID(ptb, wParam);
  2767.         // fall through
  2768.     case TB_GETITEMRECT:
  2769.         if (!lParam)
  2770.             break;
  2771.         return GetItemRect(ptb, wParam, (LPRECT)lParam);
  2772.     case TB_BUTTONSTRUCTSIZE:
  2773.         TBOnButtonStructSize(ptb, wParam);
  2774.         break;
  2775.     case TB_SETBUTTONSIZE:
  2776.         return GrowToolbar(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), FALSE);
  2777.     case TB_SETBITMAPSIZE:
  2778.         return SetBitmapSize(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2779.     case TB_SETIMAGELIST:
  2780.     {
  2781.         HIMAGELIST himl = ptb->himl;
  2782.         int cx, cy;
  2783.         ptb->himl = (HIMAGELIST)lParam;
  2784.         ptb->fHimlNative = TRUE;
  2785.         // Update the bitmap size based on this image list
  2786.         ImageList_GetIconSize(ptb->himl, &cx, &cy);
  2787.         SetBitmapSize(ptb, cx, cy);
  2788.         return (LRESULT)himl;
  2789.     }
  2790.     case TB_GETIMAGELIST:
  2791.         return (LRESULT)ptb->himl;
  2792.     case TB_SETHOTIMAGELIST:
  2793.         {
  2794.             HIMAGELIST himl = ptb->himlHot;
  2795.             ptb->himlHot = (HIMAGELIST)lParam;
  2796.             return (LRESULT)himl;
  2797.         }
  2798.     case TB_GETHOTIMAGELIST:
  2799.         return (LRESULT)ptb->himlHot;
  2800.     case TB_GETDISABLEDIMAGELIST:
  2801.         return (LRESULT)ptb->himlDisabled;
  2802.     case TB_SETDISABLEDIMAGELIST:
  2803.         {
  2804.             HIMAGELIST himl = ptb->himlDisabled;
  2805.             ptb->himlDisabled = (HIMAGELIST)lParam;
  2806.             return (LRESULT)himl;
  2807.         }
  2808.     case TB_GETOBJECT:
  2809.         if (IsEqualIID((IID *)wParam, &IID_IDropTarget))
  2810.         {
  2811.             // if we have not already registered create an unregistered target now
  2812.             if (ptb->hDragProxy == NULL)
  2813.                 ptb->hDragProxy = CreateDragProxy(ptb->ci.hwnd, ToolbarDragCallback, FALSE);
  2814.             if (ptb->hDragProxy)
  2815.                 return (LRESULT)GetDragProxyTarget(ptb->hDragProxy, (IDropTarget **)lParam);
  2816.         }
  2817.         return E_FAIL;
  2818.     case WM_GETFONT:
  2819.         return (LRESULT)(UINT)(ptb? ptb->hfontIcon : 0);
  2820.     case TB_LOADIMAGES:
  2821.         return TBLoadImages(ptb, wParam, (HINSTANCE)lParam);
  2822.     case TB_GETTOOLTIPS:
  2823.         return (LRESULT)(UINT)ptb->hwndToolTips;
  2824.     
  2825.     case TB_SETTOOLTIPS:
  2826.         ptb->hwndToolTips = (HWND)wParam;
  2827.         break;
  2828.     case TB_SETPARENT:
  2829.         {
  2830.             HWND hwndOld = ptb->ci.hwndParent;
  2831.         ptb->ci.hwndParent = (HWND)wParam;
  2832.         return (LRESULT)(UINT)hwndOld;
  2833.         }
  2834.         
  2835. #ifdef UNICODE
  2836.     case TB_GETBUTTONINFOA:
  2837.         return TB_OnGetButtonInfoA(ptb, (int)wParam, (LPTBBUTTONINFOA)lParam);
  2838.         
  2839.     case TB_SETBUTTONINFOA:
  2840.         return TB_OnSetButtonInfoA(ptb, (int)wParam, (LPTBBUTTONINFOA)lParam);
  2841. #endif
  2842.     case TB_GETBUTTONINFO:
  2843.         return TB_OnGetButtonInfo(ptb, (int)wParam, (LPTBBUTTONINFO)lParam);
  2844.         
  2845.     case TB_SETBUTTONINFO:
  2846.         return TB_OnSetButtonInfo(ptb, (int)wParam, (LPTBBUTTONINFO)lParam);
  2847.     case TB_CHANGEBITMAP:
  2848.         iPos = PositionFromID(ptb, (int)wParam);
  2849.         if (iPos < 0)
  2850.             return(FALSE);
  2851.         //
  2852.         // Check to see if the new bitmap ID is
  2853.         // valid.
  2854.         //
  2855.         ptbButton = &ptb->Buttons[iPos];
  2856.         return TB_OnSetImage(ptb, ptbButton, LOWORD(lParam));
  2857.     case TB_GETBITMAP:
  2858.         iPos = PositionFromID(ptb, (int)wParam);
  2859.         if (iPos < 0)
  2860.             return(FALSE);
  2861.         ptbButton = &ptb->Buttons[iPos];
  2862.         return ptbButton->iBitmap;
  2863. #ifdef UNICODE
  2864.     case TB_GETBUTTONTEXTA:
  2865.         iPos = PositionFromID(ptb, (int)wParam);
  2866.         if (iPos >= 0) {
  2867.             LPTSTR psz;
  2868.             
  2869.             ptbButton = &ptb->Buttons[iPos];
  2870.             psz = TB_StrForButton(ptb, ptbButton);
  2871.             if (psz) {
  2872.                 if (lParam) {
  2873.                     WideCharToMultiByte (CP_ACP, 0, psz,
  2874.                                          -1,(LPSTR)lParam , INT_MAX, NULL, NULL);
  2875.                 }
  2876.                 return lstrlen(psz);
  2877.             }
  2878.         }
  2879.         return -1;
  2880. #endif
  2881.     case TB_GETBUTTONTEXT:
  2882.         iPos = PositionFromID(ptb, (int)wParam);
  2883.         if (iPos >= 0) {
  2884.             LPCTSTR psz;
  2885.             
  2886.             ptbButton = &ptb->Buttons[iPos];
  2887.             psz = TB_StrForButton(ptb, ptbButton);
  2888.             if (psz) {
  2889.                 if (lParam) {
  2890.                     lstrcpy((LPTSTR)lParam, psz);
  2891.                 }
  2892.                 return lstrlen(psz);
  2893.             }
  2894.         }
  2895.         return -1;
  2896. #ifdef WIN32
  2897.     case TB_GETBITMAPFLAGS:
  2898.         {
  2899.         DWORD fFlags = 0;
  2900.         HDC hdc = GetDC(NULL);
  2901.         if (GetDeviceCaps(hdc, LOGPIXELSY) >= 120)
  2902.             fFlags |= TBBF_LARGE;
  2903.         ReleaseDC(NULL, hdc);
  2904.         return fFlags;
  2905.         }
  2906. #endif
  2907.     case TB_SETINDENT:
  2908.         ptb->xFirstButton = wParam;
  2909.         InvalidateRect (hwnd, NULL, TRUE);
  2910.         FlushToolTipsMgr(ptb);
  2911.         return 1;
  2912.     case TB_SETMAXTEXTROWS:
  2913.         ptb->nTextRows = wParam;
  2914.         TBRecalc(ptb);
  2915.         return 1;
  2916.     case TB_GETTEXTROWS:
  2917.         return ptb->nTextRows;
  2918.     case TB_HITTEST:
  2919.         return TBHitTest(ptb, ((LPPOINT)lParam)->x, ((LPPOINT)lParam)->y);
  2920.     default:
  2921. DoDefault:
  2922.         return DefWindowProc(hwnd, wMsg, wParam, lParam);
  2923.     }
  2924.     return 0L;
  2925. }