toolbar.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 223k
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. #include "apithk.h"
  12. #define __IOleControl_INTERFACE_DEFINED__       // There is a conflich with the IOleControl's def of CONTROLINFO
  13. #include "shlobj.h"
  14. #ifdef MAINWIN
  15. #include <mainwin.h>
  16. void TBSetHotItemWithoutNotification(PTBSTATE ptb, int iPos, DWORD dwReason);
  17. extern void  TruncateString(char *sz, int cch);
  18. #endif
  19. #define TBP_ONRELEASECAPTURE (WM_USER + 0x500)
  20. #define TBIMAGELIST
  21. // these values are defined by the UI gods...
  22. #define DEFAULTBITMAPX 16
  23. #define DEFAULTBITMAPY 15
  24. #define LIST_GAP        (g_cxEdge * 2)
  25. #define DROPDOWN_GAP    (g_cxEdge * 2)
  26. #define CX_TOP_FUDGE    (g_cxEdge * 2)
  27. #define SMALL_DXYBITMAP     16      // new dx dy for sdt images
  28. #define LARGE_DXYBITMAP     24
  29. #define DEFAULTBUTTONX      24
  30. #define DEFAULTBUTTONY      22
  31. // the insert mark is 6 pixels high/wide depending on horizontal or vertical mode...
  32. #define INSERTMARKSIZE      6
  33. const int g_dxButtonSep = 8;
  34. const int s_xFirstButton = 0;   // was 8 in 3.1
  35. #define s_dxOverlap 0           // was 1 in 3.1
  36. #define USE_MIXED_BUTTONS(ptb) (((ptb)->dwStyleEx & TBSTYLE_EX_MIXEDBUTTONS) && ((ptb)->ci.style & TBSTYLE_LIST))
  37. #define BTN_NO_SHOW_TEXT(ptb, ptbb) (!(ptb)->nTextRows || (USE_MIXED_BUTTONS(ptb) && !((ptbb)->fsStyle & BTNS_SHOWTEXT)))
  38. #define BTN_IS_AUTOSIZE(ptb, ptbb) (((ptbb)->fsStyle & BTNS_AUTOSIZE) || (USE_MIXED_BUTTONS(ptb) && !((ptbb)->fsStyle & BTNS_SEP)))
  39. #define DRAW_MONO_BTN(ptb, state)   (!(state & TBSTATE_ENABLED) || ((ptb->ci.style & WS_DISABLED) && ptb->ci.iVersion >= 5))
  40. // Globals - since all of these globals are used durring a paint we have to
  41. // take a criticial section around all toolbar paints.  this sucks.
  42. //
  43. const UINT wStateMasks[] = {
  44.     TBSTATE_ENABLED,
  45.     TBSTATE_CHECKED,
  46.     TBSTATE_PRESSED,
  47.     TBSTATE_HIDDEN,
  48.     TBSTATE_INDETERMINATE,
  49.     TBSTATE_MARKED
  50. };
  51. #define TBISSTRINGPTR(iString)  (((iString) != -1) && (!IS_INTRESOURCE(iString)))
  52. #define TBDraw_State(ptbdraw)   ((ptbdraw)->tbcd.nmcd.uItemState)
  53. LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
  54. void TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize);
  55. BOOL SetBitmapSize(PTBSTATE ptb, int width, int height);
  56. int  AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT_PTR wBMID);
  57. void TBBuildImageList(PTBSTATE ptb);
  58. BOOL GetInsertMarkRect(PTBSTATE ptb, LPRECT lpRect, BOOL fHorizMode);
  59. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTONDATA pTBButton);
  60. UINT TBGetDrawTextFlags(PTBSTATE ptb, UINT uiStyle, LPTBBUTTONDATA);
  61. BOOL TBGetMaxSize( PTBSTATE ptb, LPSIZE lpsize );
  62. void TBGetItem(PTBSTATE ptb,LPTBBUTTONDATA ptButton, LPNMTBDISPINFO ptbdi);
  63. #define GT_INSIDE       0x0001
  64. #define GT_MASKONLY     0x0002
  65. BOOL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, UINT flags);
  66. //Pager Control Functions
  67. LRESULT TB_OnScroll(PTBSTATE ptb, LPNMHDR pnm);
  68. LRESULT TB_OnPagerControlNotify(PTBSTATE ptb,LPNMHDR pnm);
  69. void TBAutoSize(PTBSTATE ptb);
  70. LRESULT TB_OnCalcSize(PTBSTATE ptb, LPNMHDR pnm);
  71. #define TBInvalidateImageList(ptb)  ((ptb)->fHimlValid = FALSE)
  72. #define TBHasStrings(ptb)  ((ptb)->nStrings || (ptb)->fNoStringPool)
  73. #ifdef DEBUG
  74. #if 0
  75. void _InvalidateRect(HWND hwnd, LPRECT prc, BOOL fInval)
  76. {
  77.     if (!(GetAsyncKeyState(VK_SHIFT) < 0) )
  78.         InvalidateRect(hwnd, prc, fInval);
  79. }
  80. void _RedrawWindow(HWND hwnd, LPRECT prc, HANDLE hrgn, UINT uFlags)
  81. {
  82.     if (!(GetAsyncKeyState(VK_SHIFT) < 0) )
  83.         RedrawWindow(hwnd, prc, hrgn, uFlags);
  84. }
  85. void _SetWindowPos(HWND hwnd, HWND hwnd2, int x, int y, int cx, int cy, UINT uFlags)
  86. {
  87.     if (GetAsyncKeyState(VK_SHIFT) < 0)
  88.         uFlags &= ~( SWP_FRAMECHANGED);
  89.     SetWindowPos(hwnd, hwnd2, x, y, cx, cy, uFlags);
  90. }
  91. #define InvalidateRect(hwnd, prc, fInval) _InvalidateRect(hwnd, prc, fInval)
  92. #define RedrawWindow(hwnd, prc, hrgn, uFlags) _RedrawWindow(hwnd, prc, hrgn, uFlags)
  93. #define SetWindowPos(hwnd, hwnd2, x, y, cx, cy, uFlags) _SetWindowPos(hwnd, hwnd2, x, y, cx, cy, uFlags)
  94. #endif
  95. #endif
  96. __inline BOOL TB_IsDropDown(LPTBBUTTONDATA ptbb)
  97. {
  98.     BOOL fRet = (ptbb->fsStyle & (BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN));
  99.     return fRet;
  100. }
  101. __inline BOOL TB_HasDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  102. {
  103.     BOOL fRet = (((ptb->dwStyleEx & TBSTYLE_EX_DRAWDDARROWS) &&
  104.                         (ptbb->fsStyle & BTNS_DROPDOWN)) ||
  105.                   (ptbb->fsStyle & BTNS_WHOLEDROPDOWN));
  106.     return fRet;
  107. }
  108. __inline BOOL TB_HasSplitDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  109. {
  110.     // If the button is both BTNS_DROPDOWN and BTNS_WHOLEDROPDOWN,
  111.     // BTNS_WHOLEDROPDOWN wins.
  112.     BOOL fRet = ((ptb->dwStyleEx & TBSTYLE_EX_DRAWDDARROWS) &&
  113.                 (ptbb->fsStyle & BTNS_DROPDOWN) &&
  114.                 !(ptbb->fsStyle & BTNS_WHOLEDROPDOWN));
  115.     return fRet;
  116. }
  117. __inline BOOL TB_HasUnsplitDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  118. {
  119.     BOOL fRet = (ptbb->fsStyle & BTNS_WHOLEDROPDOWN);
  120.     return fRet;
  121. }
  122. __inline BOOL TB_HasTopDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  123. {
  124.     BOOL fRet = (!(ptb->ci.style & TBSTYLE_LIST) &&
  125.                 TB_HasUnsplitDDArrow(ptb, ptbb) &&
  126.                 (ptb->nTextRows > 0) && TB_StrForButton(ptb, ptbb));
  127.     return fRet;
  128. }
  129. BOOL TBIsHotTrack(PTBSTATE ptb, LPTBBUTTONDATA ptButton, UINT state)
  130. {
  131.     BOOL fHotTrack = FALSE;
  132.     if ((ptb->ci.style & TBSTYLE_FLAT) && (&ptb->Buttons[ptb->iHot]==ptButton))
  133.         fHotTrack = TRUE;
  134.     // The following is in place to prevent hot tracking during the following conds:
  135.     //  - drag & drop toolbar customization
  136.     //  - when the mouse capture is on a particular button-press.
  137.     // This does _not_ drop out of the loop because we don't want to break update
  138.     // behavior; thus we'll have a little flickering on refresh as we pass over
  139.     // these buttons.
  140.     if (!(state & TBSTATE_PRESSED) && (GetKeyState (VK_LBUTTON) < 0) &&
  141.         GetCapture() == ptb->ci.hwnd)
  142.     {
  143.         fHotTrack = FALSE;
  144.     }
  145.     if (!fHotTrack && (ptb->iPressedDD == ptButton - ptb->Buttons))
  146.         fHotTrack = TRUE;
  147.     return fHotTrack;
  148. }
  149. UINT StateFromCDIS(UINT uItemState)
  150. {
  151.     UINT state = 0;
  152.     if (uItemState & CDIS_CHECKED)
  153.         state |= TBSTATE_CHECKED;
  154.     if (uItemState & CDIS_SELECTED)
  155.         state |= TBSTATE_PRESSED;
  156.     if (!(uItemState & CDIS_DISABLED))
  157.         state |= TBSTATE_ENABLED;
  158.     if (uItemState & CDIS_MARKED)
  159.         state |= TBSTATE_MARKED;
  160.     if (uItemState & CDIS_INDETERMINATE)
  161.         state |= TBSTATE_INDETERMINATE;
  162.     return state;
  163. }
  164. UINT CDISFromState(UINT state)
  165. {
  166.     UINT uItemState = 0;
  167.     // Here are the TBSTATE - to - CDIS mappings:
  168.     //
  169.     //  TBSTATE_CHECKED         = CDIS_CHECKED
  170.     //  TBSTATE_PRESSED         = CDIS_SELECTED
  171.     // !TBSTATE_ENABLED         = CDIS_DISABLED
  172.     //  TBSTATE_MARKED          = CDIS_MARKED
  173.     //  TBSTATE_INDETERMINATE   = CDIS_INDETERMINATE
  174.     //
  175.     //  Hot tracked item        = CDIS_HOT
  176.     //
  177.     if (state & TBSTATE_CHECKED)
  178.         uItemState |= CDIS_CHECKED;
  179.     if (state & TBSTATE_PRESSED)
  180.         uItemState |= CDIS_SELECTED;
  181.     if (!(state & TBSTATE_ENABLED))
  182.         uItemState |= CDIS_DISABLED;
  183.     if (state & TBSTATE_MARKED)
  184.         uItemState |= CDIS_MARKED;
  185.     if (state & TBSTATE_INDETERMINATE)
  186.         uItemState |= CDIS_INDETERMINATE;
  187.     return uItemState;
  188. }
  189. void FlushToolTipsMgrNow(PTBSTATE ptb);
  190. void TB_ForceCreateTooltips(PTBSTATE ptb)
  191. {
  192.     if (ptb->ci.style & TBSTYLE_TOOLTIPS && !ptb->hwndToolTips)
  193.     {
  194.         TOOLINFO ti;
  195.         // don't bother setting the rect because we'll do it below
  196.         // in TBInvalidateItemRects;
  197.         ti.cbSize = sizeof(ti);
  198.         ti.uFlags = TTF_IDISHWND;
  199.         ti.hwnd = ptb->ci.hwnd;
  200.         ti.uId = (UINT_PTR)ptb->ci.hwnd;
  201.         ti.lpszText = 0;
  202. #ifndef UNIX
  203.         ptb->hwndToolTips = CreateWindow(c_szSToolTipsClass, NULL,
  204.                                          WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  205.                                          ptb->ci.hwnd, NULL, HINST_THISDLL, NULL);
  206. #else
  207.         ptb->hwndToolTips = CreateWindowEx( WS_EX_MW_UNMANAGED_WINDOW, c_szSToolTipsClass, NULL,
  208.                                          WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  209.                                          ptb->ci.hwnd, NULL, HINST_THISDLL, NULL);
  210. #endif
  211.         if (ptb->hwndToolTips) {
  212.             int i;
  213.             NMTOOLTIPSCREATED nm;
  214.             CCSetInfoTipWidth(ptb->ci.hwnd, ptb->hwndToolTips);
  215.             SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  216.                         (LPARAM)(LPTOOLINFO)&ti);
  217.             nm.hwndToolTips = ptb->hwndToolTips;
  218.             CCSendNotify(&ptb->ci, NM_TOOLTIPSCREATED, &nm.hdr);
  219.             // don't bother setting the rect because we'll do it below
  220.             // in TBInvalidateItemRects;
  221.             ti.uFlags = 0;
  222.             ti.lpszText = LPSTR_TEXTCALLBACK;
  223.             for (i = 0; i < ptb->iNumButtons; i++) {
  224.                 if (!(ptb->Buttons[i].fsStyle & BTNS_SEP)) {
  225.                     ti.uId = ptb->Buttons[i].idCommand;
  226.                     SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  227.                                 (LPARAM)(LPTOOLINFO)&ti);
  228.                 }
  229.             }
  230.             FlushToolTipsMgrNow(ptb);
  231.         }
  232.     }
  233. }
  234. void TBRelayToToolTips(PTBSTATE ptb, UINT wMsg, WPARAM wParam, LPARAM lParam)
  235. {
  236.     TB_ForceCreateTooltips(ptb);
  237.     if (ptb->hwndToolTips) {
  238.         RelayToToolTips(ptb->hwndToolTips, ptb->ci.hwnd, wMsg, wParam, lParam);
  239.     }
  240. }
  241. LRESULT ToolbarDragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
  242. {
  243.     PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
  244.     LRESULT lres;
  245.     switch (code)
  246.     {
  247.     case DPX_DRAGHIT:
  248.         if (lp)
  249.         {
  250.             POINT pt;
  251.             int item;
  252.             pt.x = ((POINTL *)lp)->x;
  253.             pt.y = ((POINTL *)lp)->y;
  254.             MapWindowPoints(NULL, ptb->ci.hwnd, &pt, 1);
  255.             item = TBHitTest(ptb, pt.x, pt.y);
  256.             if (0 <= item && item < ptb->iNumButtons)
  257.                 lres = (LRESULT)ptb->Buttons[item].idCommand;
  258.             else
  259.                 lres = (LRESULT)-1;
  260.         }
  261.         else
  262.             lres = -1;
  263.         break;
  264.     case DPX_GETOBJECT:
  265.         lres = (LRESULT)GetItemObject(&ptb->ci, TBN_GETOBJECT, &IID_IDropTarget, (LPNMOBJECTNOTIFY)lp);
  266.         break;
  267.     case DPX_SELECT:
  268.         if ((int)wp >= 0)
  269.         {
  270.             NMTBHOTITEM nmhi;
  271.             nmhi.idNew = (int) wp;
  272.             if (!CCSendNotify(&ptb->ci, TBN_DRAGOVER, &nmhi.hdr))
  273.             {
  274.                 SendMessage(ptb->ci.hwnd, TB_MARKBUTTON, wp,
  275.                     MAKELPARAM((lp != DROPEFFECT_NONE), 0));
  276.             }
  277.         }
  278.         lres = 0;
  279.         break;
  280.     default:
  281.         lres = -1;
  282.         break;
  283.     }
  284.     return lres;
  285. }
  286. int TBMixedButtonHeight(PTBSTATE ptb, int iIndex)
  287. {
  288.     int iHeight;
  289.     LPTBBUTTONDATA ptbb = &(ptb->Buttons[iIndex]);
  290.     if (ptbb->fsStyle & BTNS_SHOWTEXT)                      // text and icon
  291.         iHeight = max(ptb->iDyBitmap, ptb->dyIconFont);
  292.     else                                                    // icon, no text
  293.         iHeight = ptb->iDyBitmap;
  294.     return iHeight;
  295. }
  296. int TBMixedButtonsHeight(PTBSTATE ptb)
  297. {
  298.     int i;
  299.     int iHeightMax = 0;
  300.     int iHeight;
  301.     ASSERT(ptb->ci.style & TBSTYLE_LIST);
  302.     ASSERT(USE_MIXED_BUTTONS(ptb));
  303.     for (i = 0; i < ptb->iNumButtons; i++) {
  304.         iHeight = TBMixedButtonHeight(ptb, i);
  305.         iHeightMax = max(iHeightMax, iHeight);
  306.     }
  307.     return iHeightMax;
  308. }
  309. int HeightWithString(PTBSTATE ptb, int h)
  310. {
  311.     if (USE_MIXED_BUTTONS(ptb))
  312.     {
  313.         int hMixed = TBMixedButtonsHeight(ptb);
  314.         return (max(h, hMixed));
  315.     }
  316.     else if (ptb->ci.style & TBSTYLE_LIST)
  317.         return (max(h, ptb->dyIconFont));
  318.     else if (ptb->dyIconFont)
  319.         return (h + ptb->dyIconFont + 1);
  320.     else
  321.         return (h);
  322. }
  323. int TBGetSepHeight(PTBSTATE ptb, LPTBBUTTONDATA pbtn)
  324. {
  325.     ASSERT(pbtn->fsStyle & BTNS_SEP);
  326.     if (ptb->ci.style & (CCS_VERT | TBSTYLE_FLAT) )
  327.         return pbtn->DUMMYUNION_MEMBER(cxySep);
  328.     else
  329.         return pbtn->DUMMYUNION_MEMBER(cxySep) * 2 / 3;
  330. }
  331. UINT TBWidthOfString(PTBSTATE ptb, LPTBBUTTONDATA ptbb, HDC hdc)
  332. {
  333.     UINT uiWidth = 0;
  334.     LPTSTR pstr = TB_StrForButton(ptb, ptbb);
  335.     if (pstr)
  336.     {
  337.         HDC hdcCreated = NULL;
  338.         HFONT hOldFont;
  339.         UINT uiStyle;
  340.         RECT rcText = {0,0,1000,10};
  341.         if (!hdc)
  342.         {
  343.             hdcCreated = GetDC(ptb->ci.hwnd);
  344.             hdc = hdcCreated;
  345.         }
  346.         hOldFont = SelectObject(hdc, ptb->hfontIcon);
  347.         uiStyle = DT_CALCRECT | TBGetDrawTextFlags(ptb, 0, ptbb);
  348.         DrawText(hdc, pstr, -1, &rcText, uiStyle);
  349.         uiWidth += rcText.right;
  350.         SelectObject(hdc, hOldFont);
  351.         if (hdcCreated)
  352.             ReleaseDC(ptb->ci.hwnd, hdcCreated);
  353.     }
  354.     return uiWidth;
  355. }
  356. // TBDDArrowAdjustment(ptb, ptbb): the amount by which we change the width of
  357. // this button to accomodate the drop-down arrow.  not necessarily the same as
  358. // ptb->dxDDArrowChar.
  359. int TBDDArrowAdjustment(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  360. {
  361.     int iAdjust = 0;
  362.     if (TB_HasDDArrow(ptb, ptbb))
  363.     {
  364.         // If a whole dd, non-autosize button, then we'll just use the standard
  365.         // button width which ought to have room for this button (i.e., return 0).
  366.         if (!TB_HasTopDDArrow(ptb, ptbb) || BTN_IS_AUTOSIZE(ptb, ptbb))
  367.         {
  368.             iAdjust += (WORD)ptb->dxDDArrowChar;
  369.             if (TB_HasUnsplitDDArrow(ptb, ptbb))
  370.             {
  371.                 // subtract off a bit since there won't be a border
  372.                 // around dd arrow part of this button
  373.                 iAdjust -= 2 * g_cxEdge;
  374.                 if (ptbb->iBitmap != I_IMAGENONE)
  375.                 {
  376.                     // nudge over a bit more to overlap bitmap border padding
  377.                     iAdjust -= g_cxEdge;
  378.                 }
  379.             }
  380.             if (TB_HasTopDDArrow(ptb, ptbb))
  381.             {
  382.                 // If string width >= icon width + iAdjust, then no need
  383.                 // to add extra space for the arrow.
  384.                 if ((int)TBWidthOfString(ptb, ptbb, NULL) >= ptb->iDxBitmap + iAdjust)
  385.                     iAdjust = 0;
  386.             }
  387.         }
  388.     }
  389.     return max(iAdjust, 0);
  390. }
  391. int TBWidthOfButton(PTBSTATE ptb, LPTBBUTTONDATA pButton, HDC hdc)
  392. {
  393.     RECT rc;
  394.     if (BTN_IS_AUTOSIZE(ptb, pButton)) {
  395.         // if they've set this button for autosize, calculate it and cache
  396.         // it in cx
  397.         if (BTN_NO_SHOW_TEXT(ptb, pButton)) {
  398.             pButton->cx = 0;
  399.             goto CalcIconWidth;
  400.         }
  401.         if (pButton->cx == 0) {
  402.             UINT uiStringWidth = TBWidthOfString(ptb, pButton, hdc);
  403.             pButton->cx = (WORD) ptb->xPad + uiStringWidth;
  404.             if (uiStringWidth) {
  405.                 // Since we have a string for this button, we need to add
  406.                 // some padding around it.
  407.                 if ((ptb->ci.style & TBSTYLE_LIST) && TB_HasSplitDDArrow(ptb, pButton))
  408.                     pButton->cx += (WORD) ptb->iDropDownGap;
  409.                 else
  410.                     pButton->cx += 2 * g_cxEdge;
  411.             }
  412. CalcIconWidth:
  413.             if (pButton->iBitmap != I_IMAGENONE) {
  414.                 if (ptb->ci.style & TBSTYLE_LIST) {
  415.                     pButton->cx += ptb->iDxBitmap + ptb->iListGap;
  416.                     if (BTN_NO_SHOW_TEXT(ptb, pButton))
  417.                         pButton->cx += g_cxEdge * 2;
  418.                 }
  419.                 else {
  420.                     // Use wider of string width (pButton->cx so far) and bitmap width.
  421.                     pButton->cx = max(pButton->cx, ptb->iDxBitmap + ptb->xPad);
  422.                 }
  423.             }
  424.             pButton->cx += (USHORT)TBDDArrowAdjustment(ptb, pButton);
  425.         }
  426.     }
  427.     if (pButton->cx) {
  428.         return (int)pButton->cx;
  429.     } else if (pButton->fsStyle & BTNS_SEP) {
  430.         if (ptb->ci.style & CCS_VERT) {
  431.             GetWindowRect(ptb->ci.hwnd, &rc);
  432.             return RECTWIDTH(rc);
  433.         } else {
  434.             // Compat: Corel (Font navigator) expects the separators to be
  435.             // 8 pixels wide.  So do not return pButton->cxySep here, since
  436.             // that can be calculated differently depending on the flat style.
  437.             //
  438.             // No.  owner draw items are added by specifying separator, and
  439.             // the iBitmap width which is then copied down to cxySep.
  440.             // the preserving of size for corel needs to be done at that point.
  441.             return pButton->DUMMYUNION_MEMBER(cxySep);
  442.         }
  443.     } else if (!(TBSTYLE_EX_VERTICAL & ptb->dwStyleEx)) {
  444.         return ptb->iButWidth + TBDDArrowAdjustment(ptb, pButton);
  445.     } else {
  446.         return ptb->iButWidth;
  447.     }
  448. }
  449. UINT TBGetDrawTextFlags(PTBSTATE ptb, UINT uiStyle, TBBUTTONDATA* ptbb)
  450. {
  451.     if (ptb->nTextRows > 1)
  452.         uiStyle |= DT_WORDBREAK | DT_EDITCONTROL;
  453.     else
  454.         uiStyle |= DT_SINGLELINE;
  455.     if (ptb->ci.style & TBSTYLE_LIST)
  456.     {
  457.         uiStyle |= DT_LEFT | DT_VCENTER | DT_SINGLELINE;
  458.     }
  459.     else
  460.     {
  461.         uiStyle |= DT_CENTER;
  462.     }
  463.     uiStyle &= ~(ptb->uDrawTextMask);
  464.     uiStyle |= ptb->uDrawText;
  465.     if (ptbb->fsStyle & BTNS_NOPREFIX)
  466.         uiStyle |= DT_NOPREFIX;
  467. #ifndef KEYBOARDCUES
  468.     // This flag tells User's DrawText/Ex NOT to show the prefixes at all
  469.     // when rendering. This only works on NT5.
  470.     if (!ptb->fShowPrefix && g_bRunOnNT5)
  471. #else
  472.     if (CCGetUIState(&(ptb->ci)) & UISF_HIDEACCEL)
  473. #endif
  474.     {
  475.         uiStyle |= DT_HIDEPREFIX;
  476.     }
  477.     return uiStyle;
  478. }
  479. BOOL TBRecalc(PTBSTATE ptb)
  480. {
  481.     TEXTMETRIC tm = {0};
  482.     int i;
  483.     HDC hdc;
  484.     int cxMax = 0, cxMask, cy;
  485.     HFONT hOldFont=NULL;
  486.     if (ptb->fRedrawOff) {
  487.         // redraw is off; defer recalc until redraw is turned back on
  488.         ptb->fRecalc = TRUE;
  489.         return TRUE;    // The recalc "succeeded" - actual work will happen later
  490.     }
  491.     ptb->dyIconFont = 0;
  492.     if (!TBHasStrings(ptb) || !ptb->nTextRows ) {
  493.         cxMax = ptb->iDxBitmap;
  494.         cxMask = cxMax;
  495.     } else {
  496.         SIZE size = {0};
  497.         LPCTSTR pstr;
  498.         RECT rcText = {0,0,0,0};
  499.         int cxExtra = ptb->xPad;
  500.         ptb->iButWidth = 0;
  501.         hdc = GetDC(ptb->ci.hwnd);
  502.         if (!hdc)
  503.             return(FALSE);
  504.         if (ptb->hfontIcon)
  505.             hOldFont = SelectObject(hdc, ptb->hfontIcon);
  506.         GetTextMetrics(hdc, &tm);
  507.         if (ptb->nTextRows)
  508.             ptb->dyIconFont = (tm.tmHeight * ptb->nTextRows) +
  509.                 (tm.tmExternalLeading * (ptb->nTextRows - 1)); // add an edge ?
  510.         if (ptb->ci.style & TBSTYLE_LIST)
  511.             cxExtra += ptb->iDxBitmap + ptb->iListGap;
  512.         // default to the image size...
  513.         cxMax = ptb->iDxBitmap;
  514.         // walk strings to find max width
  515.         for (i = 0; i < ptb->iNumButtons; i++)
  516.         {
  517.             if (ptb->Buttons[i].fsState & TBSTATE_HIDDEN)
  518.                 continue;
  519.             if (BTN_IS_AUTOSIZE(ptb, &ptb->Buttons[i]))
  520.                 ptb->Buttons[i].cx = 0;
  521.             pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
  522.             if (pstr) 
  523.             {
  524.                 if ( ptb->ci.iVersion < 5 )
  525.                 {
  526.                     // we used to use GetTextExtentPoint instead of DrawText.  This function would include the width
  527.                     // of the "&" character if it was present.  As a result, it returned larger values and thus created
  528.                     // wider buttons.  Without this extra fudge certain buttons will be about 6 pixels too narrow.
  529.                     GetTextExtentPoint(hdc, pstr, lstrlen(pstr), &size);
  530.                 }
  531.                 else
  532.                 {
  533.                     // wordbreak is not allowed in the calcrect w/ singleline
  534.                     UINT uiStyle = DT_CALCRECT | DT_SINGLELINE | (TBGetDrawTextFlags(ptb, 0, &ptb->Buttons[i]) & ~DT_WORDBREAK);
  535.                     RECT rcTemp = {0,0,0,0};
  536.                     rcTemp.bottom = ptb->dyIconFont;
  537.                     DrawText(hdc, pstr, -1, &rcTemp, uiStyle);
  538.                     size.cx = RECTWIDTH(rcTemp);
  539.                     size.cy = RECTHEIGHT(rcTemp);
  540.                     // BUGBUG: size.cy stuff is fishy -- last one wins
  541.                 }
  542.             }
  543.             else
  544.             {
  545.                 size.cx = 0;
  546.             }
  547.             if (TB_HasTopDDArrow(ptb, &ptb->Buttons[i])) {
  548.                 int iBmpWithArrow = CX_TOP_FUDGE + ptb->iDxBitmap + ptb->dxDDArrowChar;
  549.                 size.cx = max(size.cx, iBmpWithArrow);
  550.             }
  551.             else if ((ptb->dwStyleEx & TBSTYLE_EX_VERTICAL) && 
  552.                 TB_HasDDArrow(ptb, &ptb->Buttons[i])) {
  553.                 // for vertical toolbars, buttons with drop-down arrows
  554.                 // are drawn with the same width as normal buttons, so
  555.                 // we need to figure them into our max width calculation.
  556.                 size.cx += ptb->dxDDArrowChar;
  557.             }
  558.             if (cxMax < size.cx)
  559.                 cxMax = size.cx;
  560.         }
  561.         // if cxMax is less than the iButMinWidth - dxBitmap (if LIST) then
  562.         // cxMax = iButMinWidth
  563.         if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
  564.             cxMax = ptb->iButMinWidth - cxExtra;
  565.         cxMask = cxMax;
  566.         // Is the cxMax +  dxBitmap (if LIST) more than the max width ?
  567.         if (ptb->iButMaxWidth && (ptb->iButMaxWidth < (cxMax + cxExtra)))
  568.         {
  569.             int cyMax = 0;
  570.             int cxTemp = 0;
  571.             cxMax = ptb->iButMaxWidth - cxExtra;
  572.             // But leave cxMask at its old value since AUTOSIZE buttons
  573.             // are exempt from button truncation.  This exemption is a bug,
  574.             // but IE4 shipped that way so we're stuck with it.  (You can
  575.             // tell it's a bug because we go ahead and flip TBSTATE_ELLIPSIS
  576.             // even on AUTOSIZE buttons, only to "forget" about the ellipsis
  577.             // in TBWidthOfString().)
  578.             // walk strings to set the TBSTATE_ELLIPSES
  579.             for (i = 0; i < ptb->iNumButtons; i++)
  580.             {
  581.                 BOOL fEllipsed = FALSE;
  582.                 UINT uiStyle;
  583.                 if (ptb->Buttons[i].fsState & TBSTATE_HIDDEN)
  584.                     continue;
  585.                 if (BTN_NO_SHOW_TEXT(ptb, &ptb->Buttons[i]))
  586.                     pstr = NULL;
  587.                 else
  588.                 {
  589.                     pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
  590.                     uiStyle = DT_CALCRECT | TBGetDrawTextFlags(ptb, 0, &ptb->Buttons[i]);
  591.                 }
  592.                 if (pstr) 
  593.                 {
  594.                     int cxMaxText;
  595.                     if ((ptb->dwStyleEx & TBSTYLE_EX_VERTICAL) && 
  596.                         TB_HasDDArrow(ptb, &ptb->Buttons[i]))
  597.                     {
  598.                         // if a drop-down button on a vertical toolbar,
  599.                         // need to make space for drop-down arrow
  600.                         cxMaxText = cxMax - ptb->dxDDArrowChar;
  601.                     } 
  602.                     else 
  603.                     {
  604.                         cxMaxText = cxMax;
  605.                     }
  606.                     // DrawText doesn't like it when cxMaxText <= 0
  607.                     cxMaxText = max(cxMaxText, 1);
  608.                     rcText.bottom = ptb->dyIconFont;
  609.                     rcText.right = cxMaxText;
  610.                     DrawText(hdc, pstr, -1, &rcText, uiStyle);
  611.                     if (ptb->nTextRows > 1)
  612.                     {
  613.                         // width is width of text plus width we might
  614.                         // have lopped off for drop-down arrow
  615.                         int cx = rcText.right + (cxMax - cxMaxText);
  616.                         if (cx > cxTemp)
  617.                         {
  618.                             // this is our new multiline text hack max
  619.                             cxTemp = cx;
  620.                         }
  621.                         fEllipsed = (BOOL)(rcText.bottom > ptb->dyIconFont);
  622.                     }
  623.                     else
  624.                         fEllipsed = (BOOL)(rcText.right > cxMaxText);
  625.                     if (cyMax < rcText.bottom)
  626.                         cyMax = rcText.bottom;
  627.                 }
  628.                 if (fEllipsed)
  629.                     ptb->Buttons[i].fsState |= TBSTATE_ELLIPSES;
  630.                 else
  631.                     ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
  632.             }
  633.             if (cxTemp && (ptb->nTextRows > 1 ))
  634.                 cxMax = cxTemp;
  635.             // Set the text height to the tallest text, with the top end being the number
  636.             // of rows specified by MAXTEXTROWS
  637.             if (ptb->dyIconFont > cyMax)
  638.                 ptb->dyIconFont = cyMax;
  639.         }
  640.         else
  641.         {
  642.             for (i = 0; i < ptb->iNumButtons; i++)
  643.                 ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
  644.             if ((ptb->nTextRows) && ptb->iNumButtons && (ptb->dyIconFont > size.cy))
  645.                 ptb->dyIconFont = size.cy;
  646.         }
  647.         if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
  648.             cxMax = ptb->iButMinWidth - cxExtra;
  649.         if (hOldFont)
  650.             SelectObject(hdc, hOldFont);
  651.         ReleaseDC(ptb->ci.hwnd, hdc);
  652.     }
  653.     //
  654.     //  Need to call GrowToolbar twice, once to grow the mask, and again
  655.     //  to grow the buttons.  (Yes, this is sick.)
  656.     //
  657.     cy = HeightWithString(ptb, ptb->iDyBitmap);
  658.     if (!GrowToolbar(ptb, max(cxMax, cxMask), cy, GT_INSIDE | GT_MASKONLY))
  659.         return(FALSE);
  660.     return(GrowToolbar(ptb, cxMax, cy, GT_INSIDE));
  661. }
  662. BOOL TBChangeFont(PTBSTATE ptb, WPARAM wParam, HFONT hFont)
  663. {
  664.     LOGFONT lf;
  665.     BOOL fWasFontCreated = ptb->fFontCreated;
  666.     if ((wParam != 0) && (wParam != SPI_SETICONTITLELOGFONT) && (wParam != SPI_SETNONCLIENTMETRICS))
  667.         return(FALSE);
  668.     if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0))
  669.         return(FALSE);
  670.     if (!hFont) {
  671.         if (!(hFont = CreateFontIndirect(&lf)))
  672.             return(FALSE);
  673.         ptb->fFontCreated = TRUE;
  674.     } else {
  675.         ptb->fFontCreated = FALSE;
  676.     }
  677.     if (ptb->hfontIcon && fWasFontCreated)
  678.         DeleteObject(ptb->hfontIcon);
  679.     ptb->hfontIcon = hFont;
  680.     return(TBRecalc(ptb));
  681. }
  682. void TBSetFont(PTBSTATE ptb, HFONT hFont, BOOL fInval)
  683. {
  684.     TBChangeFont(ptb, 0, hFont);
  685.     if (fInval)
  686.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  687. }
  688. HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, UINT wID, int nBitmaps,
  689.             HINSTANCE hBMInst, UINT_PTR wBMID, LPCTBBUTTON lpButtons,
  690.             int iNumButtons, int dxButton, int dyButton,
  691.             int dxBitmap, int dyBitmap, UINT uStructSize)
  692. {
  693.     HWND hwndToolbar = CreateWindow(c_szToolbarClass, NULL, WS_CHILD | ws,
  694.           0, 0, 100, 30, hwnd, (HMENU)wID, HINST_THISDLL, NULL);
  695.     if (hwndToolbar)
  696.     {
  697.         PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwndToolbar, 0);
  698.         TBOnButtonStructSize(ptb, uStructSize);
  699.         if ((dxBitmap && dyBitmap && !SetBitmapSize(ptb, dxBitmap, dyBitmap)) ||
  700.             (dxButton && dyButton && !SetBitmapSize(ptb,dxButton, dyButton)))
  701.         {
  702.             //!!!! do we actually need to deal with this?
  703.             DestroyWindow(hwndToolbar);
  704.             hwndToolbar = NULL;
  705.             goto Error;
  706.         }
  707.         AddBitmap(ptb, nBitmaps, hBMInst, wBMID);
  708.         TBInsertButtons(ptb, (UINT)-1, iNumButtons, (LPTBBUTTON)lpButtons, TRUE);
  709.         // ptb may be bogus now after above button insert
  710.     }
  711. Error:
  712.     return hwndToolbar;
  713. }
  714. /* This is no longer declared in COMMCTRL.H.  It only exists for compatibility
  715. ** with existing apps; new apps must use CreateToolbarEx.
  716. */
  717. HWND WINAPI CreateToolbar(HWND hwnd, DWORD ws, UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT_PTR wBMID, LPCTBBUTTON lpButtons, int iNumButtons)
  718. {
  719.     // old-style toolbar, so no divider.
  720.     return CreateToolbarEx(hwnd, ws | CCS_NODIVIDER, wID, nBitmaps, hBMInst, wBMID,
  721.                 lpButtons, iNumButtons, 0, 0, 0, 0, sizeof(OLDTBBUTTON));
  722. }
  723. #pragma code_seg(CODESEG_INIT)
  724. BOOL InitToolbarClass(HINSTANCE hInstance)
  725. {
  726.     WNDCLASS wc;
  727.     if (!GetClassInfo(hInstance, c_szToolbarClass, &wc))
  728.     {
  729.         wc.lpfnWndProc   = (WNDPROC)ToolbarWndProc;
  730.         wc.lpszClassName = c_szToolbarClass;
  731.         wc.style     = CS_DBLCLKS | CS_GLOBALCLASS;
  732.         wc.cbClsExtra    = 0;
  733.         wc.cbWndExtra    = sizeof(PTBSTATE);
  734.         wc.hInstance     = hInstance;   // use DLL instance if in DLL
  735.         wc.hIcon     = NULL;
  736.         wc.hCursor   = LoadCursor(NULL, IDC_ARROW);
  737.         wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  738.         wc.lpszMenuName  = NULL;
  739.         if (!RegisterClass(&wc))
  740.             return FALSE;
  741.     }
  742.     return TRUE;
  743. }
  744. #pragma code_seg()
  745. void PatB(HDC hdc,int x,int y,int dx,int dy, DWORD rgb)
  746. {
  747.     RECT    rc;
  748.     SetBkColor(hdc,rgb);
  749.     rc.left   = x;
  750.     rc.top    = y;
  751.     rc.right  = x + dx;
  752.     rc.bottom = y + dy;
  753.     ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  754. }
  755. #ifndef UNICODE
  756. // Get actual number of characters that will be drawn into the given
  757. // rectangle by DrawTextEx. This is to avoid using DT_END_ELLIPSIS
  758. // on FarEast Win95 (golden) as it could sometimes put more characters
  759. // than what the rect actually could hold.
  760. UINT GetLengthDrawn(PTBSTATE ptb, HDC hdc, LPSTR psz, int cch, LPRECT lprc, UINT uiStyle)
  761. {
  762.     DRAWTEXTPARAMS dtParams = {0};
  763.     HDC hdcMem;
  764.     HFONT hfontOld;
  765.     ASSERT(psz);
  766.     ASSERT(lprc);
  767.     ASSERT(ptb);
  768.     hdcMem = CreateCompatibleDC(hdc);
  769.     hfontOld=SelectObject(hdcMem, ptb->hfontIcon);
  770.     dtParams.cbSize = sizeof (dtParams);
  771.     DrawTextEx(hdcMem, (LPSTR)psz, cch, lprc, uiStyle, &dtParams);
  772.     SelectObject(hdcMem, hfontOld);
  773.     DeleteDC(hdcMem);
  774.     return dtParams.uiLengthDrawn;
  775. }
  776. #endif
  777. // Parameter fHighlight determines whether to draw text highlighted, for
  778. // new TBSTATE_MARKED
  779. //
  780. void DrawString(HDC hdc, int x, int y, int dx, int dy, PTSTR pszString,
  781.                             BOOL fHighlight, TBDRAWITEM * ptbdraw)
  782. {
  783.     int oldMode;
  784.     COLORREF oldBkColor;
  785.     COLORREF oldTextColor;
  786.     RECT rcText;
  787.     UINT uiStyle = 0;
  788.     PTBSTATE ptb;
  789.     LPTBBUTTONDATA ptbb;
  790.     ASSERT(ptbdraw);
  791.     ptb = ptbdraw->ptb;
  792.     ptbb = ptbdraw->pbutton;
  793.     if (!(ptb->ci.style & TBSTYLE_LIST) && ((ptb->iDyBitmap + ptb->yPad + g_cyEdge) >= ptb->iButHeight))
  794.         // there's no room to show the text -- bail out
  795.         return;
  796.     if (BTN_NO_SHOW_TEXT(ptb, ptbb))
  797.         // don't show text for this button -- bail out
  798.         return;
  799.     if (fHighlight)
  800.     {
  801.         oldMode = SetBkMode (hdc, ptbdraw->tbcd.nHLStringBkMode);
  802.         oldBkColor = SetBkColor (hdc, ptbdraw->tbcd.clrMark);
  803.         oldTextColor = SetTextColor (hdc, ptbdraw->tbcd.clrTextHighlight);
  804.     }
  805.     else
  806.         oldMode = SetBkMode(hdc, ptbdraw->tbcd.nStringBkMode);
  807.     uiStyle = TBGetDrawTextFlags(ptb, DT_END_ELLIPSIS, ptbb);
  808.     // If we're ex_vertical want to center the text
  809.     if (!(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  810.     {
  811.         if (ptb->ci.style & TBSTYLE_LIST)
  812.         {
  813.             dy = max(ptb->dyIconFont, ptb->iDyBitmap);
  814.         }
  815.         else
  816.         {
  817.             if (!dy || ptb->dyIconFont < dy)
  818.                 dy = ptb->dyIconFont;
  819.         }
  820.     }
  821.     SetRect( &rcText, x, y, x + dx, y + dy);
  822. #ifndef UNICODE
  823.     if (g_fDBCSEnabled && !g_bRunOnMemphis
  824.        && (ptbb->fsState & TBSTATE_ELLIPSES)
  825.        && (ptb->nTextRows > 1))
  826.     {
  827.         LPSTR psz;
  828.         UINT uiLengthDrawn;
  829.         // FarEast Win95 has a bug that DT_END_ELLIPSIS can
  830.         // miscalculate the number of characters that fit in
  831.         // the specified rectangle. We have to avoid to use
  832.         // DT_END_ELLIPSIS for the platform putting ellipsis
  833.         // ourselves. Memphis will fix the bug so we won't do
  834.         // this for them.
  835.         //
  836.         uiStyle &= ~DT_END_ELLIPSIS;
  837.         psz = StrDup(pszString);
  838.         if (psz)
  839.         {
  840.             uiLengthDrawn = GetLengthDrawn(ptb, hdc, psz, -1, &rcText, uiStyle);
  841.             if (uiLengthDrawn > 3)
  842.             {
  843.                 TruncateString(psz, uiLengthDrawn-2);
  844.                 lstrcat(psz, "...");
  845.             }
  846.             DrawText(hdc, (LPTSTR)psz, -1, &rcText, uiStyle);
  847.             LocalFree(psz);
  848.         }
  849.     }
  850.     else
  851. #endif
  852.     DrawText(hdc, (LPTSTR)pszString, -1, &rcText, uiStyle);
  853.     SetBkMode(hdc, oldMode);
  854.     if (fHighlight)
  855.     {
  856.         SetBkColor (hdc, oldBkColor);
  857.         SetTextColor (hdc, oldTextColor);
  858.     }
  859. }
  860. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTONDATA pTBButton)
  861. {
  862.     if (TBISSTRINGPTR(pTBButton->iString))
  863.         return (LPTSTR)pTBButton->iString;
  864.     else {
  865.         if (pTBButton->iString != -1 &&
  866.             pTBButton->iString < ptb->nStrings)
  867.             return ptb->pStrings[pTBButton->iString];
  868.         return NULL;
  869.     }
  870. }
  871. HIMAGELIST TBGetImageList(PTBSTATE ptb, int iMode, int iIndex)
  872. {
  873.     HIMAGELIST himl = NULL;
  874.     ASSERT(iMode <= HIML_MAX);
  875.     if (iIndex >= 0 && iIndex < ptb->cPimgs) {
  876.         himl = ptb->pimgs[iIndex].himl[iMode];
  877.     }
  878.     return himl;
  879. }
  880. //
  881. //  v5 toolbars support multiple imagelists.  To use images from an alternate
  882. //  imagelist, set the imagelist handle via TB_SETIMAGELIST(iIndex, himlAlt)
  883. //  and set your button's iImage to MAKELONG(iImage, iIndex).
  884. //
  885. //  APP COMPAT:  GroupWise 5.5 passes crap as the iIndex (even though it
  886. //  was documented as "must be zero"), so we enable this functionality
  887. //  only for v5 toolbars.  IE4 ignored the iIndex, which is why they got
  888. //  away with it up until now.
  889. //
  890. #define MAX_TBIMAGELISTS 20             // arbitrary limit
  891. HIMAGELIST TBSetImageList(PTBSTATE ptb, int iMode, int iIndex, HIMAGELIST himl)
  892. {
  893.     HIMAGELIST himlOld = NULL;
  894.     // Watch out for app compat or for totally bogus parameters
  895.     if (ptb->ci.iVersion < 5 || iIndex < 0 || iIndex >= MAX_TBIMAGELISTS)
  896.         iIndex = 0;
  897.     ASSERT(iMode <= HIML_MAX);
  898.     if (iIndex >= ptb->cPimgs) {
  899.         // asking for more than we have, realloc.
  900.         void *p = CCLocalReAlloc(ptb->pimgs, (iIndex+1) * SIZEOF(TBIMAGELISTS));
  901.         if (p) {
  902.             ptb->pimgs = (TBIMAGELISTS*)p;
  903.             ZeroMemory(&ptb->pimgs[ptb->cPimgs], (iIndex + 1 - ptb->cPimgs) * sizeof(TBIMAGELISTS));
  904.             ptb->cPimgs = iIndex + 1;  // iIndex is 0 based, but cPimgs is 1 based (it's a count, not an index)
  905.         }
  906.     }
  907.     if (iIndex < ptb->cPimgs) {
  908.         himlOld = ptb->pimgs[iIndex].himl[iMode];
  909.         ptb->pimgs[iIndex].himl[iMode] = himl;
  910.     }
  911.     return himlOld;
  912. }
  913. // create a mono bitmap mask:
  914. //   1's where color == COLOR_BTNFACE || COLOR_3DHILIGHT
  915. //   0's everywhere else
  916. void CreateMask(int xoffset, int yoffset, int dx, int dy, BOOL fDrawGlyph, TBDRAWITEM * ptbdraw)
  917. {
  918.     LPTSTR psz;
  919.     IMAGELISTDRAWPARAMS imldp;
  920.     HIMAGELIST himl;
  921.     PTBSTATE ptb = ptbdraw->ptb;
  922.     LPTBBUTTONDATA pTBButton = ptbdraw->pbutton;
  923.     // initalize whole area with 1's
  924.     PatBlt(ptb->hdcMono, 0, 0, dx, dy, WHITENESS);
  925.     // create mask based on color bitmap
  926.     // convert this to 1's
  927.     himl = TBGetImageList(ptb, HIML_NORMAL, ptbdraw->iIndex);
  928.     if (fDrawGlyph && himl)
  929.     {
  930.         imldp.cbSize = sizeof(imldp);
  931.         imldp.himl   = himl;
  932.         imldp.i      = ptbdraw->iImage;
  933.         imldp.hdcDst = ptb->hdcMono;
  934.         imldp.x      = xoffset;
  935.         imldp.y      = yoffset;
  936.         imldp.cx     = 0;
  937.         imldp.cy     = 0;
  938.         imldp.xBitmap= 0;
  939.         imldp.yBitmap= 0;
  940.         imldp.rgbBk  = g_clrBtnFace;
  941.         imldp.rgbFg  = CLR_DEFAULT;
  942.         imldp.fStyle = ILD_ROP | ILD_MASK;
  943.         imldp.dwRop  = SRCCOPY;
  944.         ImageList_DrawIndirect(&imldp);
  945.         imldp.fStyle = ILD_ROP | ILD_IMAGE;
  946.         imldp.rgbBk  = g_clrBtnHighlight;
  947.         imldp.dwRop  = SRCPAINT;
  948.         ImageList_DrawIndirect(&imldp);
  949.     }
  950.     psz = TB_StrForButton(ptb, pTBButton);
  951.     if (psz)
  952.     {
  953.         xoffset = 1;
  954.         yoffset = 1;
  955.         if (ptb->ci.style & TBSTYLE_LIST)
  956.         {
  957.             if (!(pTBButton->iBitmap == I_IMAGENONE &&
  958.                 (pTBButton->fsStyle & BTNS_AUTOSIZE)))
  959.             {
  960.                 xoffset += ptb->iDxBitmap + ptb->iListGap;
  961.                 dx -= ptb->iDxBitmap + ptb->iListGap;
  962.             }
  963.         }
  964.         else 
  965.         {
  966.             yoffset += ptb->iDyBitmap + 1;
  967.             dy -= ptb->iDyBitmap + 1;
  968.         }
  969.         if (!(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  970.         {
  971.             dx -= g_cxEdge;
  972.             dy -= g_cyEdge;
  973.         }
  974.         // The FALSE in 4th param is so we don't get a box in the mask.
  975.         DrawString(ptb->hdcMono, xoffset, yoffset, dx, dy, psz,
  976.                    FALSE, ptbdraw);
  977.     }
  978. }
  979. void DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, TBDRAWITEM * ptbdraw)
  980. {
  981.     RECT r1;
  982.     UINT state;
  983.     // face color
  984.     // The Office toolbar sends us bitmaps that are smaller than they claim they are
  985.     // So we need to do the PatB or the window background shows through around the
  986.     // edges of the button bitmap  -jjk
  987.     ASSERT(ptbdraw);
  988.     state = ptbdraw->state;
  989.     if (!(state & TBSTATE_CHECKED))
  990.         PatB(hdc, x, y, dx, dy, ptbdraw->tbcd.clrBtnFace);
  991.     if  ( !(ptbdraw->dwCustom & TBCDRF_NOEDGES))
  992.     {
  993.         r1.left = x;
  994.         r1.top = y;
  995.         r1.right = x + dx;
  996.         r1.bottom = y + dy;
  997.         DrawEdge(hdc, &r1, (state & (TBSTATE_CHECKED | TBSTATE_PRESSED)) ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT | BF_SOFT);
  998.     }
  999. }
  1000. // these are raster ops
  1001. #define DSPDxax     0x00E20746  // BUGBUG: not used
  1002. #define PSDPxax     0x00B8074A
  1003. HWND g_hwndDebug = NULL;
  1004. void DrawFace(HDC hdc, int x, int y, int offx, int offy, int dxText,
  1005.               int dyText, TBDRAWITEM * ptbdraw)
  1006. {
  1007.     LPTSTR psz;
  1008.     IMAGELISTDRAWPARAMS imldp;
  1009.     BOOL fHotTrack = FALSE;
  1010.     UINT state;
  1011.     PTBSTATE ptb;
  1012.     LPTBBUTTONDATA ptButton;
  1013.     BOOL fImage;        // !fImage means no image (as opposed to a blank image)
  1014.     ASSERT(ptbdraw);
  1015.     ptb = ptbdraw->ptb;
  1016.     ptButton = ptbdraw->pbutton;
  1017.     // AutosizeTextNoImage
  1018.     if ((ptb->ci.style & TBSTYLE_LIST) &&
  1019.         (ptbdraw->iImage == I_IMAGENONE) &&
  1020.         (ptButton->fsStyle & BTNS_AUTOSIZE)) {
  1021.         fImage = FALSE;
  1022.     } else {
  1023.         fImage = TRUE;
  1024.     }
  1025.     state = ptbdraw->state;
  1026.     if (state & TBSTATE_ENABLED)
  1027.     {
  1028.         fHotTrack = ptbdraw->fHotTrack;
  1029.         if (ptb->ci.style & TBSTYLE_FLAT)
  1030.         {
  1031.             UINT bdr = 0;
  1032.             if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
  1033.                 bdr = BDR_SUNKENOUTER;
  1034.             else if (fHotTrack)
  1035.                 bdr = BDR_RAISEDINNER;
  1036.             if (bdr)
  1037.             {
  1038.                 RECT rc;
  1039.                 TB_GetItemRect(ptb, (UINT)(ptButton - ptb->Buttons), &rc);
  1040.                 if (TB_HasSplitDDArrow(ptb, ptButton))
  1041.                     rc.right -= ptb->dxDDArrowChar;
  1042.                 if (!(ptbdraw->dwCustom & TBCDRF_NOEDGES) && ptb)
  1043.                     CCDrawEdge(hdc, &rc, bdr, BF_RECT, &(ptb->clrsc));
  1044.             }
  1045.         }
  1046.     }
  1047.     imldp.himl = NULL;
  1048.     if (fHotTrack || (state & TBSTATE_CHECKED)) {
  1049.         imldp.himl   = TBGetImageList(ptb, HIML_HOT, ptbdraw->iIndex);
  1050.         if (!imldp.himl)
  1051.             imldp.himl = TBGetImageList(ptb, HIML_NORMAL, ptbdraw->iIndex);
  1052.     } else if (DRAW_MONO_BTN(ptb, state) && (imldp.himl = TBGetImageList(ptb, HIML_DISABLED, ptbdraw->iIndex))) {
  1053.         // assigned in if statement
  1054.     } else if (imldp.himl = TBGetImageList(ptb, HIML_NORMAL, ptbdraw->iIndex)) {
  1055.         // assigned in if statement
  1056.     }
  1057.     if (imldp.himl && (ptbdraw->iImage != -1) && fImage)
  1058.     {
  1059.         COLORREF rgbBk = ptbdraw->tbcd.clrBtnFace;
  1060.         if (ptb->ci.style & TBSTYLE_TRANSPARENT) 
  1061.             rgbBk = CLR_NONE;
  1062.         
  1063.         if (ptb->dwStyleEx & TBSTYLE_EX_INVERTIBLEIMAGELIST)
  1064.             rgbBk = CLR_DEFAULT;
  1065.         imldp.cbSize = sizeof(imldp);
  1066.         imldp.i      = ptbdraw->iImage;
  1067.         imldp.hdcDst = hdc;
  1068.         imldp.x      = x + offx;
  1069.         imldp.y      = y + offy;
  1070.         imldp.cx     = 0;
  1071.         imldp.cy     = 0;
  1072.         imldp.xBitmap= 0;
  1073.         imldp.yBitmap= 0;
  1074.         imldp.rgbBk  = rgbBk;
  1075.         imldp.rgbFg  = CLR_DEFAULT;
  1076.         imldp.fStyle = ILD_NORMAL;
  1077.         if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE))
  1078.             imldp.fStyle = ILD_TRANSPARENT;
  1079. #ifdef TBHIGHLIGHT_GLYPH
  1080.         if ((state & TBSTATE_MARKED) && !(ptbdraw->dwCustom & TBCDRF_NOMARK))
  1081.             imldp.fStyle = ILD_TRANSPARENT | ILD_BLEND50;
  1082. #endif
  1083.         if (ptbdraw->dwCustom & TBCDRF_BLENDICON)
  1084.             imldp.fStyle = ILD_TRANSPARENT | ILD_BLEND50;
  1085.         ImageList_DrawIndirect(&imldp);
  1086. #ifdef DEBUG
  1087.         if (g_hwndDebug == ptb->ci.hwnd) {
  1088.             imldp.hdcDst = GetDC(NULL);
  1089.             ImageList_DrawIndirect(&imldp);
  1090.             ReleaseDC(NULL, imldp.hdcDst);
  1091.         }
  1092. #endif
  1093.     }
  1094.     psz = TB_StrForButton(ptb, ptButton);
  1095.     if (psz)
  1096.     {
  1097.         BOOL bHighlight = (state & TBSTATE_MARKED) && (ptb->ci.style & TBSTYLE_LIST) &&
  1098.                           !(ptbdraw->dwCustom & TBCDRF_NOMARK);
  1099.         if ((state & (TBSTATE_PRESSED | TBSTATE_CHECKED)) &&
  1100.             !(ptbdraw->dwCustom & TBCDRF_NOOFFSET))
  1101.         {
  1102.             x++;
  1103.             if (ptb->ci.style & TBSTYLE_LIST)
  1104.                 y++;
  1105.         }
  1106.         if (ptb->ci.style & TBSTYLE_LIST)
  1107.         {
  1108.             if (fImage)
  1109.             {
  1110.                 x += ptb->iDxBitmap + ptb->iListGap;
  1111.                 dxText -= ptb->iDxBitmap + ptb->iListGap;
  1112.             }
  1113.             else
  1114.             {
  1115.                 // fudge for I_IMAGENONE buttons
  1116.                 x += g_cxEdge;
  1117.             }
  1118.         }
  1119.         else
  1120.         {
  1121.             y += offy + ptb->iDyBitmap;
  1122.             dyText -= offy + ptb->iDyBitmap;
  1123.         }
  1124.         DrawString(hdc, x + 1, y + 1, dxText, dyText, psz, bHighlight, ptbdraw);
  1125.     }
  1126. }
  1127. void InitTBDrawItem(TBDRAWITEM * ptbdraw, PTBSTATE ptb, LPTBBUTTONDATA pbutton,
  1128.                     UINT state, BOOL fHotTrack, int dxText, int dyText)
  1129. {
  1130.     NMTBCUSTOMDRAW * ptbcd;
  1131.     NMCUSTOMDRAW * pnmcd;
  1132.     ASSERT(ptbdraw);
  1133.     ptbdraw->ptb = ptb;
  1134.     ptbdraw->pbutton = pbutton;
  1135.     ptbdraw->fHotTrack = fHotTrack;
  1136.     ptbdraw->iIndex = GET_HIML_INDEX(pbutton->DUMMYUNION_MEMBER(iBitmap));
  1137.     ptbdraw->iImage = GET_IMAGE_INDEX(pbutton->DUMMYUNION_MEMBER(iBitmap));
  1138.     ptbdraw->state = state;
  1139.     ptbcd = &ptbdraw->tbcd;
  1140.     ptbcd->hbrMonoDither = g_hbrMonoDither;
  1141.     ptbcd->hbrLines = GetStockObject(BLACK_BRUSH);
  1142.     ptbcd->hpenLines = GetStockObject(BLACK_PEN);
  1143.     ptbcd->clrMark = g_clrHighlight;
  1144.     ptbcd->clrBtnHighlight = g_clrBtnHighlight;
  1145.     ptbcd->clrTextHighlight = g_clrHighlightText;
  1146.     ptbcd->clrBtnFace = g_clrBtnFace;
  1147.     ptbcd->nStringBkMode = TRANSPARENT;
  1148.     ptbcd->nHLStringBkMode = OPAQUE;
  1149.     ptbcd->clrText = g_clrBtnText;
  1150.     SetRect(&ptbcd->rcText, 0, 0, dxText, dyText);
  1151.     pnmcd = (NMCUSTOMDRAW *)ptbcd;
  1152.     pnmcd->uItemState = CDISFromState(state);
  1153. #ifdef KEYBOARDCUES
  1154. #if 0
  1155.     // BUGBUG: Custom draw stuff for UISTATE (stephstm)
  1156.     if (CCGetUIState(&(ptb->ci), KC_TBD))
  1157.         pnmcd->uItemState |= CDIS_SHOWKEYBOARDCUES;
  1158. #endif
  1159. #endif
  1160.     if ((ptb->ci.style & TBSTYLE_FLAT) && fHotTrack)
  1161.         pnmcd->uItemState |= CDIS_HOT;
  1162. }
  1163. void DrawButton(HDC hdc, int x, int y, PTBSTATE ptb, LPTBBUTTONDATA ptButton, BOOL fActive)
  1164. {
  1165.     // BUGBUG: cleanup -- separate layout calculation & rendering
  1166.     int yOffset;
  1167.     HBRUSH hbrOld;
  1168.     UINT state;
  1169.     int dxFace, dyFace;
  1170.     int dxText, dyText;
  1171.     int xCenterOffset;
  1172.     int dx = TBWidthOfButton(ptb, ptButton, hdc);
  1173.     HFONT oldhFont;
  1174.     int dy = ptb->iButHeight;
  1175.     TBDRAWITEM tbdraw = { 0 };
  1176.     NMTBCUSTOMDRAW * ptbcd = &tbdraw.tbcd;
  1177.     NMCUSTOMDRAW * pnmcd = (NMCUSTOMDRAW *)ptbcd;
  1178.     COLORREF clrSave;
  1179.     BOOL fHotTrack;
  1180.     HFONT hFontNoAntiAlias = NULL;
  1181.     state = (UINT)ptButton->fsState;
  1182.     // make local copy of state and do proper overriding
  1183.     if (state & TBSTATE_INDETERMINATE) {
  1184.         if (state & TBSTATE_PRESSED)
  1185.             state &= ~TBSTATE_INDETERMINATE;
  1186.         else if (state & TBSTATE_ENABLED)
  1187.             state = TBSTATE_INDETERMINATE;
  1188.         else
  1189.             state &= ~TBSTATE_INDETERMINATE;
  1190.     }
  1191.     if (!fActive) {
  1192.         state &= ~TBSTATE_ENABLED;
  1193.     }
  1194.     fHotTrack = TBIsHotTrack(ptb, ptButton, state);
  1195.     pnmcd->hdc = hdc;
  1196.     pnmcd->dwItemSpec = ptButton->idCommand;
  1197.     pnmcd->uItemState = 0;
  1198.     pnmcd->lItemlParam = (LPARAM)ptButton->dwData;
  1199.     SetRect(&pnmcd->rc, x, y, x + dx, y + dy);
  1200.     dxText = dx - (3 * g_cxEdge);
  1201.     if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1202.     {
  1203.         dyText = dy;
  1204.     }
  1205.     else
  1206.     {
  1207.         dyText = dy - (2 * g_cyEdge);
  1208.     }
  1209.     InitTBDrawItem(&tbdraw, ptb, ptButton, state, fHotTrack, dxText, dyText);
  1210.     tbdraw.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, (NMCUSTOMDRAW *)ptbcd);
  1211.     // We gotta update our concept of hotness
  1212.     tbdraw.fHotTrack = fHotTrack = pnmcd->uItemState & CDIS_HOT;
  1213.     if (!(tbdraw.dwCustom & CDRF_SKIPDEFAULT ))
  1214.     {
  1215.         // Get the state back from what custom draw may have set
  1216.         state = tbdraw.state = StateFromCDIS(pnmcd->uItemState);
  1217.         dxFace = dx - (2 * g_cxEdge);
  1218.         dyFace = dy - (2 * g_cyEdge);
  1219.         dxText = ptbcd->rcText.right - ptbcd->rcText.left;
  1220.         dyText = ptbcd->rcText.bottom - ptbcd->rcText.top;
  1221.         if (TB_HasDDArrow(ptb, ptButton) && !TB_HasTopDDArrow(ptb, ptButton)) {
  1222.             int iAdjust = TBDDArrowAdjustment(ptb, ptButton);
  1223.             dxFace -= iAdjust;
  1224.             dxText -= iAdjust;
  1225.         }
  1226.         // Should we display the font using the GDI AntiAliasing?
  1227.         if (!ptb->fAntiAlias)
  1228.         {
  1229.             // No. Must be doing drag and drop. We don't want to AntiAlias because the
  1230.             // Purple color key will show through and it looks ugly.
  1231.             LOGFONT lfFont;
  1232.             if (GetObject(ptb->hfontIcon, sizeof(lfFont), &lfFont))
  1233.             {
  1234.                 lfFont.lfQuality = NONANTIALIASED_QUALITY;
  1235.                 hFontNoAntiAlias = CreateFontIndirect(&lfFont);
  1236.             }
  1237.         }
  1238.         if (hFontNoAntiAlias)
  1239.             oldhFont = SelectObject(hdc, hFontNoAntiAlias);
  1240.         else
  1241.             oldhFont = SelectObject(hdc, ptb->hfontIcon);
  1242.         clrSave = SetTextColor(hdc, ptbcd->clrText);
  1243.         if (!(ptb->ci.style & TBSTYLE_FLAT))
  1244.             DrawBlankButton(hdc, x, y, dx, dy, &tbdraw);
  1245.         // move coordinates inside border and away from upper left highlight.
  1246.         // the extents change accordingly.
  1247.         x += g_cxEdge;
  1248.         y += g_cyEdge;
  1249.         if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1250.         {
  1251.             yOffset = (ptb->iButHeight - ptb->iDyBitmap) / 2;
  1252.         }
  1253.         else
  1254.         {
  1255.             // calculate offset of face from (x,y).  y is always from the top,
  1256.             // so the offset is easy.  x needs to be centered in face.
  1257.             // center it taking the padding into account the padding area
  1258.             yOffset = (ptb->yPad - (2 * g_cyEdge)) / 2;
  1259.         }
  1260.         if (yOffset < 0)
  1261.             yOffset = 0;
  1262.         if ((ptb->ci.style & TBSTYLE_LIST) && !BTN_NO_SHOW_TEXT(ptb, ptButton)) {
  1263.             xCenterOffset = ptb->xPad / 2;
  1264.         } else if (TB_HasTopDDArrow(ptb, ptButton)) {
  1265.             //
  1266.             // Layout of "top dropdown" buttons looks like this:
  1267.             //
  1268.             //       icon            
  1269.             // fudge   |  dropdown arrow
  1270.             //    |    |    |
  1271.             //    v    v    v
  1272.             // +-+-+-------+--+-+
  1273.             // | | |       |  | |
  1274.             // | | |       |  | |
  1275.             // +-+-+-------+--+-+
  1276.             // |     <text>     |
  1277.             // +----------------+
  1278.             //
  1279.             // |<--- dxFace --->|
  1280.             //
  1281.             // xCenterOffset is the offset at which to start drawing the icon.
  1282.             //
  1283.             xCenterOffset = (dxFace + CX_TOP_FUDGE - (ptb->iDxBitmap + ptb->dxDDArrowChar)) / 2;
  1284.         } else {
  1285.             xCenterOffset = (dxFace - ptb->iDxBitmap) / 2;
  1286.         }
  1287.         if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED) &&
  1288.             !(tbdraw.dwCustom & TBCDRF_NOOFFSET))
  1289.         {
  1290.             // pressed state moves down and to the right
  1291.             xCenterOffset++;
  1292.             yOffset++;
  1293.         }
  1294.         // draw the dithered background
  1295.         if  ((!fHotTrack || ptb->ci.iVersion < 5) &&
  1296.              (((state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) ||
  1297.               ((state & TBSTATE_MARKED) &&
  1298.                !(ptb->ci.style & TBSTYLE_FLAT) &&
  1299.                !(tbdraw.dwCustom & TBCDRF_NOMARK)))))
  1300.         {
  1301.             //Custom Draw can set hbrMonoDither to be NULL. Validate it before using it
  1302.             hbrOld = ptbcd->hbrMonoDither ? SelectObject(hdc, ptbcd->hbrMonoDither) : NULL;
  1303.             if (hbrOld)
  1304.             {
  1305.                 COLORREF clrText, clrBack;
  1306. #ifdef TBHIGHLIGHT_BACK
  1307.                 if (state & TBSTATE_MARKED)
  1308.                     clrText = SetTextColor(hdc, ptbcd->clrMark);
  1309.                 else
  1310. #endif
  1311.                 clrText = SetTextColor(hdc, ptbcd->clrBtnHighlight); // 0 -> 0
  1312.                 clrBack = SetBkColor(hdc, ptbcd->clrBtnFace);        // 1 -> 1
  1313.                 // only draw the dither brush where the mask is 1's
  1314.                 PatBlt(hdc, x, y, dxFace, dyFace, PATCOPY);
  1315.                 SelectObject(hdc, hbrOld);
  1316.                 SetTextColor(hdc, clrText);
  1317.                 SetBkColor(hdc, clrBack);
  1318.             }
  1319.         }
  1320.         // Paint the background of the hot-tracked item if the
  1321.         // custom draw said so
  1322.         if ((tbdraw.dwCustom & TBCDRF_HILITEHOTTRACK) && fHotTrack)
  1323.         {
  1324.             PatB(hdc, pnmcd->rc.left, pnmcd->rc.top,
  1325.                  pnmcd->rc.right - pnmcd->rc.left, pnmcd->rc.bottom - pnmcd->rc.top,
  1326.                  ptbcd->clrHighlightHotTrack);
  1327.         }
  1328.         tbdraw.iImage = ptButton->DUMMYUNION_MEMBER(iBitmap);
  1329.         if((ptButton->DUMMYUNION_MEMBER(iBitmap) == I_IMAGECALLBACK) && ptb->fHimlNative)
  1330.         {
  1331.             NMTBDISPINFO  tbgdi = {0};
  1332.             tbgdi.dwMask  = TBNF_IMAGE;
  1333.             TBGetItem(ptb,ptButton,&tbgdi);
  1334.             tbdraw.iImage = tbgdi.iImage;
  1335.         }
  1336.         tbdraw.iIndex = GET_HIML_INDEX(tbdraw.iImage);
  1337.         tbdraw.iImage = GET_IMAGE_INDEX(tbdraw.iImage);
  1338.         // Now put on the face.
  1339.         // TODO: Validate himlDisabled and ensure that the index is in range
  1340.         if (!DRAW_MONO_BTN(ptb, state) ||
  1341.             TBGetImageList(ptb, HIML_DISABLED, tbdraw.iIndex))
  1342.         {
  1343.             // regular version
  1344.             int yStart = y;
  1345.             if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1346.                 yStart -= g_cyEdge;
  1347.             DrawFace(hdc, x, yStart, xCenterOffset, yOffset, dxText, dyText, &tbdraw);
  1348.         }
  1349.         if (DRAW_MONO_BTN(ptb, state))
  1350.         {
  1351.             HBITMAP hbmOld;
  1352.             //initialize the monochrome dc
  1353.             if (!ptb->hdcMono) {
  1354.                 ptb->hdcMono = CreateCompatibleDC(hdc);
  1355.                 if (!ptb->hdcMono)
  1356.                     return;
  1357.                 SetTextColor(ptb->hdcMono, 0L);
  1358.                 SelectObject(ptb->hdcMono, ptb->hfontIcon);
  1359.             }
  1360.             hbmOld = SelectObject(ptb->hdcMono, ptb->hbmMono);
  1361.             //
  1362.             // If we a mirrored DC, mirror the Memory DC so that
  1363.             // text written on the bitmap won't get flipped.
  1364.             //
  1365.             if ((IS_DC_RTL_MIRRORED(hdc)) &&
  1366.                 (!(IS_DC_RTL_MIRRORED(ptb->hdcMono))))
  1367.             {
  1368.                 SET_DC_RTL_MIRRORED(ptb->hdcMono);
  1369.             }
  1370.             // disabled version (or indeterminate)
  1371.             CreateMask(xCenterOffset, yOffset, dxFace, dyFace, (TBGetImageList(ptb, HIML_DISABLED, tbdraw.iIndex) == NULL), &tbdraw);
  1372.             SetTextColor(hdc, 0L);       // 0's in mono -> 0 (for ROP)
  1373.             SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
  1374.             // draw glyph's etched-effect
  1375.             if (!(state & TBSTATE_INDETERMINATE) &&
  1376.                 !(tbdraw.dwCustom & TBCDRF_NOETCHEDEFFECT)) {
  1377.                 hbrOld = SelectObject(hdc, g_hbrBtnHighlight);
  1378.                 if (hbrOld) {
  1379.                     // draw hilight color where we have 0's in the mask
  1380.                     BitBlt(hdc, x + 1, y + 1, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
  1381.                     SelectObject(hdc, hbrOld);
  1382.                 }
  1383.             }
  1384.             // gray out glyph
  1385.             hbrOld = SelectObject(hdc, g_hbrBtnShadow);
  1386.             if (hbrOld) {
  1387.                 // draw the shadow color where we have 0's in the mask
  1388.                 BitBlt(hdc, x, y, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
  1389.                 SelectObject(hdc, hbrOld);
  1390.             }
  1391.             if (state & TBSTATE_CHECKED) {
  1392.                 BitBlt(ptb->hdcMono, 1, 1, dxFace - 1, dyFace - 1, ptb->hdcMono, 0, 0, SRCAND);
  1393.             }
  1394.             SelectObject(ptb->hdcMono, hbmOld);
  1395.         }
  1396.         if (TB_HasDDArrow(ptb, ptButton))
  1397.         {
  1398.             WORD wDSAFlags = DCHF_TRANSPARENT | DCHF_FLIPPED;
  1399.             BOOL fPressedDD = ((ptb->Buttons + ptb->iPressedDD) == ptButton);
  1400.             RECT rc;
  1401.             if (TB_HasTopDDArrow(ptb, ptButton)) {
  1402.                 // position the dd arrow up next to the bitmap
  1403.                 rc.left = x + xCenterOffset + ptb->iDxBitmap;
  1404.                 rc.right = rc.left + ptb->dxDDArrowChar;
  1405.                 rc.top = y + yOffset;
  1406.                 rc.bottom = rc.top + ptb->iDyBitmap;
  1407.             }
  1408.             else 
  1409.             {
  1410.                 // position the dd arrow to the right of the text & bitmap
  1411.                 TB_GetItemRect(ptb, (UINT)(ptButton - ptb->Buttons), &rc);
  1412.                 rc.left = rc.right - ptb->dxDDArrowChar;
  1413.             }
  1414.             if (TB_HasUnsplitDDArrow(ptb, ptButton)) {
  1415.                 // if a non-split dd arrow, don't draw a border.
  1416.                 wDSAFlags |= DCHF_NOBORDER;
  1417.             }
  1418.             if (DRAW_MONO_BTN(ptb, state)) {
  1419.                 // DFCS_INACTIVE means "draw the arrow part grayed"
  1420.                 wDSAFlags |= DCHF_INACTIVE;
  1421.             }
  1422.             // if TB_HasTopDDArrow, we've already offset rect, so don't draw DCHF_PUSHED
  1423.             else if ((fPressedDD || (state & (TBSTATE_CHECKED | TBSTATE_PRESSED))) &&
  1424.                    !TB_HasTopDDArrow(ptb, ptButton)) {
  1425.                 // DCHF_PUSHED means "offset the arrow and draw indented border"
  1426.                 wDSAFlags |= DCHF_PUSHED;
  1427.             } 
  1428.             else if (fHotTrack || !(ptb->ci.style & TBSTYLE_FLAT)) {
  1429.                 // DCHF_HOT means "draw raised border"
  1430.                 // non-flat dropdown arrows are either pushed or hot
  1431.                 wDSAFlags |= DCHF_HOT;
  1432.             }
  1433.             DrawScrollArrow(hdc, &rc, wDSAFlags);
  1434.         }
  1435.         SelectObject(hdc, oldhFont);
  1436.         SetTextColor(hdc, clrSave);
  1437.         if (hFontNoAntiAlias)
  1438.         {
  1439.             DeleteObject(hFontNoAntiAlias);
  1440.         }
  1441.     }
  1442.     if (tbdraw.dwCustom & CDRF_NOTIFYPOSTPAINT)
  1443.         CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, (NMCUSTOMDRAW *)ptbcd);
  1444. }
  1445. // make sure that g_hbmMono is big enough to do masks for this
  1446. // size of button.  if not, fail.
  1447. BOOL CheckMonoMask(PTBSTATE ptb, int width, int height)
  1448. {
  1449.     BITMAP bm;
  1450.     HBITMAP hbmTemp;
  1451.     if (ptb->hbmMono) {
  1452.         GetObject(ptb->hbmMono, sizeof(BITMAP), &bm);
  1453.         if (width <= bm.bmWidth && height <= bm.bmHeight) {
  1454.             return TRUE;
  1455.         }
  1456.     }
  1457.     // Add a bit of fudge to keep this from being reallocated too often.
  1458.     hbmTemp = CreateMonoBitmap(width+8, height+8);
  1459.     if (!hbmTemp)
  1460.         return FALSE;
  1461.     if (ptb->hbmMono)
  1462.         DeleteObject(ptb->hbmMono);
  1463.     ptb->hbmMono = hbmTemp;
  1464.     return TRUE;
  1465. }
  1466. /*
  1467. ** GrowToolbar
  1468. **
  1469. ** Attempt to grow the button size.
  1470. **
  1471. ** The calling function can either specify a new internal measurement
  1472. ** (GT_INSIDE) or a new external measurement.
  1473. **
  1474. ** GT_MASKONLY updates the mono mask and nothing else.
  1475. */
  1476. BOOL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, UINT flags)
  1477. {
  1478.     if (!newButWidth)
  1479.         newButWidth = DEFAULTBUTTONX;
  1480.     if (!newButHeight)
  1481.         newButHeight = DEFAULTBUTTONY;
  1482.     // if growing based on inside measurement, get full size
  1483.     if (flags & GT_INSIDE)
  1484.     {
  1485.         if (ptb->ci.style & TBSTYLE_LIST)
  1486.             newButWidth += ptb->iDxBitmap + ptb->iListGap;
  1487.         newButHeight += ptb->yPad;
  1488.         newButWidth += ptb->xPad;
  1489.         // if toolbar already has strings, don't shrink width it because it
  1490.         // might clip room for the string
  1491.         if ((newButWidth < ptb->iButWidth) && ptb->nStrings &&
  1492.             (ptb->ci.iVersion < 5 || ptb->nTextRows > 0))
  1493.             newButWidth = ptb->iButWidth;
  1494.     }
  1495.     else {
  1496.         if (newButHeight == -1)
  1497.             newButHeight = ptb->iButHeight;
  1498.         if (newButWidth == -1)
  1499.             newButWidth = ptb->iButWidth;
  1500.         if (newButHeight < ptb->iDyBitmap + ptb->yPad)
  1501.             newButHeight = ptb->iDyBitmap + ptb->yPad;
  1502.         if (newButWidth < ptb->iDxBitmap + ptb->xPad)
  1503.             newButWidth = ptb->iDxBitmap + ptb->xPad;
  1504.     }
  1505.     // if the size of the toolbar is actually growing, see if shadow
  1506.     // bitmaps can be made sufficiently large.
  1507.     if (!ptb->hbmMono || (newButWidth > ptb->iButWidth) || (newButHeight > ptb->iButHeight)) {
  1508.         if (!CheckMonoMask(ptb, newButWidth, newButHeight))
  1509.             return(FALSE);
  1510.     }
  1511.     if (flags & GT_MASKONLY)
  1512.         return(TRUE);
  1513.     if (!(flags & GT_INSIDE) && ((ptb->iButWidth != newButWidth) || (ptb->iButHeight != newButHeight)))
  1514.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1515.     ptb->iButWidth = newButWidth;
  1516.     ptb->iButHeight = newButHeight;
  1517.     // bar height has 2 pixels above, 2 below
  1518.     if (ptb->ci.style & TBSTYLE_FLAT)
  1519.         ptb->iYPos = 0;
  1520.     else
  1521.         ptb->iYPos = 2;
  1522.     TBInvalidateItemRects(ptb);
  1523.     return TRUE;
  1524. }
  1525. BOOL SetBitmapSize(PTBSTATE ptb, int width, int height)
  1526. {
  1527.     int realh;
  1528.     if (!width)
  1529.         width = 1;
  1530.     if (!height)
  1531.         height = 1;
  1532.     if (width == -1)
  1533.         width = ptb->iDxBitmap;
  1534.     if (height == -1)
  1535.         height = ptb->iDyBitmap;
  1536.     realh = height;
  1537.     if ((ptb->iDxBitmap == width) && (ptb->iDyBitmap == height))
  1538.         return TRUE;
  1539.     if (TBHasStrings(ptb))
  1540.         realh = HeightWithString(ptb, height);
  1541.     if (GrowToolbar(ptb, width, realh, GT_INSIDE)) {
  1542.         ptb->iDxBitmap = width;
  1543.         ptb->iDyBitmap = height;
  1544.         // the size changed, we need to rebuild the imagelist
  1545.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1546.         TBInvalidateImageList(ptb);
  1547.         return TRUE;
  1548.     }
  1549.     return FALSE;
  1550. }
  1551. void TB_OnSysColorChange(PTBSTATE ptb)
  1552. {
  1553.     int i;
  1554.     InitGlobalColors();
  1555.     //  Reset all of the bitmaps
  1556.     for (i = 0; i < ptb->cPimgs; i++) {
  1557.         HIMAGELIST himl = TBGetImageList(ptb, HIML_NORMAL, i);
  1558.         if (himl)
  1559.             ImageList_SetBkColor(himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  1560.         himl = TBGetImageList(ptb, HIML_HOT, i);
  1561.         if (himl)
  1562.             ImageList_SetBkColor(himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  1563.     }
  1564. }
  1565. #define CACHE 0x01
  1566. #define BUILD 0x02
  1567. void PASCAL ReleaseMonoDC(PTBSTATE ptb)
  1568. {
  1569.     if (ptb->hdcMono) {
  1570.         SelectObject(ptb->hdcMono, g_hfontSystem);
  1571.         DeleteDC(ptb->hdcMono);
  1572.         ptb->hdcMono = NULL;
  1573.     }
  1574. }
  1575. void TB_OnEraseBkgnd(PTBSTATE ptb, HDC hdc)
  1576. {
  1577.     NMTBCUSTOMDRAW  tbcd = { 0 };
  1578.     DWORD           dwRes = FALSE;
  1579.     tbcd.nmcd.hdc = hdc;
  1580.     if (ptb->ci.style & TBSTYLE_CUSTOMERASE) {
  1581.         ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREERASE, (NMCUSTOMDRAW *)&tbcd);
  1582.     } else {
  1583.         ptb->ci.dwCustom = CDRF_DODEFAULT;
  1584.     }
  1585.     if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
  1586.     {
  1587.         // for transparent toolbars, forward erase background to parent
  1588.         // but handle thru DefWindowProc in the event parent doesn't paint
  1589.         if (!(ptb->ci.style & TBSTYLE_TRANSPARENT) ||
  1590.             !CCForwardEraseBackground(ptb->ci.hwnd, hdc))
  1591.             DefWindowProc(ptb->ci.hwnd, WM_ERASEBKGND, (WPARAM) hdc, 0);
  1592.     }
  1593.     if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
  1594.         CICustomDrawNotify(&ptb->ci, CDDS_POSTERASE, (NMCUSTOMDRAW *)&tbcd);
  1595. }
  1596. void PASCAL DrawInsertMark(HDC hdc, LPRECT prc, BOOL fHorizMode, COLORREF clr)
  1597. {
  1598.     HPEN hPnMark = CreatePen(PS_SOLID, 1, clr);
  1599.     HPEN hOldPn;
  1600.     POINT rgPoint[4];
  1601.     if (!hPnMark)
  1602.         hPnMark = (HPEN)GetStockObject(BLACK_PEN);    // fallback to draw with black pen
  1603.     hOldPn = (HPEN)SelectObject(hdc, (HGDIOBJ)hPnMark);
  1604.     if ( fHorizMode )
  1605.     {
  1606.         int iXCentre = (prc->left + prc->right) /2;
  1607.         rgPoint[0].x = iXCentre + 1;
  1608.         rgPoint[0].y = prc->top + 2;
  1609.         rgPoint[1].x = iXCentre + 3;
  1610.         rgPoint[1].y = prc->top;
  1611.         rgPoint[2].x = iXCentre - 2;
  1612.         rgPoint[2].y = prc->top;
  1613.         rgPoint[3].x = iXCentre;
  1614.         rgPoint[3].y = prc->top + 2;
  1615.         // draw the top bit...
  1616.         Polyline( hdc, rgPoint, 4 );
  1617.         rgPoint[0].x = iXCentre;
  1618.         rgPoint[0].y = prc->top;
  1619.         rgPoint[1].x = iXCentre;
  1620.         rgPoint[1].y = prc->bottom - 1;
  1621.         rgPoint[2].x = iXCentre + 1;
  1622.         rgPoint[2].y = prc->bottom - 1;
  1623.         rgPoint[3].x = iXCentre + 1;
  1624.         rgPoint[3].y = prc->top;
  1625.         // draw the middle...
  1626.         Polyline( hdc, rgPoint, 4 );
  1627.         rgPoint[0].x = iXCentre + 1;
  1628.         rgPoint[0].y = prc->bottom - 3;
  1629.         rgPoint[1].x = iXCentre + 3;
  1630.         rgPoint[1].y = prc->bottom - 1;
  1631.         rgPoint[2].x = iXCentre - 2;
  1632.         rgPoint[2].y = prc->bottom - 1;
  1633.         rgPoint[3].x = iXCentre;
  1634.         rgPoint[3].y = prc->bottom - 3;
  1635.         // draw the bottom bit...
  1636.         Polyline( hdc, rgPoint, 4 );
  1637.     }
  1638.     else
  1639.     {
  1640.         int iYCentre = (prc->top + prc->bottom) /2;
  1641.         rgPoint[0].x = prc->left + 2;
  1642.         rgPoint[0].y = iYCentre;
  1643.         rgPoint[1].x = prc->left;
  1644.         rgPoint[1].y = iYCentre - 2;
  1645.         rgPoint[2].x = prc->left;
  1646.         rgPoint[2].y = iYCentre + 3;
  1647.         rgPoint[3].x = prc->left + 2;
  1648.         rgPoint[3].y = iYCentre + 1;
  1649.         // draw the top bit...
  1650.         Polyline( hdc, rgPoint, 4 );
  1651.         rgPoint[0].x = prc->left;
  1652.         rgPoint[0].y = iYCentre;
  1653.         rgPoint[1].x = prc->right - 1;
  1654.         rgPoint[1].y = iYCentre;
  1655.         rgPoint[2].x = prc->right - 1;
  1656.         rgPoint[2].y = iYCentre + 1;
  1657.         rgPoint[3].x = prc->left;
  1658.         rgPoint[3].y = iYCentre + 1;
  1659.         // draw the middle...
  1660.         Polyline( hdc, rgPoint, 4 );
  1661.         rgPoint[0].x = prc->right - 3;
  1662.         rgPoint[0].y = iYCentre;
  1663.         rgPoint[1].x = prc->right - 1;
  1664.         rgPoint[1].y = iYCentre - 2;
  1665.         rgPoint[2].x = prc->right - 1;
  1666.         rgPoint[2].y = iYCentre + 3;
  1667.         rgPoint[3].x = prc->right - 3;
  1668.         rgPoint[3].y = iYCentre + 1;
  1669.         // draw the bottom bit...
  1670.         Polyline( hdc, rgPoint, 4 );
  1671.     }
  1672.     SelectObject( hdc, hOldPn );
  1673.     DeleteObject((HGDIOBJ)hPnMark);
  1674. }
  1675. BOOL TBIsRectClipped(PTBSTATE ptb, LPRECT prc)
  1676. {
  1677.     RECT rc;
  1678.     RECT rcTB;
  1679.     if (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN)
  1680.         CopyRect(&rcTB, &ptb->rc);
  1681.     else
  1682.         GetClientRect(ptb->ci.hwnd, &rcTB);
  1683.     if (IntersectRect(&rc, &rcTB, prc)) {
  1684.         if (EqualRect(prc, &rc))
  1685.             return FALSE;
  1686.     }
  1687.     return TRUE;
  1688. }
  1689. BOOL TBShouldDrawButton(PTBSTATE ptb, LPRECT prcBtn, HDC hdc)
  1690. {
  1691.     // don't bother drawing buttons that aren't in the dc clipping region
  1692.     if (RectVisible(hdc, prcBtn)) {
  1693.         if (ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
  1694.             return !TBIsRectClipped(ptb, prcBtn);
  1695.         else
  1696.             return TRUE;
  1697.     }
  1698.     return FALSE;
  1699. }
  1700. // goin horizontal . . .
  1701. void DrawToolbarH(PTBSTATE ptb, HDC hdc, LPRECT prc)
  1702. {
  1703.     int iButton, xButton, yButton, cxBar;
  1704.     LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  1705.     cxBar = prc->right - prc->left;
  1706.     yButton   = ptb->iYPos;
  1707.     prc->top    = ptb->iYPos;
  1708.     prc->bottom = ptb->iYPos + ptb->iButHeight;   // BUGBUG (scotth): what if first btn is a separator?
  1709.     for (iButton = 0, xButton = ptb->xFirstButton;
  1710.             iButton < ptb->iNumButtons; iButton++)
  1711.     {
  1712.         LPTBBUTTONDATA pButton = &pAllButtons[iButton];
  1713.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  1714.         {
  1715.             int cxButton = TBWidthOfButton(ptb, pButton, hdc);
  1716.             // Is there anything to draw?
  1717.             if (!(pButton->fsStyle & BTNS_SEP) || (ptb->ci.style & TBSTYLE_FLAT))
  1718.             {
  1719.                 // Yes
  1720.                 prc->left = xButton;
  1721.                 prc->right = xButton + cxButton;
  1722.                 if (TBShouldDrawButton(ptb, prc, hdc))
  1723.                 {
  1724.                     // Draw separator?
  1725.                     if (pButton->fsStyle & BTNS_SEP)
  1726.                     {
  1727.                         // Yes; must be a flat separator.  Is this toolbar vertical?
  1728.                         if (ptb->ci.style & CCS_VERT)
  1729.                         {
  1730.                             // Yes; draw a horizontal separator.  Center w/in the
  1731.                             // button rect
  1732.                             int iSave = prc->top;
  1733.                             prc->top += (TBGetSepHeight(ptb, pButton) - 1) / 2;
  1734.                             InflateRect(prc, -g_cxEdge, 0);
  1735.                             CCDrawEdge(hdc, prc, EDGE_ETCHED, BF_TOP, &(ptb->clrsc));
  1736.                             InflateRect(prc, g_cxEdge, 0);
  1737.                             prc->top = iSave;
  1738.                         }
  1739.                         else
  1740.                         {
  1741.                             // No; draw a vertical separator
  1742.                             prc->left += (cxButton - 1) / 2;
  1743.                             InflateRect(prc, 0, -g_cyEdge);
  1744.                             CCDrawEdge(hdc, prc, EDGE_ETCHED, BF_LEFT, &(ptb->clrsc));
  1745.                             InflateRect(prc, 0, g_cyEdge);
  1746.                         }
  1747.                     }
  1748.                     else
  1749.                     {
  1750.                         // No
  1751.                         DrawButton(hdc, xButton, yButton, ptb, pButton, ptb->fActive);
  1752.                     }
  1753.                 }
  1754.             }
  1755.             xButton += (cxButton - s_dxOverlap);
  1756.             if (pButton->fsState & TBSTATE_WRAP)
  1757.             {
  1758.                 int dy;
  1759.                 if (pButton->fsStyle & BTNS_SEP)
  1760.                 {
  1761.                     if (ptb->ci.style & CCS_VERT)
  1762.                         dy = TBGetSepHeight(ptb, pButton);
  1763.                     else
  1764.                     {
  1765.                         if (ptb->ci.style & TBSTYLE_FLAT)
  1766.                         {
  1767.                             // Draw a separator across the entire toolbar to separate rows.
  1768.                             // For horizontal toolbars only.
  1769.                             RECT rcMid;
  1770.                             rcMid.top = prc->top + ptb->iButHeight + ((TBGetSepHeight(ptb, pButton) - 1) / 2);
  1771.                             rcMid.bottom = rcMid.top + g_cxEdge;
  1772.                             rcMid.left = g_cxEdge;
  1773.                             rcMid.right = cxBar - g_cxEdge;
  1774.                             CCDrawEdge(hdc, &rcMid, EDGE_ETCHED, BF_TOP, &(ptb->clrsc));
  1775.                         }
  1776.                         dy = ptb->iButHeight + TBGetSepHeight(ptb, pButton);
  1777.                     }
  1778.                 }
  1779.                 else
  1780.                     dy = ptb->iButHeight;
  1781.                 xButton = ptb->xFirstButton;
  1782.                 yButton   += dy;
  1783.                 prc->top    += dy;
  1784.                 prc->bottom += dy;
  1785.             }
  1786.         }
  1787.     }
  1788. }
  1789. // goin vertical . . .
  1790. void DrawToolbarV(PTBSTATE ptb, HDC hdc, LPRECT prc)
  1791. {
  1792.     int iButton, xButton, yButton, cyBar;
  1793.     LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  1794.     NMTBCUSTOMDRAW  tbcd = { 0 };
  1795.     LPTBBUTTONDATA pButton = pAllButtons;
  1796.     cyBar = prc->bottom - prc->top;
  1797.     xButton = ptb->xFirstButton;
  1798.     prc->left = xButton;
  1799.     prc->right = prc->left + ptb->iButWidth;
  1800.     for (iButton = 0, yButton = 0;
  1801.             iButton < ptb->iNumButtons; iButton++, pButton++)
  1802.     {
  1803.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  1804.         {
  1805.             // Is there anything to draw?
  1806.             if (!(pButton->fsStyle & BTNS_SEP) || (ptb->ci.style & TBSTYLE_FLAT))
  1807.             {
  1808.                 int cyButton;
  1809.                 
  1810.                 if (pButton->fsStyle & BTNS_SEP)
  1811.                     cyButton = TBGetSepHeight(ptb, pButton);
  1812.                 else
  1813.                     cyButton = ptb->iButHeight;
  1814.                 prc->top = yButton;
  1815.                 prc->bottom = yButton + cyButton;
  1816.                 if (TBShouldDrawButton(ptb, prc, hdc))
  1817.                 {
  1818.                     // Draw separator?
  1819.                     if (pButton->fsStyle & BTNS_SEP)
  1820.                     {
  1821.                         DWORD dwCustRet;
  1822.                         NMTBCUSTOMDRAW  tbcd = { 0 };
  1823.                         tbcd.nmcd.hdc = hdc;
  1824.                         tbcd.nmcd.dwItemSpec = -1;
  1825.                         CopyRect(&tbcd.nmcd.rc, prc);
  1826.                         dwCustRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, (NMCUSTOMDRAW *)&tbcd);
  1827.                         if ( !(CDRF_SKIPDEFAULT &  dwCustRet) )
  1828.                         {
  1829.                             // Yes; must be a flat separator.
  1830.                             InflateRect(prc, -g_cxEdge, 0);
  1831.                             CCDrawEdge(hdc, prc, EDGE_ETCHED, BF_TOP, &(ptb->clrsc));
  1832.                             InflateRect(prc, g_cxEdge, 0);
  1833.                         }
  1834.                     }
  1835.                     else
  1836.                     {
  1837.                         // No
  1838.                         DrawButton(hdc, xButton, yButton, ptb, pButton, ptb->fActive);
  1839.                     }
  1840.                 }
  1841.                 
  1842.                 yButton += cyButton;
  1843.             }
  1844.             if (pButton->fsState & TBSTATE_WRAP)
  1845.             {
  1846.                 int dx;
  1847.             
  1848.                 if (ptb->ci.style & TBSTYLE_FLAT)
  1849.                 {
  1850.                     // Draw a separator vertival across the entire toolbar to separate cols.
  1851.                     // For vertical toolbars only.
  1852.                     RECT rcMid;
  1853.                     rcMid.top = ptb->rc.top + g_cxEdge;
  1854.                     rcMid.bottom = ptb->rc.bottom - g_cxEdge;
  1855.                     rcMid.left = xButton + ptb->iButWidth;
  1856.                     rcMid.right = rcMid.left + g_cxEdge;
  1857.                     CCDrawEdge(hdc, &rcMid, EDGE_ETCHED, BF_LEFT, &(ptb->clrsc));
  1858.                 }
  1859.                 dx = ptb->iButWidth + g_cxEdge;
  1860.                 yButton  = 0;
  1861.                 xButton += dx;
  1862.                 prc->left += dx;
  1863.                 prc->right += dx;
  1864.             }
  1865.         }
  1866.     }
  1867. }
  1868. COLORREF TB_GetInsertMarkColor(PTBSTATE ptb)
  1869. {
  1870.     if (ptb->clrim == CLR_DEFAULT)
  1871.         return g_clrBtnText;
  1872.     else
  1873.         return ptb->clrim;
  1874. }
  1875. void TBPaint(PTBSTATE ptb, HDC hdcIn)
  1876. {
  1877.     RECT rc;
  1878.     HDC hdc;
  1879.     PAINTSTRUCT ps;
  1880.     NMTBCUSTOMDRAW  tbcd = { 0 };
  1881.     GetClientRect(ptb->ci.hwnd, &rc);
  1882.     if (hdcIn)
  1883.     {
  1884.         hdc = hdcIn;
  1885.     }
  1886.     else
  1887.         hdc = BeginPaint(ptb->ci.hwnd, &ps);
  1888.     if (!rc.right)
  1889.         goto Error1;
  1890.     tbcd.nmcd.hdc = hdc;
  1891.     tbcd.nmcd.rc = rc;
  1892.     ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREPAINT, (NMCUSTOMDRAW *)&tbcd);
  1893.     if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
  1894.     {
  1895.         if (!ptb->fHimlValid)
  1896.             TBBuildImageList(ptb);
  1897.         if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1898.             DrawToolbarV(ptb, hdc, &rc);
  1899.         else
  1900.             DrawToolbarH(ptb, hdc, &rc);
  1901.         if (ptb->iInsert!=-1)
  1902.         {
  1903.             BOOL fHorizMode = !(ptb->ci.style & CCS_VERT);
  1904.             RECT rc;
  1905.             if (GetInsertMarkRect(ptb, &rc, fHorizMode))
  1906.             {
  1907.                 DrawInsertMark(hdc, &rc, fHorizMode, TB_GetInsertMarkColor(ptb));
  1908.             }
  1909.         }
  1910.         ReleaseMonoDC(ptb);
  1911.     }
  1912.     if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  1913.     {
  1914.         tbcd.nmcd.hdc = hdc;
  1915.         tbcd.nmcd.uItemState = 0;
  1916.         tbcd.nmcd.lItemlParam = 0;
  1917.         CICustomDrawNotify(&ptb->ci, CDDS_POSTPAINT, (NMCUSTOMDRAW *)&tbcd);
  1918.     }
  1919. Error1:
  1920.     if (hdcIn == NULL)
  1921.         EndPaint(ptb->ci.hwnd, &ps);
  1922. }
  1923. void TB_GetItemDropDownRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
  1924. {
  1925.     TB_GetItemRect(ptb,uButton,lpRect);
  1926.     lpRect->left = lpRect->right - ptb->dxDDArrowChar;
  1927. }
  1928. int TBHeightOfButton(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  1929. {
  1930.     int dy;
  1931. if ((ptbb->fsStyle & BTNS_SEP)  && 
  1932. (ptbb->fsState & TBSTATE_WRAP || ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  1933. {
  1934. if (!(ptb->ci.style & CCS_VERT) && !(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)) 
  1935. {
  1936. dy = TBGetSepHeight(ptb, ptbb) + ptb->iButHeight;
  1937. else 
  1938. {
  1939. dy = TBGetSepHeight(ptb, ptbb);
  1940. }
  1941. }
  1942. else
  1943. {
  1944. dy = ptb->iButHeight;
  1945. }
  1946.     return dy;
  1947. }
  1948. void TB_CalcItemRects(PTBSTATE ptb)
  1949. {
  1950.     int iButton, xPos, yPos;
  1951.     ASSERT(!ptb->fItemRectsValid);
  1952.     xPos = ptb->xFirstButton;
  1953.     yPos = ptb->iYPos;
  1954.     for (iButton = 0; iButton < ptb->iNumButtons; iButton++)
  1955.     {
  1956.         int xPosButton;
  1957.         LPTBBUTTONDATA pButton = &ptb->Buttons[iButton];
  1958.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  1959.         {
  1960.             if ((pButton->fsState & TBSTATE_WRAP) && (pButton->fsStyle & BTNS_SEP))
  1961.                 xPosButton = ptb->xFirstButton;
  1962.             else
  1963.                 xPosButton = xPos;
  1964.             pButton->pt.x = xPosButton;
  1965.             pButton->pt.y = yPos;
  1966.             if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1967.             {
  1968.                 if (pButton->fsState & TBSTATE_WRAP)
  1969.                 {
  1970.                     xPos += (ptb->iButWidth + g_cxEdge);    // to not overwrite the edge.
  1971.                     yPos = 0;
  1972.                 }
  1973.                 else if (pButton->fsStyle & BTNS_SEP)
  1974.                     yPos += (TBGetSepHeight(ptb, pButton));
  1975.                 else
  1976.                     yPos += ptb->iButHeight;
  1977.             }
  1978.             else // standard horizontal toolbar.
  1979.             {
  1980.                 xPos += TBWidthOfButton(ptb, pButton, NULL) - s_dxOverlap;
  1981.                 if (pButton->fsState & TBSTATE_WRAP)
  1982.                 {
  1983.                     yPos += ptb->iButHeight;
  1984.                     if (pButton->fsStyle & BTNS_SEP)
  1985.                     {
  1986.                         if (ptb->ci.style & CCS_VERT) {
  1987.                             yPos -= ptb->iButHeight;
  1988.                         }
  1989.                         yPos += (TBGetSepHeight(ptb, pButton));
  1990.                     }
  1991.                     xPos = ptb->xFirstButton;
  1992.                 }
  1993.             }
  1994.         }
  1995.     }
  1996. }
  1997. BOOL TB_GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
  1998. {
  1999.     int dy = ptb->iButHeight;
  2000.     if (uButton >= (UINT)ptb->iNumButtons
  2001.         || (ptb->Buttons[uButton].fsState & TBSTATE_HIDDEN))
  2002.     {
  2003.         return FALSE;
  2004.     }
  2005.     if (!ptb->fItemRectsValid) {
  2006.         TB_CalcItemRects(ptb);
  2007.         ptb->fItemRectsValid = TRUE;
  2008.     }
  2009.     lpRect->left   = ptb->Buttons[uButton].pt.x;
  2010.     lpRect->right  = lpRect->left + TBWidthOfButton(ptb, &ptb->Buttons[uButton], NULL);
  2011.     lpRect->top    = ptb->Buttons[uButton].pt.y;
  2012.     lpRect->bottom = lpRect->top + TBHeightOfButton(ptb, &ptb->Buttons[uButton]);
  2013.     return TRUE;
  2014. }
  2015. void InvalidateButton(PTBSTATE ptb, LPTBBUTTONDATA pButtonToPaint, BOOL fErase)
  2016. {
  2017.     RECT rc;
  2018.     if (TB_GetItemRect(ptb, (UINT) (pButtonToPaint - ptb->Buttons), &rc))
  2019.     {
  2020.         InvalidateRect(ptb->ci.hwnd, &rc, fErase);
  2021.     }
  2022. }
  2023. /*----------------------------------------------------------
  2024. Purpose: Toggles the button as a dropdown
  2025. Returns: TRUE if handled
  2026. */
  2027. BOOL TBToggleDropDown(PTBSTATE ptb, int iPos, BOOL fEatMsg)
  2028. {
  2029.     BOOL bRet = FALSE;
  2030.     LPTBBUTTONDATA ptbButton = &ptb->Buttons[iPos];
  2031.     ASSERT(TB_IsDropDown(ptbButton));
  2032.     if (ptbButton->fsState & TBSTATE_ENABLED)
  2033.     {
  2034.         UINT nVal;
  2035.         ptb->iPressedDD = iPos;
  2036.         if (TB_HasUnsplitDDArrow(ptb, ptbButton))
  2037.             ptbButton->fsState |= TBSTATE_PRESSED;
  2038.         InvalidateButton(ptb, ptbButton, TRUE);
  2039.         UpdateWindow(ptb->ci.hwnd);
  2040.         MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT, iPos+1);
  2041.         nVal = (UINT) SendItemNotify(ptb, ptbButton->idCommand, TBN_DROPDOWN);
  2042.         if (TBDDRET_DEFAULT == nVal || TBDDRET_TREATPRESSED == nVal)
  2043.         {
  2044.             if (fEatMsg)
  2045.             {
  2046.                 MSG msg;
  2047.                 PeekMessage(&msg, ptb->ci.hwnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
  2048.             }
  2049.             ptb->iPressedDD = -1;
  2050.             if (TB_HasUnsplitDDArrow(ptb, ptbButton))
  2051.                 ptbButton->fsState &= ~TBSTATE_PRESSED;
  2052.             InvalidateButton(ptb, ptbButton, TRUE);
  2053.             UpdateWindow(ptb->ci.hwnd);
  2054.             MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT, iPos+1);
  2055.         }
  2056.         bRet = (TBDDRET_DEFAULT == nVal);
  2057.     }
  2058.     return bRet;
  2059. }
  2060. void TBInvalidateButton(PTBSTATE ptb, int i, BOOL fErase)
  2061. {
  2062.     if (i != -1) {
  2063.         InvalidateButton(ptb, &ptb->Buttons[i], fErase);
  2064.    }
  2065. }
  2066. void TBSetHotItem(PTBSTATE ptb, int iPos, DWORD dwReason)
  2067. {
  2068.     HWND hwnd;
  2069.     if ((ptb->ci.style & TBSTYLE_FLAT) ) {
  2070.         // Either one of these values can be -1, but refrain
  2071.         // from processing if both are negative b/c it is wasteful
  2072.         // and very common
  2073.         if ((ptb->iHot != iPos || (dwReason & HICF_RESELECT)) &&
  2074.             (0 <= ptb->iHot || 0 <= iPos) &&
  2075.             iPos < ptb->iNumButtons)
  2076.         {
  2077.             NMTBHOTITEM nmhot = {0};
  2078.             // Has the mouse moved away from the toolbar but
  2079.             // do we still anchor the highlight?
  2080.             if (0 > iPos && ptb->fAnchorHighlight && (dwReason & HICF_MOUSE))
  2081.                 return ;        // Yes; deny the hot item change
  2082.             // Send a notification about the hot item change
  2083.             if (0 > ptb->iHot)
  2084.             {
  2085.                 if (iPos >= 0)
  2086.                     nmhot.idNew = ptb->Buttons[iPos].idCommand;
  2087.                 nmhot.dwFlags = HICF_ENTERING;
  2088.             }
  2089.             else if (0 > iPos)
  2090.             {
  2091.                 if (ptb->iHot >= 0 && ptb->iHot < ptb->iNumButtons)
  2092.                     nmhot.idOld = ptb->Buttons[ptb->iHot].idCommand;
  2093.                 nmhot.dwFlags = HICF_LEAVING;
  2094.             }
  2095.             else
  2096.             {
  2097.                 if (ptb->iHot < ptb->iNumButtons)
  2098.                     nmhot.idOld = ptb->Buttons[ptb->iHot].idCommand;
  2099.                 nmhot.idNew = ptb->Buttons[iPos].idCommand;
  2100.             }
  2101.             nmhot.dwFlags |= dwReason;
  2102.             // must save this for revalidation
  2103.             hwnd = ptb->ci.hwnd;
  2104.             if (CCSendNotify(&ptb->ci, TBN_HOTITEMCHANGE, &nmhot.hdr))
  2105.                 return;         // deny the hot item change
  2106.             // Revalidate the window
  2107.             if (!IsWindow(hwnd)) return;
  2108.             TBInvalidateButton(ptb, ptb->iHot, TRUE);
  2109.             if ((iPos < 0) || !(ptb->Buttons[iPos].fsState & TBSTATE_ENABLED))
  2110.                 iPos = -1;
  2111.             ptb->iHot = iPos;
  2112.             if (GetFocus() == ptb->ci.hwnd)
  2113.                 MyNotifyWinEvent(EVENT_OBJECT_FOCUS, ptb->ci.hwnd, OBJID_CLIENT, iPos + 1);
  2114.             TBInvalidateButton(ptb, ptb->iHot, TRUE);
  2115.             if ((iPos >= 0 && iPos < ptb->iNumButtons) &&
  2116.                 (TB_IsDropDown(&ptb->Buttons[iPos])) &&
  2117.                 (dwReason & HICF_TOGGLEDROPDOWN))
  2118.             {
  2119.                 TBToggleDropDown(ptb, iPos, FALSE);
  2120.             }
  2121.         }
  2122.     }
  2123. }
  2124. BOOL GetInsertMarkRect(PTBSTATE ptb, LPRECT prc, BOOL fHorizMode)
  2125. {
  2126.     BOOL fRet = TB_GetItemRect(ptb, ptb->iInsert, prc);
  2127.     if (fRet)
  2128.     {
  2129.         // if we are in horizontal mode, we need a vertical insertion marker
  2130.         if ( fHorizMode )
  2131.         {
  2132.             if (ptb->fInsertAfter)
  2133.                 prc->left = prc->right;
  2134.             else
  2135.                 prc->right = prc->left;
  2136.             prc->left -= INSERTMARKSIZE/2;
  2137.             prc->right += INSERTMARKSIZE/2 + 1;
  2138.         }
  2139.         else
  2140.         {
  2141.             if (ptb->fInsertAfter)
  2142.                 prc->top = prc->bottom;
  2143.             else
  2144.                 prc->bottom = prc->top;
  2145.             prc->top -= INSERTMARKSIZE/2;
  2146.             prc->bottom += INSERTMARKSIZE/2 + 1;
  2147.         }
  2148.     }
  2149.     return fRet;
  2150. }
  2151. void TBInvalidateMark(PTBSTATE ptb)
  2152. {
  2153.     RECT rc;
  2154.     if (GetInsertMarkRect(ptb, &rc, !(ptb->ci.style & CCS_VERT)))
  2155.     {
  2156.         InvalidateRect(ptb->ci.hwnd, &rc, TRUE);
  2157.     }
  2158. }
  2159. void TBSetInsertMark(PTBSTATE ptb, LPTBINSERTMARK ptbim)
  2160. {
  2161.     if (ptbim->iButton != ptb->iInsert ||
  2162.         BOOLIFY(ptb->fInsertAfter) != BOOLIFY(ptbim->dwFlags & TBIMHT_AFTER))
  2163.     {
  2164.         if (ptb->iInsert != -1)
  2165.             TBInvalidateMark(ptb);
  2166.         ptb->iInsert = ptbim->iButton;
  2167.         ptb->fInsertAfter = BOOLIFY(ptbim->dwFlags & TBIMHT_AFTER);
  2168.         if (ptb->iInsert != -1)
  2169.             TBInvalidateMark(ptb);
  2170.     }
  2171. }
  2172. void TBCycleHotItem(PTBSTATE ptb, int iStart, int iDirection, UINT nReason)
  2173. {
  2174.     int i;
  2175.     int iPrev;
  2176.     NMTBWRAPHOTITEM nmwh;
  2177.     nmwh.iDir = iDirection;
  2178.     nmwh.nReason = nReason;
  2179.     //When cycling around the menu, without this check, the second to last menu
  2180.     //item would be selected.
  2181.     if (iStart == -1 && iDirection == -1)
  2182.         iStart = 0;
  2183.     for (i = 0; i < ptb->iNumButtons; i++)
  2184.     {
  2185.         iPrev = iStart;
  2186.         iStart += iDirection + ptb->iNumButtons;
  2187.         iStart %= ptb->iNumButtons;
  2188.         if ( ( iPrev + iDirection >= ptb->iNumButtons) || (iPrev + iDirection < 0) )
  2189.         {
  2190.             nmwh.iStart = iStart;
  2191.             if (CCSendNotify(&ptb->ci, TBN_WRAPHOTITEM, &nmwh.hdr))
  2192.                 return;
  2193.         }
  2194.         if (ptb->Buttons[iStart].fsState & TBSTATE_ENABLED &&
  2195.             !(ptb->Buttons[iStart].fsState & TBSTATE_HIDDEN) &&
  2196.             !(ptb->Buttons[iStart].fsStyle & BTNS_SEP))
  2197.         {
  2198.             // if the old hot item was dropped down, undrop it.
  2199.             if (ptb->iHot != -1 && ptb->iHot == ptb->iPressedDD)
  2200.                 TBToggleDropDown(ptb, ptb->iHot, FALSE);
  2201.             TBSetHotItem(ptb, iStart, nReason);
  2202.             break;
  2203.         }
  2204.     }
  2205. }
  2206. // Do hit testing by sliding the origin of the supplied point
  2207. //
  2208. // returns:
  2209. //  >= 0    index of non separator item hit
  2210. //  < 0     index of separator or nearest non separator item (area
  2211. //          just below and to the left)
  2212. //
  2213. // +--------------------------------------
  2214. // |      -1    -1    -1    -1
  2215. // |      btn   sep   btn
  2216. // |    +-----+     +-----+
  2217. // |    |     |     |     |
  2218. // | -1 |  0  | -1  |  2  | -3
  2219. // |    |     |     |     |
  2220. // |    +-----+     +-----+
  2221. // |
  2222. // | -1   -1    -1    -2    -3
  2223. //
  2224. int TBHitTest(PTBSTATE ptb, int xPos, int yPos)
  2225. {
  2226.     int prev = 0;
  2227.     int last = 0;
  2228.     int i;
  2229.     RECT rc;
  2230.     if (ptb->iNumButtons == 0)
  2231.         return(-1);
  2232.     for (i=0; i<ptb->iNumButtons; i++)
  2233.     {
  2234.         if (TB_GetItemRect(ptb, i, &rc))
  2235.         {
  2236.             // ignore this button if hidden because of HideClippedButtons style
  2237.             if (!(ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS) || !(TBIsRectClipped(ptb, &rc)))
  2238.             {
  2239.                 // From PtInRect docs:
  2240.                 //   A point is within a rectangle if it lies on the left or top
  2241.                 //   side or is within all four sides. A point on the right or
  2242.                 //   bottom side is considered outside the rectangle.
  2243.                 if (yPos >= rc.top && yPos < rc.bottom)
  2244.                 {
  2245.                     if (xPos >= rc.left && xPos < rc.right)
  2246.                     {
  2247.                         if (ptb->Buttons[i].fsStyle & BTNS_SEP)
  2248.                             return - i - 1;
  2249.                         else
  2250.                             return i;
  2251.                     }
  2252.                     else
  2253.                     {
  2254.                         prev = i + 1;
  2255.                     }
  2256.                 }
  2257.                 else
  2258.                 {
  2259.                     last = i;
  2260.                 }
  2261.             }
  2262.         }
  2263.     }
  2264.     if (prev)
  2265.         return -1 - prev;
  2266.     else if (yPos > rc.bottom)
  2267.         // this means that we are off the bottom of the toolbar
  2268.         return(- i - 1);
  2269.     return last + 1;
  2270. }
  2271. // Same as above except:
  2272. //  - returns TRUE if the cursor is on the button edge.
  2273. //  - returns FALSE is the cursor is b/t buttons or on the button itself
  2274. BOOL TBInsertMarkHitTest(PTBSTATE ptb, int xPos, int yPos, LPTBINSERTMARK ptbim)
  2275. {
  2276.     TBINSERTMARK prev = {-1, TBIMHT_AFTER|TBIMHT_BACKGROUND}; // best guess if we hit a row
  2277.     TBINSERTMARK last = {-1, TBIMHT_AFTER|TBIMHT_BACKGROUND}; // best guess if we don't
  2278.     int i;
  2279.     // restrict hit testing depending upon whether we are vertical or horizontal
  2280.     BOOL fHorizMode = !(ptb->ci.style & CCS_VERT);
  2281.     for (i=0; i<ptb->iNumButtons; i++)
  2282.     {
  2283.         RECT rc;
  2284.         if (TB_GetItemRect(ptb, i, &rc))
  2285.         {
  2286.             if (yPos >= rc.top && yPos < rc.bottom)
  2287.             {
  2288.                 if (xPos >= rc.left && xPos < rc.right)
  2289.                 {
  2290.                     ptbim->iButton = i;
  2291.                     if ( fHorizMode )
  2292.                     {
  2293.                         if (xPos < rc.left + g_cxEdge*4)
  2294.                         {
  2295.                             ptbim->dwFlags = 0;
  2296.                             return TRUE;
  2297.                         }
  2298.                         else if (xPos > rc.right - g_cxEdge*4)
  2299.                         {
  2300.                             ptbim->dwFlags = TBIMHT_AFTER;
  2301.                             return TRUE;
  2302.                         }
  2303.                     }
  2304.                     else
  2305.                     {
  2306.                         // vertical....
  2307.                         if (yPos < rc.top + g_cyEdge*4)
  2308.                         {
  2309.                             ptbim->dwFlags = 0;
  2310.                             return TRUE;
  2311.                         }
  2312.                         else if (yPos > rc.bottom - g_cyEdge*4)
  2313.                         {
  2314.                             ptbim->dwFlags = TBIMHT_AFTER;
  2315.                             return TRUE;
  2316.                         }
  2317.                     }
  2318.                     // else we are just on a button...
  2319.                     ptbim->dwFlags = 0;
  2320.                     return FALSE;
  2321.                 }
  2322.                 else
  2323.                 {
  2324.                     if (xPos < rc.left)
  2325.                     {
  2326.                         // since buttons are laid out left to right
  2327.                         // and rows are laid out top to bottom,
  2328.                         // if we ever hit this case, we can't hit anything else
  2329.                         ptbim->iButton = i;
  2330.                         ptbim->dwFlags = TBIMHT_BACKGROUND;
  2331.                         return FALSE;
  2332.                     }
  2333.                     else // (xPos > rc.right)
  2334.                     {
  2335.                         // remember the last one we've seen on this row
  2336.                         prev.iButton = i;
  2337.                     }
  2338.                 }
  2339.             }
  2340.             else
  2341.             {
  2342.                 if (yPos < rc.top)
  2343.                 {
  2344.                     if (prev.iButton != -1)
  2345.                     {
  2346.                         *ptbim = prev;
  2347.                     }
  2348.                     else
  2349.                     {
  2350.                         ptbim->iButton = i;
  2351.                         ptbim->dwFlags = TBIMHT_BACKGROUND;
  2352.                     }
  2353.                 }
  2354.                 else
  2355.                 {
  2356.                     // remember the last one we've seen
  2357.                     last.iButton = i;
  2358.                 }
  2359.             }
  2360.         }
  2361.     }
  2362.     if (prev.iButton != -1)
  2363.         *ptbim = prev;
  2364.     else
  2365.         *ptbim = last;
  2366.     return FALSE;
  2367. }
  2368. int CountRows(PTBSTATE ptb)
  2369. {
  2370.     LPTBBUTTONDATA pButton, pBtnLast;
  2371.     int rows = 1;
  2372.     // BUGBUG (scotth): this doesn't look vertical-friendly
  2373.     // chrisny:  semantically no, technically it will work like a charm :-)
  2374.     pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  2375.     for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++) {
  2376.         if (pButton->fsState & TBSTATE_WRAP) {
  2377.             rows++;
  2378.             if (pButton->fsStyle & BTNS_SEP)
  2379.                 rows++;
  2380.         }
  2381.     }
  2382.     return rows;
  2383. }
  2384. #define CountCols(ptb)  CountRows(ptb)
  2385. void WrapToolbarCol(PTBSTATE ptb, int dy, LPRECT lpRect, int *pCols)
  2386. {
  2387.     LPTBBUTTONDATA pButton, pBtnLast, pBtnPrev;
  2388.     LPTBBUTTONDATA pbtnLastVisible = NULL;
  2389.     LPTBBUTTONDATA pbtnPrev = NULL;
  2390.     int xPos, yPos;
  2391.     int dyButton;
  2392.     int yPosWrap = 0;
  2393.     int cCols = 1;
  2394.     DEBUG_CODE( int cItemsPerCol = 0; )
  2395.     ASSERT(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL);
  2396.     TraceMsg(TF_TOOLBAR, "Toolbar: calculating WrapToolbar");
  2397.     // dy must be at least the button height, otherwise the final
  2398.     // rect is mis-calculated and will be too big.
  2399.     if (dy < ptb->iButHeight)
  2400.         dy = ptb->iButHeight;
  2401.     dyButton = ptb->iButHeight;
  2402.     xPos = ptb->xFirstButton;
  2403.     yPos = ptb->iYPos;
  2404.     pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  2405.     ptb->szCached.cx = -1;
  2406.     ptb->szCached.cy = -1;
  2407.     if (pCols)
  2408.         (*pCols) = 1;
  2409.     pBtnPrev = ptb->Buttons;
  2410.     for (pButton = ptb->Buttons; pButton < pBtnLast; pButton++)
  2411.     {
  2412.         DEBUG_CODE( cItemsPerCol++; )
  2413.         // we nuke the wrap state at the start of the loop.
  2414.         // so we don't know if/when we are adding on a wrap bit that wasn't there
  2415.         // before.  we overstep the button, then back up when we've gone too far,
  2416.         pButton->fsState &= ~TBSTATE_WRAP;
  2417.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  2418.         {
  2419.             if (pButton->fsStyle & BTNS_SEP)
  2420.                 yPos += (TBGetSepHeight(ptb, pButton));
  2421.             else
  2422.                 yPos += dyButton;
  2423.             // Is this button out of bounds?
  2424.             if (yPos > dy)
  2425.             {
  2426.                 // Yes; wrap it.
  2427.                 if ((pButton->fsStyle & BTNS_SEP) &&
  2428.                     yPos - TBGetSepHeight(ptb, pButton) > yPosWrap)
  2429.                 {
  2430.                     yPosWrap = yPos - TBGetSepHeight(ptb, pButton); // wrap at first in next col.
  2431.                 }
  2432.                 else if (yPos - dyButton > yPosWrap)
  2433.                     yPosWrap = yPos - dyButton; // wrap at first in next col.
  2434.                 if (xPos + ptb->iButWidth <= ptb->sizeBound.cx)
  2435.                     xPos += ptb->iButWidth;
  2436.                 yPos = dyButton;
  2437.                 cCols++;
  2438.                 pBtnPrev->fsState |= TBSTATE_WRAP;
  2439.                 DEBUG_CODE( cItemsPerCol = 0; )
  2440.             }
  2441.            // button in bounds gets handled above.
  2442.             pBtnPrev = pButton; // save previous for wrap point
  2443.         }
  2444.     }
  2445.     yPos = yPosWrap ? yPosWrap : yPos;
  2446.     if (pCols)
  2447.         *pCols = cCols;
  2448.     ptb->rc.left = 0;
  2449.     ptb->rc.right = xPos + ptb->iButWidth;
  2450.     ptb->rc.top = 0;
  2451.     ptb->rc.bottom = yPos;
  2452.     if (lpRect)
  2453.         CopyRect(lpRect, &ptb->rc);
  2454.     InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  2455. }
  2456. /**** WrapToolbar: * The buttons in the toolbar is layed out from left to right,
  2457.  * top to bottom. If adding another button to the current row,
  2458.  * while computing the layout, would cause that button to extend
  2459.  * beyond the right edge or the client area, then locate a break-
  2460.  * point (marked with the TBSTATE_WRAP flag). A break-point is:
  2461.  *
  2462.  * a) The right-most separator on the current row.
  2463.  *
  2464.  * b) The right-most button if there is no separator on the current row.
  2465.  *
  2466.  * A new row is also started at the end of any button group (sequence
  2467.  * of buttons that are delimited by separators) that are taller than
  2468.  * or equal to two rows.
  2469.  */
  2470. void WrapToolbar(PTBSTATE ptb, int dx, LPRECT lpRect, int *pRows)
  2471. {
  2472.     BOOL fInvalidate = FALSE;
  2473.     LPTBBUTTONDATA pButton, pBtnT, pBtnLast;
  2474.     LPTBBUTTONDATA pbtnLastVisible = NULL;
  2475.     LPTBBUTTONDATA pbtnPrev = NULL;
  2476.     BOOL fLastVisibleWrapped = FALSE;
  2477.     int xPos, yPos, xMax;
  2478.     int dyButton;
  2479.     BOOL bWrapAtNextSeparator = FALSE;
  2480.     ASSERT(!(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL));
  2481.     TraceMsg(TF_TOOLBAR, "Toolbar: calculating WrapToolbar");
  2482.     if (ptb->iNumButtons == 0) {
  2483.         // no buttons, so we're not going to go through the loop below; initialize 
  2484.         // dyButton to 0 so that we fill in lpRect with 0 height.  this fixes ideal 
  2485.         // size calculation for empty toolbars (NT5 #180430)
  2486.         dyButton = 0;
  2487.     } else {
  2488.         if (dx < ptb->iButWidth) {
  2489.             // dx must be at least the button width, otherwise the final
  2490.             // rect is mis-calculated and will be too big.
  2491.             dx = ptb->iButWidth;
  2492.         }
  2493.         dyButton = ptb->iButHeight;
  2494.     }
  2495.     xMax = 0;
  2496.     xPos = ptb->xFirstButton;
  2497.     yPos = ptb->iYPos;
  2498.     pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  2499.     ptb->szCached.cx = -1;
  2500.     ptb->szCached.cy = -1;
  2501.     if (pRows)
  2502.         (*pRows)=1;
  2503.     for (pButton = ptb->Buttons; pButton < pBtnLast; pButton++)
  2504.     {
  2505.         // we nuke the wrap state at the start of the loop.
  2506.         // so we don't know if/when we are adding on a wrap bit that wasn't there
  2507.         // before.  we overstep the button, then back up when we've gone too far,
  2508.         // so we can't simply keep the at the start of the loop
  2509.         // we need to keep it over to the next iteration
  2510.         BOOL fNextLastVisibleWrapped = (pButton->fsState & TBSTATE_WRAP);
  2511.         LPTBBUTTONDATA pbtnSav = pButton;
  2512.         pButton->fsState &= ~TBSTATE_WRAP;
  2513.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  2514.         {
  2515.             LPTBBUTTONDATA pbtnNextLastVisible = pButton;
  2516.             xPos += TBWidthOfButton(ptb, pButton, NULL) - s_dxOverlap;
  2517.             // Is this a normal button and is the button out of bounds?
  2518.             if (!(pButton->fsStyle & BTNS_SEP) && (xPos > dx)) {
  2519.                 // Yes; wrap it.  Go back to the first non-hidden separator
  2520.                 // as a break-point candidate.
  2521.                 for (pBtnT=pButton;
  2522.                      pBtnT>ptb->Buttons && !(pBtnT->fsState & TBSTATE_WRAP);
  2523.                      pBtnT--)
  2524.                 {
  2525.                     if ((pBtnT->fsStyle & BTNS_SEP) &&
  2526.                         !(pBtnT->fsState & TBSTATE_HIDDEN))
  2527.                     {
  2528.                         yPos += (TBGetSepHeight(ptb, pBtnT)) + dyButton;
  2529.                         bWrapAtNextSeparator = FALSE;
  2530.                         if (pRows)
  2531.                             (*pRows)++;
  2532.                         goto SetWrapHere;
  2533.                     }
  2534.                 }
  2535.                 pBtnT = pButton;
  2536.                 // Are we at the first button?
  2537.                 if (pButton != ptb->Buttons) {
  2538.                     // No; back up to first non-hidden button
  2539.                     do {
  2540.                         pBtnT--;
  2541.                     } while ((pBtnT>ptb->Buttons) &&
  2542.                              (pBtnT->fsState & TBSTATE_HIDDEN));
  2543.                     // Is it already wrapped?
  2544.                     if (pBtnT->fsState & TBSTATE_WRAP)
  2545.                     {
  2546.                         // Yes; wrap the button we were looking at originally
  2547.                         pBtnT = pButton;
  2548.                     }
  2549.                 }
  2550.                 // Wrap at the next separator because we've now wrapped in the middle
  2551.                 // of a group of buttons.
  2552.                 bWrapAtNextSeparator = TRUE;
  2553.                 yPos += dyButton;
  2554. SetWrapHere:
  2555.                 pBtnT->fsState |= TBSTATE_WRAP;
  2556.                 // find out if this wrap bit is new...
  2557.                 // it isn't if this button was the last visible button
  2558.                 // and that last visible button started off wrapped
  2559.                 if (pBtnT != pbtnLastVisible || !fLastVisibleWrapped)
  2560.                     fInvalidate = TRUE;
  2561.                 xPos = ptb->xFirstButton;
  2562.                 pButton = pBtnT;
  2563.                 // Count another row.
  2564.                 if (pRows)
  2565.                     (*pRows)++;
  2566.             }
  2567.             else
  2568.             {
  2569.                 // No; this is a separator (in or out of bounds) or a button that is in-bounds.
  2570.                 if (pButton->fsStyle & BTNS_SEP)
  2571.                 {
  2572.                     if (ptb->ci.style & CCS_VERT)
  2573.                     {
  2574.                         if (pbtnPrev && !(pbtnPrev->fsState & TBSTATE_WRAP))
  2575.                         {
  2576.                             pbtnPrev->fsState |= TBSTATE_WRAP;
  2577.                             yPos += dyButton;
  2578.                         }
  2579.                         xPos = ptb->xFirstButton;
  2580.                         yPos += TBGetSepHeight(ptb, pButton);
  2581.                         pButton->fsState |= TBSTATE_WRAP;
  2582.                         if (pRows)
  2583.                             (*pRows)++;
  2584.                     }
  2585.                     else if (bWrapAtNextSeparator)
  2586.                     {
  2587.                         bWrapAtNextSeparator = FALSE;
  2588.                         pButton->fsState |= TBSTATE_WRAP;
  2589.                         xPos = ptb->xFirstButton;
  2590.                         yPos += dyButton + (TBGetSepHeight(ptb, pButton));
  2591.                         if (pRows)
  2592.                             (*pRows)+=2;
  2593.                     }
  2594.                 }
  2595.                 // This button is visible and it's one we cached at the top of the loop
  2596.                 // set it for the next loop
  2597.                 if (pButton == pbtnNextLastVisible) {
  2598.                     ASSERT(!(pButton->fsState & TBSTATE_HIDDEN));
  2599.                     if (!(pButton->fsState & TBSTATE_HIDDEN)) {
  2600.                         // we don't know that we're not going to re-wrap an item that was initially wrapped
  2601.                         // until this point
  2602.                         if (pbtnLastVisible && fLastVisibleWrapped && !(pbtnLastVisible->fsState & TBSTATE_WRAP))
  2603.                             fInvalidate = TRUE;
  2604.                         pbtnLastVisible = pButton;
  2605.                         fLastVisibleWrapped = fNextLastVisibleWrapped;
  2606.                     }
  2607.                 }
  2608.             }
  2609.             if (!(pButton->fsStyle&BTNS_SEP))
  2610.                 xMax = max(xPos, xMax);
  2611.             pbtnPrev = pbtnSav;
  2612.         }
  2613.     }
  2614.     if (lpRect)
  2615.     {
  2616.         lpRect->left = 0;
  2617.         lpRect->right = xMax;
  2618.         lpRect->top = 0;
  2619.         lpRect->bottom = yPos + ptb->iYPos + dyButton;
  2620.     }
  2621.     if (fInvalidate)
  2622.         InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  2623. }
  2624. // only called from TB_SETROWS so no worry's about TBSTYLE_EX_MULTICOLUMN
  2625. BOOL BoxIt(PTBSTATE ptb, int height, BOOL fLarger, LPRECT lpRect)
  2626. {
  2627.     int dx, bwidth;
  2628.     int rows, prevRows, prevWidth;
  2629.     RECT rcCur;
  2630.     if (height<1)
  2631.         height = 1;
  2632.     rows = CountRows(ptb);
  2633.     if (height==rows || ptb->iNumButtons==0)
  2634.     {
  2635.         GetClientRect(ptb->ci.hwnd, lpRect);
  2636.         return FALSE;
  2637.     }
  2638.     bwidth = ptb->iButWidth-s_dxOverlap;
  2639.     prevRows = ptb->iNumButtons+1;
  2640.     prevWidth = bwidth;
  2641.     for (rows=height+1, dx = bwidth; rows>height;dx+=bwidth/4)
  2642.     {
  2643.         WrapToolbar(ptb, dx, &rcCur, &rows);
  2644.         if (rows<prevRows && rows>height)
  2645.         {
  2646.             prevWidth = dx;
  2647.             prevRows = rows;
  2648.         }
  2649.     }
  2650.     if (rows<height && fLarger)
  2651.     {
  2652.         WrapToolbar(ptb, prevWidth, &rcCur, NULL);
  2653.     }
  2654.     if (lpRect)
  2655.         *lpRect = rcCur;
  2656.     return TRUE;
  2657. }
  2658. int PositionFromID(PTBSTATE ptb, LONG_PTR id)
  2659. {
  2660.     int i;
  2661.     // Handle case where this is sent at the wrong time..
  2662.     if (ptb == NULL || id == -1)
  2663.         return -1;
  2664.     // note, we don't skip separators, so you better not have conflicting
  2665.     // cmd ids and separator ids.
  2666.     for (i = 0; i < ptb->iNumButtons; i++)
  2667.         if (ptb->Buttons[i].idCommand == id)
  2668.             return i;       // position found
  2669.     return -1;      // ID not found!
  2670. }
  2671. // check a radio button by button index.
  2672. // the button matching idCommand was just pressed down.  this forces
  2673. // up all other buttons in the group.
  2674. // this does not work with buttons that are forced up with
  2675. void MakeGroupConsistant(PTBSTATE ptb, int idCommand)
  2676. {
  2677.     int i, iFirst, iLast, iButton;
  2678.     int cButtons = ptb->iNumButtons;
  2679.     LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  2680.     iButton = PositionFromID(ptb, idCommand);
  2681.     if (iButton < 0)
  2682.         return;
  2683.     // assertion
  2684. //    if (!(pAllButtons[iButton].fsStyle & BTNS_CHECK))
  2685. //  return;
  2686.     // did the pressed button just go down?
  2687.     if (!(pAllButtons[iButton].fsState & TBSTATE_CHECKED))
  2688.         return;         // no, can't do anything
  2689.     // find the limits of this radio group
  2690.     // there was a bug here since win95 days -- ; there was no ; at the end of for loop
  2691.     // and if was part of it -- some apps may rely on that (reljai 6/16/98)
  2692.     for (iFirst = iButton; (iFirst > 0) && (pAllButtons[iFirst].fsStyle & BTNS_GROUP); iFirst--);
  2693.     
  2694.     if (!(pAllButtons[iFirst].fsStyle & BTNS_GROUP))
  2695.         iFirst++;
  2696.     cButtons--;
  2697.     for (iLast = iButton; (iLast < cButtons) && (pAllButtons[iLast].fsStyle & BTNS_GROUP); iLast++);
  2698.     if (!(pAllButtons[iLast].fsStyle & BTNS_GROUP))
  2699.         iLast--;
  2700.     // search for the currently down button and pop it up
  2701.     for (i = iFirst; i <= iLast; i++) {
  2702.         if (i != iButton) {
  2703.             // is this button down?
  2704.             if (pAllButtons[i].fsState & TBSTATE_CHECKED) {
  2705.                 pAllButtons[i].fsState &= ~TBSTATE_CHECKED;     // pop it up
  2706.                 TBInvalidateButton(ptb, i, TRUE);
  2707.                 break;          // only one button is down right?
  2708.             }
  2709.         }
  2710.     }
  2711. }
  2712. void DestroyStrings(PTBSTATE ptb)
  2713. {
  2714.     PTSTR *p;
  2715.     PTSTR end = 0, start = 0;
  2716.     int i;
  2717.     p = ptb->pStrings;
  2718.     for (i = 0; i < ptb->nStrings; i++) {
  2719.         if (!((*p < end) && (*p > start))) {
  2720.             start = (*p);
  2721.             end = start + (LocalSize((HANDLE)*p) / sizeof(TCHAR));
  2722.             LocalFree((HANDLE)*p);
  2723.         }
  2724.     p++;
  2725.     }
  2726.     LocalFree((HANDLE)ptb->pStrings);
  2727. }
  2728. // gets the iString from pStrings and copies it to pszText.
  2729. // returns the lstrlen.
  2730. // pszText can be null to just fetch the length.
  2731. int TBGetString(PTBSTATE ptb, int iString, int cchText, LPTSTR pszText)
  2732. {
  2733.     int iRet = -1;
  2734.     if (iString < ptb->nStrings) {
  2735.         iRet = lstrlen(ptb->pStrings[iString]);
  2736.         if (pszText) {
  2737.             lstrcpyn(pszText, ptb->pStrings[iString], cchText);
  2738.         }
  2739.     }
  2740.     return iRet;
  2741. }
  2742. #ifdef UNICODE
  2743. // gets the iString from pStrings and copies it to pszText.
  2744. // returns the lstrlen.
  2745. // pszText can be null to just fetch the length.
  2746. int TBGetStringA(PTBSTATE ptb, int iString, int cchText, LPSTR pszText)
  2747. {
  2748.     int iRet = -1;
  2749.     if (iString < ptb->nStrings) {
  2750.         iRet = lstrlenW(ptb->pStrings[iString]);
  2751.         if (pszText) {
  2752.             WideCharToMultiByte (CP_ACP, 0, ptb->pStrings[iString],
  2753.                                  -1, pszText, cchText, NULL, NULL);
  2754.         }
  2755.     }
  2756.     return iRet;
  2757. }
  2758. #endif
  2759. #define MAXSTRINGSIZE 1024
  2760. int TBAddStrings(PTBSTATE ptb, WPARAM wParam, LPARAM lParam)
  2761. {
  2762.     int i = 0,j = 0, cxMax = 0;
  2763.     LPTSTR lpsz;
  2764.     PTSTR  pString, pStringAlloc, psz;
  2765.     int numstr;
  2766.     PTSTR *pFoo;
  2767.     PTSTR *pOffset;
  2768.     TCHAR cSeparator;
  2769.     int len;
  2770.     // read the string as a resource
  2771.     if (wParam != 0) {
  2772.         pString = (PTSTR)LocalAlloc(LPTR, (MAXSTRINGSIZE * sizeof (TCHAR)));
  2773.         if (!pString)
  2774.             return -1;
  2775.         i = LoadString((HINSTANCE)wParam, LOWORD(lParam), (LPTSTR)pString, MAXSTRINGSIZE);
  2776.         if (!i) {
  2777.             LocalFree(pString);
  2778.             return -1;
  2779.         }
  2780.         // realloc string buffer to actual needed size
  2781.         psz = LocalReAlloc(pString, (i+1) * sizeof (TCHAR), LMEM_MOVEABLE);
  2782.         if (psz)
  2783.             pString = psz;
  2784.         // convert separators to '' and count number of strings
  2785.         cSeparator = *pString;
  2786. #ifndef UNICODE
  2787.         for (numstr = 0, psz = pString + 1, i--; i; i--, psz++ ) {
  2788.             if (*psz == cSeparator) {
  2789.                 if (i != 1)     // We don't want to count the second terminator as another string
  2790.                     numstr++;
  2791.                 *psz = 0;    // terminate with 0
  2792.             }
  2793.             // extra i-- if DBCS
  2794.             if (IsDBCSLeadByte(*psz))
  2795.             {
  2796.                 *(WORD *)(psz-1) = *(WORD *)psz;
  2797.                 psz++;
  2798.                 i--;
  2799.             }
  2800.             else
  2801.             {
  2802.                 // shift string to the left to overwrite separator identifier
  2803.                 *(psz - 1) = *psz;
  2804.             }
  2805.         }
  2806. #else
  2807.         for (numstr = 0, psz = pString + 1, i--; i; i--, psz++) {
  2808.             if (*psz == cSeparator) {
  2809.                 if (i != 1)     // We don't want to count the second terminator as another string
  2810.                     numstr++;
  2811.                 *psz = 0;   // terminate with 0
  2812.             }
  2813.             // shift string to the left to overwrite separator identifier
  2814.             *(psz - 1) = *psz;
  2815.         }
  2816. #endif
  2817.     }
  2818.     // read explicit string.  copy it into local memory, too.
  2819.     else {
  2820.         // Common mistake is to forget to check the return value of
  2821.         // LoadLibrary and accidentally pass wParam=NULL.
  2822.         if (IS_INTRESOURCE(lParam))
  2823.             return -1;
  2824.         // find total length and number of strings
  2825.         for (i = 0, numstr = 0, lpsz = (LPTSTR)lParam;;) {
  2826.             i++;
  2827.             if (*lpsz == 0) {
  2828.                 numstr++;
  2829.                 if (*(lpsz + 1) == 0)
  2830.                     break;
  2831.             }
  2832.             lpsz++;
  2833.         }
  2834.         pString = (PTSTR)LocalAlloc(LPTR, (i * sizeof (TCHAR)));
  2835.         if (!pString)
  2836.             return -1;
  2837.         hmemcpy(pString, (void *)lParam, i * sizeof(TCHAR));
  2838.     }
  2839.     pStringAlloc = pString;         // in case something bad happens
  2840.     // make room for increased string pointer table
  2841.     pFoo = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
  2842.             (ptb->nStrings + numstr) * sizeof(PTSTR));
  2843.     if (!pFoo) {
  2844.         goto Failure;
  2845.     }
  2846.     ptb->pStrings = pFoo;
  2847.     // pointer to next open slot in string index table.
  2848.     pOffset = ptb->pStrings + ptb->nStrings;
  2849.     for (i = 0; i < numstr; i++, pOffset++)
  2850.     {
  2851.         *pOffset = pString;
  2852.         len = lstrlen(pString);
  2853.         pString += len + 1;
  2854.     }
  2855.     // is the world big enough to handle the larger buttons?
  2856.     i = ptb->nStrings;
  2857.     ptb->nStrings += numstr;
  2858.     if (!TBRecalc(ptb))
  2859.     {
  2860.         ptb->nStrings -= numstr;
  2861.         // back out changes.
  2862.         pFoo = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
  2863.                     ptb->nStrings * sizeof(PTSTR));
  2864.         if (pFoo)
  2865.             ptb->pStrings = pFoo;
  2866.          // don't get mad if pFoo == NULL; it means the shrink failed, no big deal
  2867. Failure:
  2868.         LocalFree(pStringAlloc);
  2869.         return -1;
  2870.     }
  2871.     return i;               // index of first added string
  2872. }
  2873. void MapToStandardBitmaps(HINSTANCE *phinst, UINT_PTR *pidBM, int *pnButtons)
  2874. {
  2875.     if (*phinst == HINST_COMMCTRL) {
  2876.         *phinst = g_hinst;
  2877.         // low 2 bits are coded M(mono == ~color) L(large == ~small)
  2878.         //  0 0   -> color small
  2879.         //  0 1   -> color large
  2880.         //  ...
  2881.         //  1 1   -> mono  large
  2882.         switch (*pidBM)
  2883.         {
  2884.         case IDB_STD_SMALL_COLOR:
  2885.         case IDB_STD_LARGE_COLOR:
  2886.         case IDB_STD_SMALL_MONO:
  2887.         case IDB_STD_LARGE_MONO:
  2888.             *pidBM = IDB_STDTB_SMALL_COLOR + (*pidBM & 1);
  2889.             *pnButtons = STD_PRINT + 1;
  2890.             break;
  2891.         case IDB_HIST_SMALL_COLOR:
  2892.         case IDB_HIST_LARGE_COLOR:
  2893.         //case IDB_HIST_SMALL_MONO:
  2894.         //case IDB_HIST_LARGE_MONO:
  2895.             *pidBM = IDB_HISTTB_SMALL_COLOR + (*pidBM & 1);
  2896.             *pnButtons = HIST_LAST + 1;
  2897.             break;
  2898.         case IDB_VIEW_SMALL_COLOR:
  2899.         case IDB_VIEW_LARGE_COLOR:
  2900.         case IDB_VIEW_SMALL_MONO:
  2901.         case IDB_VIEW_LARGE_MONO:
  2902.             *pidBM = IDB_VIEWTB_SMALL_COLOR + (*pidBM & 1);
  2903.             *pnButtons = VIEW_NEWFOLDER + 1;
  2904.             break;
  2905.         }
  2906.     }
  2907. }
  2908. //
  2909. //  the PBITMAP points to the BITMAP structure that was GetObject'd from
  2910. //  the hbm, except that pbm->bmWidth and pbm->bmHeight have been adjusted
  2911. //  to represent the *desired* height and width, not the actual height
  2912. //  and width.
  2913. //
  2914. HBITMAP _CopyBitmap(PTBSTATE ptb, HBITMAP hbm, PBITMAP pbm)
  2915. {
  2916.     HBITMAP hbmCopy = NULL;
  2917.     HDC hdcWin;
  2918.     HDC hdcSrc, hdcDest;
  2919.     // Old code called CreateColorBitmap, which is bad on multimon systems
  2920.     // because it will create a bitmap that ImageList_AddMasked can't handle,
  2921.     // resulting in disabled toolbar buttons looking like crap.
  2922.     // so we have to create the bitmap copy in the same format as the source
  2923.     hdcWin = GetDC(ptb->ci.hwnd);
  2924.     hdcSrc = CreateCompatibleDC(hdcWin);
  2925.     hdcDest = CreateCompatibleDC(hdcWin);
  2926.     if (hdcWin && hdcSrc && hdcDest) {
  2927.         SelectObject(hdcSrc, hbm);
  2928.         if (pbm->bmBits) {
  2929.             // Source was a DIB section.  Create a DIB section in the same
  2930.             // color format with the same palette.
  2931.             //
  2932.             // Man, creating a DIB section is so annoying.
  2933.             struct {                    // Our private version of BITMAPINFO
  2934.                 BITMAPINFOHEADER bmiHeader;
  2935.                 RGBQUAD bmiColors[256];
  2936.             } bmi;
  2937.             UINT cBitsPixel;
  2938.             LPVOID pvDummy;
  2939.             ZeroMemory(&bmi.bmiHeader, sizeof(bmi.bmiHeader));
  2940.             bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
  2941.             bmi.bmiHeader.biWidth = pbm->bmWidth;
  2942.             bmi.bmiHeader.biHeight = pbm->bmHeight;
  2943.             bmi.bmiHeader.biPlanes = 1;
  2944.             // DIB color depths must be exactly 1, 4, 8 or 24.
  2945.             cBitsPixel = pbm->bmPlanes * pbm->bmBitsPixel;
  2946.             if (cBitsPixel <= 1)
  2947.                 bmi.bmiHeader.biBitCount = 1;
  2948.             else if (cBitsPixel <= 4)
  2949.                 bmi.bmiHeader.biBitCount = 4;
  2950.             else if (cBitsPixel <= 8)
  2951.                 bmi.bmiHeader.biBitCount = 8;
  2952.             else
  2953.                 goto CreateDDB; // ImageList_AddMasked doesn't like DIBs deeper than 8bpp
  2954.             // And get the color table too
  2955.             ASSERT(bmi.bmiHeader.biBitCount <= 8);
  2956.             bmi.bmiHeader.biClrUsed = GetDIBColorTable(hdcSrc, 0, 1 << bmi.bmiHeader.biBitCount, bmi.bmiColors);
  2957.             ASSERT(bmi.bmiHeader.biCompression == BI_RGB);
  2958.             ASSERT(bmi.bmiHeader.biSizeImage == 0);
  2959.             hbmCopy = CreateDIBSection(hdcWin, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, &pvDummy, NULL, 0);
  2960.         } else {
  2961.             // Source was a DDB.  Create a duplicate DDB.
  2962.         CreateDDB:
  2963.             // Since the caller may have dorked the bmWidth,
  2964.             // we have to recompute the bmWidthBytes, because GDI
  2965.             // gets mad if it's not exactly right, even in the bmBits == NULL
  2966.             // case.
  2967.             pbm->bmBits = NULL;
  2968.             pbm->bmWidthBytes = ((pbm->bmBitsPixel * pbm->bmWidth + 15) >> 4) << 1;
  2969.             hbmCopy = CreateBitmapIndirect(pbm);
  2970.         }
  2971.         SelectObject(hdcDest, hbmCopy);
  2972.         // fill the background
  2973.         PatB(hdcDest, 0, 0, pbm->bmWidth, pbm->bmHeight, g_clrBtnFace);
  2974.         BitBlt(hdcDest, 0, 0, pbm->bmWidth, pbm->bmHeight,
  2975.                hdcSrc, 0, 0, SRCCOPY);
  2976.     }
  2977.     if (hdcWin)
  2978.         ReleaseDC(ptb->ci.hwnd, hdcWin);
  2979.     if (hdcSrc)
  2980.         DeleteDC(hdcSrc);
  2981.     if (hdcDest)
  2982.         DeleteDC(hdcDest);
  2983.     return hbmCopy;
  2984. }
  2985. BOOL TBAddBitmapToImageList(PTBSTATE ptb, PTBBMINFO pTemp)
  2986. {
  2987.     HBITMAP hbm = NULL, hbmTemp = NULL;
  2988.     HIMAGELIST himl = TBGetImageList(ptb, HIML_NORMAL, 0);
  2989.     if (!himl) {
  2990.         himl = ImageList_Create(ptb->iDxBitmap, ptb->iDyBitmap, ILC_MASK | ILC_COLORDDB, 4, 4);
  2991.         if (!himl)
  2992.             return(FALSE);
  2993.         TBSetImageList(ptb, HIML_NORMAL, 0, himl);
  2994.         ImageList_SetBkColor(himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  2995.     }
  2996.     if (pTemp->hInst) {
  2997.         // can't use LoadImage(..., LR_MAP3DCOLORS) - more than 3 colors
  2998.         hbm = hbmTemp = CreateMappedBitmap(pTemp->hInst, pTemp->wID, CMB_DIBSECTION, NULL, 0);
  2999.     } else if (pTemp->wID) {
  3000.         hbm = (HBITMAP)pTemp->wID;
  3001.     }
  3002.     if (hbm) {
  3003.         //
  3004.         // Fix up bitmaps that aren't iDxBitmap x iDyBitmap
  3005.         //
  3006.         BITMAP bm;
  3007.         GetObject( hbm, sizeof(bm), &bm);
  3008.         if (bm.bmWidth < ptb->iDxBitmap) {
  3009.             bm.bmWidth = ptb->iDxBitmap;
  3010.         }
  3011.         if (bm.bmHeight < ptb->iDyBitmap) {
  3012.             bm.bmHeight = ptb->iDyBitmap;
  3013.         }
  3014.         // The error cases we are catching are:
  3015.         // If the pTemp->nButtons is 0 then we assume there is one button
  3016.         // If width of the bitmap is less than what it is supposed to be, we fix it.
  3017.         if (!pTemp->nButtons)
  3018.             bm.bmWidth = ptb->iDxBitmap;
  3019.         else if (pTemp->nButtons > (bm.bmWidth / ptb->iDxBitmap))
  3020.             bm.bmWidth = ptb->iDxBitmap * pTemp->nButtons;
  3021.         // Must preserve color depth to keep ImageList_AddMasked happy
  3022.         // And if we started with a DIB section, then create a DIB section.
  3023.         // (Curiously, CopyImage does not preserve DIB-ness.)
  3024.         hbm = (HBITMAP)_CopyBitmap(ptb, hbm, &bm);
  3025.     }
  3026.     // AddMasked parties on the bitmap, so we want to use a local copy
  3027.     if (hbm) {
  3028.         ImageList_AddMasked(himl, hbm, g_clrBtnFace);
  3029.         DeleteObject(hbm);
  3030.     }
  3031.     if (hbmTemp) {
  3032.         DeleteObject(hbmTemp);
  3033.     }
  3034.     return(TRUE);
  3035. }
  3036. void TBBuildImageList(PTBSTATE ptb)
  3037. {
  3038.     int i;
  3039.     PTBBMINFO pTemp;
  3040.     HIMAGELIST himl;
  3041.     ptb->fHimlValid = TRUE;
  3042.     // is the parent dealing natively with imagelists?  if so,
  3043.     // don't do this back compat building
  3044.     if (ptb->fHimlNative)
  3045.         return;
  3046.     himl = TBSetImageList(ptb, HIML_NORMAL, 0, NULL);
  3047.     ImageList_Destroy(himl);
  3048.     for (i = 0, pTemp = ptb->pBitmaps; i < ptb->nBitmaps; i++, pTemp++) {
  3049.         TBAddBitmapToImageList(ptb, pTemp);
  3050.     }
  3051. }
  3052. /* Adds a new bitmap to the list of BMs available for this toolbar.
  3053.  * Returns the index of the first button in the bitmap or -1 if there
  3054.  * was an error.
  3055.  */
  3056. int AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT_PTR idBM)
  3057. {