toolbar.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 62k
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. // these values are defined by the UI gods...
  11. #define DEFAULTBITMAPX 16
  12. #define DEFAULTBITMAPY 15
  13. #define SMALL_DXYBITMAP 16 // new dx dy for sdt images
  14. #define LARGE_DXYBITMAP 24
  15. #define DEFAULTBUTTONX 24
  16. #define DEFAULTBUTTONY 22
  17. // horizontal/vertical space taken up by button chisel, sides,
  18. // and a 1 pixel margin.  used in GrowToolbar.
  19. #define XSLOP 7
  20. #define YSLOP 6
  21. extern HBRUSH g_hbrColorDither;
  22. const int g_dxButtonSep = 8;
  23. const int s_xFirstButton = 0; // was 8 in 3.1
  24. const int s_dxOverlap = 0; // was 1 in 3.1
  25. int g_cRefToolbar = 0;
  26. // Globals - since all of these globals are used durring a paint we have to
  27. // take a criticial section around all toolbar paints.  this sucks.
  28. //
  29. HDC g_hdcGlyphs = NULL; // globals for fast drawing
  30. HDC g_hdcMono = NULL;
  31. HBITMAP g_hbmMono = NULL;
  32. HBITMAP g_hbmDefault = NULL;
  33. HDC     g_hdcButton = NULL; // contains g_hbmFace (when it exists)
  34. HBITMAP g_hbmFace   = NULL;
  35. int g_dxFace, g_dyFace; // current dimensions of g_hbmFace (2*g_dxFace)
  36. HDC g_hdcFaceCache = NULL; // used for button cache
  37. const UINT wStateMasks[] = {
  38.     TBSTATE_ENABLED,
  39.     TBSTATE_CHECKED,
  40.     TBSTATE_PRESSED,
  41.     TBSTATE_HIDDEN,
  42.     TBSTATE_INDETERMINATE
  43. };
  44. LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
  45. void NEAR PASCAL TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize);
  46. BOOL NEAR PASCAL SetBitmapSize(PTBSTATE ptb, int width, int height);
  47. int  NEAR PASCAL AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT wBMID);
  48. BOOL NEAR PASCAL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, BOOL bInside);
  49. #define HeightWithString(ptb, h) (h + ptb->dyIconFont + 1)
  50. #ifdef IEWIN31_25
  51. #define SEND_WM_COMMAND(hwnd, id, hwndCtl, codeNotify) 
  52.     SendMessage((hwnd), WM_COMMAND, (WPARAM)(int)(id), MAKELPARAM((UINT)(hwndCtl), (codeNotify)))
  53. BOOL FAR PASCAL SendItemNotify(PTBSTATE ptb, int iItem, int code)
  54. {
  55.     // for compatibility with old users of we do this bogus stuff with WM_COMMAND
  56.     // stuffing in an item index in a field that should be an hwnd
  57.     return SEND_WM_COMMAND(ptb->hwndCommand, GetWindowID(ptb->hwnd), (HWND)iItem, code);
  58. }
  59. #endif
  60. BOOL NEAR PASCAL InitGlobalObjects(void)
  61. {
  62.     g_cRefToolbar++;
  63.     if (g_cRefToolbar != 1)
  64.         return TRUE;
  65.     g_hdcGlyphs = CreateCompatibleDC(NULL);
  66.     if (!g_hdcGlyphs)
  67.         return FALSE;
  68.     SetObjectOwner(g_hdcGlyphs, HINST_THISDLL);
  69.     g_hdcMono = CreateCompatibleDC(NULL);
  70.     if (!g_hdcMono)
  71.         return FALSE;
  72.     SetObjectOwner(g_hdcMono, HINST_THISDLL);
  73.     g_hbmMono = CreateMonoBitmap(DEFAULTBUTTONX, DEFAULTBUTTONY);
  74.     if (!g_hbmMono)
  75.         return FALSE;
  76.     SetObjectOwner(g_hbmMono, HINST_THISDLL);
  77.     g_hbmDefault = SelectObject(g_hdcMono, g_hbmMono);
  78.     g_hdcButton = CreateCompatibleDC(NULL);
  79.     if (!g_hdcButton)
  80.         return FALSE;
  81.     SetObjectOwner(g_hdcButton, HINST_THISDLL);
  82.     g_hdcFaceCache = CreateCompatibleDC(NULL);
  83.     if (!g_hdcFaceCache)
  84.         return FALSE;
  85.     SetObjectOwner(g_hdcFaceCache, HINST_THISDLL);
  86.     return TRUE;
  87. }
  88. void NEAR PASCAL TBChangeFont(PTBSTATE ptb, WPARAM wParam)
  89. {
  90.     HFONT hOldFont;
  91.     LOGFONT lf;
  92.     TEXTMETRIC tm;
  93.     int i;
  94.     int newWidth;
  95.     if ((wParam != 0) && (wParam != SPI_SETICONTITLELOGFONT))
  96.         return;
  97.     ENTERCRITICAL;
  98.     // Several of these windows may get this at the same time
  99.     // so we need to process this under the critical section...
  100.     if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0))
  101.         goto Abort;
  102.     hOldFont = ptb->hfontIcon;
  103.     ptb->hfontIcon = CreateFontIndirect(&lf);
  104.     if (!ptb->hfontIcon)
  105.     {
  106.         ptb->hfontIcon = hOldFont;
  107.         goto Abort;
  108.     }
  109.     if (hOldFont)
  110.         DeleteObject(hOldFont);
  111.     hOldFont = SelectObject(g_hdcMono, ptb->hfontIcon);
  112.     GetTextMetrics(g_hdcMono, &tm);
  113.     ptb->dyIconFont = tm.tmHeight;
  114.     if (ptb->nStrings > 0)
  115.     {
  116.         SIZE size;
  117.         PSTR pstr;
  118.         // Now we need to walk through each of the strings to see
  119.         // if we need to resize the toolbar.
  120.         newWidth = ptb->iDxBitmap;
  121.         for (i=ptb->nStrings-1; i>= 0; i--)
  122.         {
  123.             pstr = ptb->pStrings[i];
  124.             GetTextExtentPoint(g_hdcMono, pstr, lstrlen(pstr), &size);
  125.             if (size.cx > newWidth)
  126.                 newWidth = size.cx;
  127.         }
  128.         GrowToolbar(ptb, newWidth, HeightWithString(ptb, ptb->iDyBitmap), TRUE);
  129.     }
  130.     if (hOldFont)
  131. SelectObject(g_hdcMono, hOldFont);
  132. Abort:
  133.     LEAVECRITICAL;
  134. }
  135. BOOL NEAR PASCAL FreeGlobalObjects(void)
  136. {
  137.     g_cRefToolbar--;
  138.     if (g_cRefToolbar != 0)
  139.         return TRUE;
  140.     if (g_hdcMono) {
  141. if (g_hbmDefault)
  142.     SelectObject(g_hdcMono, g_hbmDefault);
  143. DeleteDC(g_hdcMono); // toast the DCs
  144. g_hdcMono = NULL;
  145.     }
  146.     if (g_hdcGlyphs) {
  147. DeleteDC(g_hdcGlyphs);
  148. g_hdcGlyphs = NULL;
  149.     }
  150.     if (g_hdcFaceCache) {
  151. DeleteDC(g_hdcFaceCache);
  152. g_hdcFaceCache = NULL;
  153.     }
  154.     if (g_hdcButton) {
  155. if (g_hbmDefault)
  156.     SelectObject(g_hdcButton, g_hbmDefault);
  157. DeleteDC(g_hdcButton);
  158. g_hdcButton = NULL;
  159.     }
  160.     if (g_hbmFace) {
  161. DeleteObject(g_hbmFace);
  162. g_hbmFace = NULL;
  163.     }
  164.     if (g_hbmMono) {
  165. DeleteObject(g_hbmMono);
  166. g_hbmMono = NULL;
  167.     }
  168. }
  169. HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, UINT wID, int nBitmaps,
  170. HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons,
  171. int iNumButtons, int dxButton, int dyButton,
  172. int dxBitmap, int dyBitmap, UINT uStructSize)
  173. {
  174.     HWND hwndToolbar = CreateWindow(c_szToolbarClass, NULL, WS_CHILD | ws,
  175.       0, 0, 100, 30, hwnd, (HMENU)wID, HINST_THISDLL, NULL);
  176.     if (hwndToolbar)
  177.     {
  178.         PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwndToolbar, 0);
  179.         TBOnButtonStructSize(ptb, uStructSize);
  180.         if ((dxBitmap && dyBitmap && !SetBitmapSize(ptb, dxBitmap, dyBitmap)) ||
  181.             (dxButton && dyButton && !SetBitmapSize(ptb,dxButton, dyButton)))
  182. {
  183.     //!!!! do we actually need to deal with this?
  184.     DestroyWindow(hwndToolbar);
  185.     hwndToolbar = NULL;
  186.     goto Error;
  187. }
  188.         AddBitmap(ptb, nBitmaps, hBMInst, wBMID);
  189.         InsertButtons(ptb, (UINT)-1, iNumButtons, (LPTBBUTTON)lpButtons);
  190.         // ptb may be bogus now after above button insert
  191.     }
  192. Error:
  193.     return hwndToolbar;
  194. }
  195. /* This is no longer declared in COMMCTRL.H.  It only exists for compatibility
  196. ** with existing apps; new apps must use CreateToolbarEx.
  197. */
  198. HWND WINAPI CreateToolbar(HWND hwnd, DWORD ws, UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons, int iNumButtons)
  199. {
  200.     // old-style toolbar, so no divider.
  201.     return CreateToolbarEx(hwnd, ws | CCS_NODIVIDER, wID, nBitmaps, hBMInst, wBMID,
  202.      lpButtons, iNumButtons, 0, 0, 0, 0, sizeof(OLDTBBUTTON));
  203. }
  204. #pragma code_seg(CODESEG_INIT)
  205. BOOL FAR PASCAL InitToolbarClass(HINSTANCE hInstance)
  206. {
  207.     WNDCLASS wc;
  208.     if (!GetClassInfo(hInstance, c_szToolbarClass, &wc)) {
  209. #ifndef WIN32
  210. #ifndef IEWIN31
  211. extern LRESULT CALLBACK _ToolbarWndProc(HWND, UINT, WPARAM, LPARAM);
  212. wc.lpfnWndProc  = _ToolbarWndProc;
  213. #else
  214. wc.lpfnWndProc  = (WNDPROC)ToolbarWndProc;
  215. #endif
  216. #else
  217. wc.lpfnWndProc  = (WNDPROC)ToolbarWndProc;
  218. #endif
  219. wc.lpszClassName = c_szToolbarClass;
  220. wc.style  = CS_DBLCLKS | CS_GLOBALCLASS;
  221. wc.cbClsExtra  = 0;
  222. wc.cbWndExtra  = sizeof(PTBSTATE);
  223. wc.hInstance  = hInstance; // use DLL instance if in DLL
  224. wc.hIcon  = NULL;
  225. wc.hCursor  = LoadCursor(NULL, IDC_ARROW);
  226. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  227. wc.lpszMenuName  = NULL;
  228. if (!RegisterClass(&wc))
  229.     return FALSE;
  230.     }
  231.     return TRUE;
  232. }
  233. #pragma code_seg()
  234. #define BEVEL   2
  235. #define FRAME   1
  236. void NEAR PASCAL PatB(HDC hdc,int x,int y,int dx,int dy, DWORD rgb)
  237. {
  238.     RECT    rc;
  239.     SetBkColor(hdc,rgb);
  240.     rc.left   = x;
  241.     rc.top    = y;
  242.     rc.right  = x + dx;
  243.     rc.bottom = y + dy;
  244.     ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  245. }
  246. void NEAR PASCAL DrawString(PTBSTATE ptb, HDC hdc, int x, int y, int dx, PSTR pszString)
  247. {
  248.     int oldMode;
  249.     DWORD oldTextColor;
  250.     HFONT oldhFont;
  251.     int len;
  252.     SIZE size;
  253.     oldMode = SetBkMode(hdc, TRANSPARENT);
  254.     oldTextColor = SetTextColor(hdc, 0L);
  255.     oldhFont = SelectObject(hdc, ptb->hfontIcon);
  256.     len = lstrlen(pszString);
  257.     GetTextExtentPoint(hdc, pszString, len, &size);
  258.     // center the string horizontally
  259.     x += (dx-size.cx-1)/2;
  260.     TextOut(hdc, x, y, (LPSTR)pszString, len);
  261.     if (oldhFont)
  262. SelectObject(hdc, oldhFont);
  263.     SetTextColor(hdc, oldTextColor);
  264.     SetBkMode(hdc, oldMode);
  265. }
  266. // create a mono bitmap mask:
  267. //   1's where color == COLOR_BTNFACE || COLOR_3DHILIGHT
  268. //   0's everywhere else
  269. void NEAR PASCAL CreateMask(PTBSTATE ptb, LPTBBUTTON pTBButton, int xoffset, int yoffset, int dx, int dy)
  270. {
  271.     // initalize whole area with 1's
  272.     PatBlt(g_hdcMono, 0, 0, dx, dy, WHITENESS);
  273.     // create mask based on color bitmap
  274.     // convert this to 1's
  275.     SetBkColor(g_hdcGlyphs, g_clrBtnFace);
  276.     BitBlt(g_hdcMono, xoffset, yoffset, ptb->iDxBitmap, ptb->iDyBitmap,
  277.      g_hdcGlyphs, pTBButton->iBitmap * ptb->iDxBitmap, 0, SRCCOPY);
  278.     // convert this to 1's
  279.     SetBkColor(g_hdcGlyphs, g_clrBtnHighlight);
  280.     // OR in the new 1's
  281.     BitBlt(g_hdcMono, xoffset, yoffset, ptb->iDxBitmap, ptb->iDyBitmap,
  282.      g_hdcGlyphs, pTBButton->iBitmap * ptb->iDxBitmap, 0, SRCPAINT);
  283.     if (pTBButton->iString != -1 && (pTBButton->iString < ptb->nStrings))
  284.     {
  285. DrawString(ptb, g_hdcMono, 1, yoffset + ptb->iDyBitmap + 1, dx, ptb->pStrings[pTBButton->iString]);
  286.     }
  287. }
  288. /* Given a button number, the corresponding bitmap is loaded and selected in,
  289.  * and the Window origin set.
  290.  * Returns NULL on Error, 1 if the necessary bitmap is already selected,
  291.  * or the old bitmap otherwise.
  292.  */
  293. HBITMAP FAR PASCAL SelectBM(HDC hDC, PTBSTATE ptb, int nButton)
  294. {
  295.   PTBBMINFO pTemp;
  296.   HBITMAP hRet;
  297.   int nBitmap, nTot;
  298.   for (pTemp=ptb->pBitmaps, nBitmap=0, nTot=0; ; ++pTemp, ++nBitmap)
  299.     {
  300.       if (nBitmap >= ptb->nBitmaps)
  301.   return(NULL);
  302.       if (nButton < nTot+pTemp->nButtons)
  303.   break;
  304.       nTot += pTemp->nButtons;
  305.     }
  306.   /* Special case when the required bitmap is already selected
  307.    */
  308.   if (nBitmap == ptb->nSelectedBM)
  309.       return((HBITMAP)1);
  310.   if (!pTemp->hbm || (hRet=SelectObject(hDC, pTemp->hbm))==NULL)
  311.     {
  312.       if (pTemp->hbm)
  313.   DeleteObject(pTemp->hbm);
  314.       if (pTemp->hInst)
  315.       {
  316.   pTemp->hbm = CreateMappedBitmap(pTemp->hInst, pTemp->wID, 0, NULL, 0);
  317.       }
  318.       else
  319.   pTemp->hbm = (HBITMAP)pTemp->wID;
  320.       if (!pTemp->hbm || (hRet=SelectObject(hDC, pTemp->hbm))==NULL)
  321.   return(NULL);
  322.     }
  323.   ptb->nSelectedBM = nBitmap;
  324. #ifdef WIN32
  325.   SetWindowOrgEx(hDC, nTot * ptb->iDxBitmap, 0, NULL);
  326. #else // WIN32
  327.   SetWindowOrg(hDC, nTot * ptb->iDxBitmap, 0);
  328. #endif
  329.   return(hRet);
  330. }
  331. void FAR PASCAL DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, UINT state)
  332. {
  333.     RECT r1;
  334.     // face color
  335.     PatB(hdc, x, y, dx, dy, g_clrBtnFace);
  336.     r1.left = x;
  337.     r1.top = y;
  338.     r1.right = x + dx;
  339.     r1.bottom = y + dy;
  340.     DrawFrameControl(hdc, &r1, DFC_BUTTON, ((state & TBSTATE_PRESSED) ? (DFCS_BUTTONPUSH | DFCS_PUSHED) : DFCS_BUTTONPUSH));
  341. }
  342. #define DSPDxax  0x00E20746
  343. #define PSDPxax  0x00B8074A
  344. #define FillBkColor(hdc, prc) ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL)
  345. void NEAR PASCAL DrawFace(PTBSTATE ptb, LPTBBUTTON ptButton, HDC hdc, int x, int y,
  346. int offx, int offy, int dx)
  347. {
  348.     BitBlt(hdc, x + offx, y + offy, ptb->iDxBitmap, ptb->iDyBitmap,
  349.     g_hdcGlyphs, ptButton->iBitmap * ptb->iDxBitmap, 0, SRCCOPY);
  350.     if (ptButton->iString != -1 && (ptButton->iString < ptb->nStrings))
  351.     {
  352. DrawString(ptb, hdc, x + 1, y + offy + ptb->iDyBitmap + 1, dx, ptb->pStrings[ptButton->iString]);
  353.     }
  354. }
  355. void FAR PASCAL DrawButton(HDC hdc, int x, int y, int dx, int dy, PTBSTATE ptb, LPTBBUTTON ptButton, BOOL bFaceCache)
  356. {
  357.     int yOffset;
  358.     HBRUSH hbrOld;
  359.     BOOL bMaskCreated = FALSE;
  360.     BYTE state;
  361.     int xButton = 0; // assume button is down
  362.     int g_dxFace, g_dyFace;
  363.     int xCenterOffset;
  364.     g_dxFace = dx - 4;
  365.     g_dyFace = dy - 4;
  366.     // make local copy of state and do proper overriding
  367.     state = ptButton->fsState;
  368.     if (state & TBSTATE_INDETERMINATE) {
  369. if (state & TBSTATE_PRESSED)
  370.     state &= ~TBSTATE_INDETERMINATE;
  371. else if (state & TBSTATE_ENABLED)
  372.     state = TBSTATE_INDETERMINATE;
  373. else
  374.     state &= ~TBSTATE_INDETERMINATE;
  375.     }
  376.     // get the proper button look-- up or down.
  377.     if (!(state & (TBSTATE_PRESSED | TBSTATE_CHECKED))) {
  378. xButton = dx; // use 'up' version of button
  379.     }
  380.     if (bFaceCache)
  381. BitBlt(hdc, x, y, dx, dy, g_hdcButton, xButton, 0, SRCCOPY);
  382.     else
  383. DrawBlankButton(hdc, x, y, dx, dy, state);
  384.     // move coordinates inside border and away from upper left highlight.
  385.     // the extents change accordingly.
  386.     x += 2;
  387.     y += 2;
  388.     if (!SelectBM(g_hdcGlyphs, ptb, ptButton->iBitmap))
  389. return;
  390.     // calculate offset of face from (x,y).  y is always from the top,
  391.     // so the offset is easy.  x needs to be centered in face.
  392.     yOffset = 1;
  393.     xCenterOffset = (g_dxFace - ptb->iDxBitmap)/2;
  394.     if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
  395.     {
  396. // pressed state moves down and to the right
  397. xCenterOffset++;
  398.         yOffset++;
  399.     }
  400.     // now put on the face
  401.     if (state & TBSTATE_ENABLED) {
  402.         // regular version
  403. DrawFace(ptb, ptButton, hdc, x, y, xCenterOffset, yOffset, g_dxFace);
  404.     } else {
  405.         // disabled version (or indeterminate)
  406. bMaskCreated = TRUE;
  407. CreateMask(ptb, ptButton, xCenterOffset, yOffset, g_dxFace, g_dyFace);
  408. SetTextColor(hdc, 0L);  // 0's in mono -> 0 (for ROP)
  409. SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
  410. // draw glyph's white understrike
  411. if (!(state & TBSTATE_INDETERMINATE)) {
  412.     hbrOld = SelectObject(hdc, g_hbrBtnHighlight);
  413.     if (hbrOld) {
  414.         // draw hilight color where we have 0's in the mask
  415.                 BitBlt(hdc, x + 1, y + 1, g_dxFace, g_dyFace, g_hdcMono, 0, 0, PSDPxax);
  416.         SelectObject(hdc, hbrOld);
  417.     }
  418. }
  419. // gray out glyph
  420. hbrOld = SelectObject(hdc, g_hbrBtnShadow);
  421. if (hbrOld) {
  422.     // draw the shadow color where we have 0's in the mask
  423.             BitBlt(hdc, x, y, g_dxFace, g_dyFace, g_hdcMono, 0, 0, PSDPxax);
  424.     SelectObject(hdc, hbrOld);
  425. }
  426. if (state & TBSTATE_CHECKED) {
  427.     BitBlt(g_hdcMono, 1, 1, g_dxFace - 1, g_dyFace - 1, g_hdcMono, 0, 0, SRCAND);
  428. }
  429.     }
  430.     if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) {
  431.         LOGBRUSH lb;
  432.         // BUGBUG, after the problem with craating the dither brush is cleared
  433.         // up, move this init to image InitDitherBrush()
  434. #ifdef WIN32
  435.         HBRUSH hbrGray = (HBRUSH)DefWindowProc(ptb->hwnd, WM_CTLCOLORSCROLLBAR, (WPARAM)hdc, (LPARAM)ptb->hwnd);
  436. #else
  437.         HBRUSH hbrGray = (HBRUSH)DefWindowProc(ptb->hwnd, WM_CTLCOLOR, (WPARAM)hdc,
  438.                 MAKELPARAM(ptb->hwnd, CTLCOLOR_SCROLLBAR));
  439. #endif
  440.         GetObject(hbrGray, sizeof(lb), &lb);
  441.         if (lb.lbStyle != BS_SOLID) {
  442.             hbrGray = g_hbrColorDither;
  443.         }
  444.         hbrOld = SelectObject(hdc, hbrGray);
  445. if (hbrOld) {
  446.     if (!bMaskCreated)
  447.         CreateMask(ptb, ptButton, xCenterOffset, yOffset, g_dxFace, g_dyFace);
  448.     SetTextColor(hdc, 0L); // 0 -> 0
  449.     SetBkColor(hdc, 0x00FFFFFF); // 1 -> 1
  450.     // only draw the dither brush where the mask is 1's
  451.             BitBlt(hdc, x, y, g_dxFace, g_dyFace, g_hdcMono, 0, 0, DSPDxax);
  452.     SelectObject(hdc, hbrOld);
  453. }
  454.     }
  455. }
  456. void FAR PASCAL FlushButtonCache(PTBSTATE ptb)
  457. {
  458.     if (ptb->hbmCache) {
  459. DeleteObject(ptb->hbmCache);
  460. ptb->hbmCache = 0;
  461.     }
  462. }
  463. // make sure that g_hbmMono is big enough to do masks for this
  464. // size of button.  if not, fail.
  465. BOOL NEAR PASCAL CheckMonoMask(int width, int height)
  466. {
  467.     BITMAP bm;
  468.     HBITMAP hbmTemp;
  469.     GetObject(g_hbmMono, sizeof(BITMAP), &bm);
  470.     if (width > bm.bmWidth || height > bm.bmHeight) {
  471. hbmTemp = CreateMonoBitmap(width, height);
  472. if (!hbmTemp)
  473.     return FALSE;
  474. SelectObject(g_hdcMono, hbmTemp);
  475. DeleteObject(g_hbmMono);
  476. g_hbmMono = hbmTemp;
  477.     }
  478.     return TRUE;
  479. }
  480. /*
  481. ** GrowToolbar
  482. **
  483. ** Attempt to grow the button size.
  484. **
  485. ** The calling function can either specify a new internal measurement
  486. ** or a new external measurement.
  487. */
  488. BOOL NEAR PASCAL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, BOOL bInside)
  489. {
  490.     if (!newButWidth)
  491. newButWidth = DEFAULTBUTTONX;
  492.     if (!newButHeight)
  493. newButHeight = DEFAULTBUTTONY;
  494.     // if growing based on inside measurement, get full size
  495.     if (bInside) {
  496. newButHeight += YSLOP;
  497. newButWidth += XSLOP;
  498. // if toolbar already has strings, don't shrink width it because it
  499. // might clip room for the string
  500. if ((newButWidth < ptb->iButWidth) && ptb->nStrings)
  501.     newButWidth = ptb->iButWidth;
  502.     }
  503.     else {
  504.      if (newButHeight == -1)
  505.     newButHeight = ptb->iButHeight;
  506.      if (newButWidth == -1)
  507.     newButWidth = ptb->iButWidth;
  508.      if (newButHeight < ptb->iDyBitmap + YSLOP)
  509.     newButHeight = ptb->iDyBitmap + YSLOP;
  510.      if (newButWidth < ptb->iDxBitmap + XSLOP)
  511.     newButWidth = ptb->iDxBitmap + XSLOP;
  512.     }
  513.     // if the size of the toolbar is actually growing, see if shadow
  514.     // bitmaps can be made sufficiently large.
  515.     if ((newButWidth > ptb->iButWidth) || (newButHeight > ptb->iButHeight)) {
  516. if (!CheckMonoMask(newButWidth, newButHeight))
  517.     return(FALSE);
  518.     }
  519.     if (!bInside && (ptb->iButWidth != newButWidth) || (ptb->iButHeight != newButHeight))
  520.         InvalidateRect(ptb->hwnd, NULL, TRUE);
  521.     ptb->iButWidth = newButWidth;
  522.     ptb->iButHeight = newButHeight;
  523.     // bar height has 2 pixels above, 2 below
  524.     ptb->iYPos = 2;
  525.     FlushButtonCache(ptb);
  526.     FlushToolTipsMgr(ptb);
  527.     return TRUE;
  528. }
  529. BOOL NEAR PASCAL SetBitmapSize(PTBSTATE ptb, int width, int height)
  530. {
  531.     int realh;
  532.     if (width == -1)
  533.         width = ptb->iDxBitmap;
  534.     if (height == -1)
  535.         height = ptb->iDyBitmap;
  536.     realh = height;
  537.     if ((ptb->iDxBitmap == width) && (ptb->iDyBitmap == height))
  538. return TRUE;
  539.     if (ptb->nStrings)
  540. realh = HeightWithString(ptb, height);
  541.     if (GrowToolbar(ptb, width, realh, TRUE)) {
  542. ptb->iDxBitmap = width;
  543. ptb->iDyBitmap = height;
  544. return TRUE;
  545.     }
  546.     return FALSE;
  547. }
  548. void NEAR PASCAL TB_OnSysColorChange(PTBSTATE ptb)
  549. {
  550.     int i;
  551.     PTBBMINFO pBitmap;
  552.     ReInitGlobalColors();
  553.     ColorDitherBrush_OnSysColorChange();
  554.     //  Reset all of the bitmaps
  555.     for (i = ptb->nBitmaps - 1, pBitmap = ptb->pBitmaps; i >= 0; --i, ++pBitmap)
  556.     {
  557.      if (pBitmap->hInst && pBitmap->hbm)
  558.      {
  559.          DeleteObject(pBitmap->hbm);
  560.          pBitmap->hbm = NULL;
  561.      }
  562.     }
  563.     FlushButtonCache(ptb);
  564. }
  565. #define CACHE 0x01
  566. #define BUILD 0x02
  567. BOOL NEAR PASCAL ToolbarPaint(PTBSTATE ptb, HDC hdcIn)
  568. {
  569.     RECT rc;
  570.     HDC hdc;
  571.     PAINTSTRUCT ps;
  572.     int iButton, xButton, yButton;
  573.     PTBBUTTON pAllButtons = ptb->Buttons;
  574.     HBITMAP hbmOldGlyphs;
  575.     int xCache = 0;
  576.     UINT wFlags = 0;
  577.     int iCacheWidth = 0;
  578.     HBITMAP hbmTemp;
  579.     BOOL bFaceCache = TRUE; // assume face cache exists
  580.     BOOL bFlushToolTipsMgr = FALSE;
  581.     GetClientRect(ptb->hwnd, &rc);
  582.     if (hdcIn)
  583.     {
  584.         hdc = hdcIn;
  585.     }
  586.     else
  587.         hdc = BeginPaint(ptb->hwnd, &ps);
  588.     if (!rc.right)
  589.      goto Error1;
  590.     // setup global stuff for fast painting
  591.     // We need to kick-start the bitmap selection process.
  592.     ptb->nSelectedBM = -1;
  593.     hbmOldGlyphs = SelectBM(g_hdcGlyphs, ptb, 0);
  594.     if (!hbmOldGlyphs)
  595.      goto Error1;
  596.     yButton   = ptb->iYPos;
  597.     rc.top    = ptb->iYPos;
  598.     rc.bottom = ptb->iYPos + ptb->iButHeight;
  599.     if (!ptb->hbmCache) {
  600.      // calculate the width of the cache.
  601.      for (iButton = 0; iButton < ptb->iNumButtons; iButton++)
  602.      {
  603.          if (!(pAllButtons[iButton].fsState & TBSTATE_HIDDEN) &&
  604.           !(pAllButtons[iButton].fsStyle & TBSTYLE_SEP))
  605.           iCacheWidth += ptb->iButWidth;
  606.      }
  607.      ptb->hbmCache = CreateCompatibleBitmap(g_hdcGlyphs, iCacheWidth, ptb->iButHeight);
  608.      wFlags |= BUILD;
  609.      // if needed, create or enlarge bitmap for pre-building button states
  610.      if (!(g_hbmFace &&
  611.        (ptb->iButWidth <= g_dxFace) &&
  612.        (ptb->iButHeight <= g_dyFace)))
  613.      {
  614.          hbmTemp = CreateCompatibleBitmap(g_hdcGlyphs, 2 * ptb->iButWidth, ptb->iButHeight);
  615.          if (hbmTemp)
  616.          {
  617.           SelectObject(g_hdcButton, hbmTemp);
  618.           if (g_hbmFace)
  619.               DeleteObject(g_hbmFace);
  620.           g_hbmFace = hbmTemp;
  621.           SetObjectOwner(g_hbmFace, HINST_THISDLL);
  622.           g_dxFace = ptb->iButWidth;
  623.           g_dyFace = ptb->iButHeight;
  624.          }
  625.          else
  626.           bFaceCache = FALSE;
  627.      }
  628. bFlushToolTipsMgr = TRUE;
  629.     }
  630.     if (ptb->hbmCache)
  631.     {
  632.         SelectObject(g_hdcFaceCache, ptb->hbmCache);
  633. wFlags |= CACHE;
  634.     }
  635.     else
  636.         wFlags = 0;
  637.     if (bFaceCache)
  638.     {
  639. DrawBlankButton(g_hdcButton, 0, 0,
  640. ptb->iButWidth, ptb->iButHeight, TBSTATE_PRESSED);
  641. DrawBlankButton(g_hdcButton, ptb->iButWidth, 0,
  642. ptb->iButWidth, ptb->iButHeight, 0);
  643.     }
  644.     for (iButton = 0, xButton = s_xFirstButton;
  645. iButton < ptb->iNumButtons; iButton++)
  646.     {
  647. PTBBUTTON pButton = &pAllButtons[iButton];
  648. if (!(pButton->fsState & TBSTATE_HIDDEN))
  649. {
  650.     if (pButton->fsStyle & TBSTYLE_SEP)
  651.     {
  652.      xButton += pButton->iBitmap;
  653.     }
  654.     else
  655.     {
  656.      if (wFlags & BUILD) {
  657.                     BYTE bStateOld = pButton->fsState;
  658.                     pButton->fsState &= ~TBSTATE_PRESSED;
  659.          DrawButton(g_hdcFaceCache, xCache, 0, ptb->iButWidth, ptb->iButHeight, ptb, pButton, bFaceCache);
  660.                     pButton->fsState = bStateOld;
  661.                 }
  662. rc.left = xButton;
  663.                 rc.right = xButton + ptb->iButWidth;
  664. if (RectVisible(hdc, &rc))
  665. {
  666.     if ((wFlags & CACHE) && !(pButton->fsState & TBSTATE_PRESSED))
  667. BitBlt(hdc, xButton, yButton, ptb->iButWidth, ptb->iButHeight, g_hdcFaceCache, xCache, 0, SRCCOPY);
  668.     else
  669.      DrawButton(hdc, xButton, yButton, ptb->iButWidth, ptb->iButHeight, ptb, pButton, bFaceCache);
  670. }
  671. // advance the "pointer" in the cache
  672. xCache += ptb->iButWidth;
  673. xButton += (ptb->iButWidth - s_dxOverlap);
  674.     }
  675.     if (pButton->fsState & TBSTATE_WRAP)
  676.     {
  677.      int dy;
  678.      if (pButton->fsStyle & TBSTYLE_SEP)
  679.          dy = ptb->iButHeight + pButton->iBitmap * 2 / 3;
  680.      else
  681.          dy = ptb->iButHeight;
  682.      xButton = s_xFirstButton;
  683.      yButton   += dy;
  684.      rc.top    += dy;
  685.      rc.bottom += dy;
  686.     }
  687.         }
  688.     }
  689.     if (wFlags & CACHE)
  690.      SelectObject(g_hdcFaceCache, g_hbmDefault);
  691.     SelectObject(g_hdcGlyphs, hbmOldGlyphs);
  692. Error1:
  693.     if (hdcIn == NULL)
  694.         EndPaint(ptb->hwnd, &ps);
  695.     return bFlushToolTipsMgr;
  696. }
  697. BOOL NEAR PASCAL GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
  698. {
  699.     UINT iButton, xPos, yPos;
  700.     PTBBUTTON pButton;
  701.     if (uButton >= (UINT)ptb->iNumButtons
  702.      || (ptb->Buttons[uButton].fsState & TBSTATE_HIDDEN))
  703.     {
  704.      return FALSE;
  705.     }
  706.     xPos = s_xFirstButton;
  707.     yPos = ptb->iYPos;
  708.     for (iButton = 0, pButton = ptb->Buttons; iButton < uButton; iButton++, pButton++)
  709.     {
  710. if (!(pButton->fsState & TBSTATE_HIDDEN))
  711. {
  712.     if (pButton->fsStyle & TBSTYLE_SEP)
  713.      xPos += pButton->iBitmap;
  714.     else
  715.      xPos += (ptb->iButWidth - s_dxOverlap);
  716.     if (pButton->fsState & TBSTATE_WRAP)
  717.     {
  718.      if (pButton->fsStyle & TBSTYLE_SEP)
  719.          yPos += ptb->iButHeight + pButton->iBitmap * 2 / 3;
  720.      else
  721.          yPos += ptb->iButHeight;
  722.      xPos = s_xFirstButton;
  723.     }
  724. }
  725.     }
  726.     // pButton should now point at the required button, and xPos should be
  727.     // its left edge.  Note that we already checked if the button was hidden above
  728.     lpRect->left   = xPos;
  729.     lpRect->right  = xPos + (pButton->fsStyle & TBSTYLE_SEP ? pButton->iBitmap : ptb->iButWidth);
  730.     lpRect->top    = yPos;
  731.     lpRect->bottom = yPos + ptb->iButHeight;
  732.     return TRUE;
  733. }
  734. void NEAR PASCAL InvalidateButton(PTBSTATE ptb, PTBBUTTON pButtonToPaint)
  735. {
  736.     RECT rc;
  737.     if (GetItemRect(ptb, pButtonToPaint-ptb->Buttons, &rc))
  738.     {
  739.      InvalidateRect(ptb->hwnd, &rc, FALSE);
  740.     }
  741. }
  742. // do hit testing by sliding the origin of the supplied point
  743. //
  744. // returns:
  745. // >= 0 index of non sperator item hit
  746. // < 0 index of seperator or nearest non seperator item (area just below and to the left)
  747. //
  748. // +--------------------------------------
  749. // |   -1 -1    -1    -1
  750. // |   btn   sep   btn
  751. // |    +-----+     +-----+
  752. // |    |     |     |     |
  753. // | -1 |  0  | -1  |  2  | -3
  754. // |    |     |     |     |
  755. // |    +-----+     +-----+
  756. // |
  757. // | -1   -1 -1    -2    -3
  758. //
  759. int FAR PASCAL TBHitTest(PTBSTATE ptb, int xPos, int yPos)
  760. {
  761. int prev = 0;
  762. int last = 0;
  763. int i;
  764. RECT rc;
  765.         if (ptb->iNumButtons == 0)
  766.             return(-1);
  767. for (i=0; i<ptb->iNumButtons; i++)
  768. {
  769. if (GetItemRect(ptb, i, &rc))
  770. {
  771. if (yPos >= rc.top && yPos <= rc.bottom)
  772. {
  773. if (xPos >= rc.left && xPos <= rc.right)
  774. {
  775. if (ptb->Buttons[i].fsStyle & TBSTYLE_SEP)
  776. return - i - 1;
  777. else
  778. return i;
  779. }
  780. else
  781. {
  782. prev = i + 1;
  783. }
  784. }
  785. else
  786. {
  787. last = i;
  788. }
  789. }
  790. }
  791. if (prev)
  792. return -1 - prev;
  793. return last + 1;
  794. #if 0
  795.     PTBBUTTON pBtnT, pBtnLast;
  796.     int iButton, xOriginal;
  797.     PTBBUTTON pButton;
  798.     xPos -= s_xFirstButton;
  799.     if (xPos < 0)
  800.         return -1;
  801.     yPos -= ptb->iYPos;
  802.     if (yPos < 0)
  803.         return -1;
  804.     xOriginal = xPos;
  805.     for (iButton = 0, pButton = ptb->Buttons; iButton < ptb->iNumButtons; iButton++, pButton++)
  806.     {
  807.         if (!(pButton->fsState & TBSTATE_HIDDEN))
  808. {
  809.             if (pButton->fsStyle & TBSTYLE_SEP)
  810.         xPos -= pButton->iBitmap;
  811.             else
  812.         xPos -= (ptb->iButWidth - s_dxOverlap);
  813.          if (pButton->fsState & TBSTATE_WRAP)
  814.     {
  815.         xPos = xOriginal;
  816.         yPos -= ptb->iYPos + ptb->iButHeight;
  817.     }
  818.             if (xPos < 0)
  819.     {
  820.         if (yPos < (ptb->iYPos + ptb->iButHeight))
  821.         {
  822.             if (pButton->fsStyle & TBSTYLE_SEP)
  823.                 break;
  824.             if (yPos < ptb->iButHeight)
  825.                 return iButton;
  826.     else
  827.         break;
  828.         }
  829.             }
  830. }
  831.     }
  832.     return -1 - iButton;
  833. #endif
  834. }
  835. int NEAR PASCAL CountRows(PTBSTATE ptb)
  836. {
  837.     PTBBUTTON pButton, pBtnLast;
  838.     int rows = 1;
  839.     pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  840.     for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++) {
  841.         if (pButton->fsState & TBSTATE_WRAP) {
  842.             rows++;
  843.             if (pButton->fsStyle & TBSTYLE_SEP)
  844.                 rows++;
  845.         }
  846.     }
  847.     return rows;
  848. }
  849. /**** WrapToolbar:
  850.  * The buttons in the toolbar is layed out from left to right,
  851.  * top to bottom. If adding another button to the current row,
  852.  * while computing the layout, would cause that button to extend
  853.  * beyond the right edge or the client area, then locate a break-
  854.  * point (marked with the TBSTATE_WRAP flag). A break-point is:
  855.  *
  856.  * a) The right-most separator on the current row.
  857.  *
  858.  * b) The right-most button if there is no separator on the current row.
  859.  *
  860.  * A new row is also started at the end of any button group (sequence
  861.  * of buttons that are dlimited by separators) that are taller than
  862.  * or equal to two rows.
  863.  */
  864. void NEAR PASCAL WrapToolbar(PTBSTATE ptb, int dx, LPRECT lpRect, int FAR *pRows)
  865. {
  866. PTBBUTTON pButton, pBtnT, pBtnLast;
  867. int xPos, yPos, xMax;
  868. BOOL bFoundIt;
  869. BOOL bNextBreak = FALSE;
  870. xMax = ptb->iButWidth;
  871. xPos = s_xFirstButton;
  872. yPos = ptb->iYPos;
  873. pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  874. if (pRows)
  875. (*pRows)=1;
  876. for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++)
  877. {
  878. pButton->fsState &= ~TBSTATE_WRAP;
  879. if (!(pButton->fsState & TBSTATE_HIDDEN))
  880. {
  881. if (pButton->fsStyle & TBSTYLE_SEP)
  882. xPos += pButton->iBitmap;
  883. else
  884. xPos += (ptb->iButWidth - s_dxOverlap);
  885. // The current row exceeds the right edge. Wrap it.
  886. if (!(pButton->fsStyle&TBSTYLE_SEP) && (xPos > dx)) {
  887. for (pBtnT=pButton, bFoundIt = FALSE;
  888. pBtnT>ptb->Buttons && !(pBtnT->fsState & TBSTATE_WRAP);
  889. pBtnT--)
  890. {
  891. if ((pBtnT->fsStyle & TBSTYLE_SEP) &&
  892.  !(pBtnT->fsState & TBSTATE_HIDDEN))
  893. {
  894. pBtnT->fsState |= TBSTATE_WRAP;
  895. xPos = s_xFirstButton;
  896. yPos += pBtnT->iBitmap * 2 / 3 + ptb->iButHeight;
  897. bFoundIt = TRUE;
  898. pButton = pBtnT;
  899. bNextBreak = FALSE;
  900. if (pRows)
  901. (*pRows)++;
  902. break;
  903. }
  904. }
  905. // Did we find a separator? Force a wrap anyway!
  906. if (bFoundIt==FALSE)
  907. {
  908. pBtnT = pButton;
  909. if (pButton!=ptb->Buttons) {
  910. /* Back-up to first non-hidden button. */
  911. do {
  912. pBtnT--;
  913. } while ((pBtnT>ptb->Buttons) &&
  914. (pBtnT->fsState & TBSTATE_HIDDEN));
  915. /* Is it already wrapped? */
  916. if (pBtnT->fsState & TBSTATE_WRAP)
  917. pBtnT = pButton;
  918. }
  919. pBtnT->fsState |= TBSTATE_WRAP;
  920. xPos = s_xFirstButton;
  921. yPos += ptb->iButHeight;
  922. pButton = pBtnT;
  923. bNextBreak = TRUE;
  924. }
  925. // Count another row.
  926. if (pRows)
  927. (*pRows)++;
  928. }
  929. else
  930. {
  931. pButton->fsState &= ~TBSTATE_WRAP;
  932. if ((pButton->fsStyle&TBSTYLE_SEP) && (bNextBreak))
  933. {
  934.                                         bNextBreak = FALSE;
  935. pButton->fsState |= TBSTATE_WRAP;
  936. xPos = s_xFirstButton;
  937. yPos += ptb->iButHeight + pButton->iBitmap * 2 / 3 ;
  938. if (pRows)
  939. (*pRows)+=2;
  940. }
  941. }
  942. if (!(pButton->fsStyle&TBSTYLE_SEP))
  943. xMax = max(xPos, xMax);
  944. }
  945. }
  946. if (lpRect)
  947. {
  948. lpRect->left = 0;
  949. lpRect->right = xMax;
  950. lpRect->top = 0;
  951. lpRect->bottom = yPos + ptb->iYPos + ptb->iButHeight;
  952. }
  953. }
  954. BOOL NEAR PASCAL BoxIt(PTBSTATE ptb, int height, BOOL fLarger, LPRECT lpRect)
  955. {
  956. int dx, bwidth;
  957. int rows, prevRows, prevWidth;
  958. RECT rcCur;
  959. if (height<1)
  960. height = 1;
  961. rows = CountRows(ptb);
  962. if (height==rows || ptb->iNumButtons==0)
  963. {
  964.     GetClientRect(ptb->hwnd, lpRect);
  965.     return FALSE;
  966. }
  967.   bwidth = ptb->iButWidth-s_dxOverlap;
  968. prevRows = ptb->iNumButtons+1;
  969. prevWidth = bwidth;
  970. for (rows=height+1, dx = bwidth; rows>height;dx+=bwidth/4)
  971. {
  972. WrapToolbar(ptb, dx, &rcCur, &rows);
  973. if (rows<prevRows && rows>height)
  974. {
  975. prevWidth = dx;
  976. prevRows = rows;
  977. }
  978. }
  979. if (rows<height && fLarger)
  980. {
  981. WrapToolbar(ptb, prevWidth, &rcCur, NULL);
  982. }
  983. if (lpRect)
  984. *lpRect = rcCur;
  985. return TRUE;
  986. }
  987. int FAR PASCAL PositionFromID(PTBSTATE ptb, int id)
  988. {
  989.     int i;
  990.     // Handle case where this is sent at the wrong time..
  991.     if (ptb == NULL)
  992.         return -1;
  993.     // note, we don't skip seperators, so you better not have conflicting
  994.     // cmd ids and seperator ids.
  995.     for (i = 0; i < ptb->iNumButtons; i++)
  996.         if (ptb->Buttons[i].idCommand == id)
  997.     return i; // position found
  998.     return -1; // ID not found!
  999. }
  1000. // check a radio button by button index.
  1001. // the button matching idCommand was just pressed down.  this forces
  1002. // up all other buttons in the group.
  1003. // this does not work with buttons that are forced up with
  1004. void NEAR PASCAL MakeGroupConsistant(PTBSTATE ptb, int idCommand)
  1005. {
  1006.     int i, iFirst, iLast, iButton;
  1007.     int cButtons = ptb->iNumButtons;
  1008.     PTBBUTTON pAllButtons = ptb->Buttons;
  1009.     iButton = PositionFromID(ptb, idCommand);
  1010.     if (iButton < 0)
  1011.         return;
  1012.     // assertion
  1013. //    if (!(pAllButtons[iButton].fsStyle & TBSTYLE_CHECK))
  1014. // return;
  1015.     // did the pressed button just go down?
  1016.     if (!(pAllButtons[iButton].fsState & TBSTATE_CHECKED))
  1017.         return;         // no, can't do anything
  1018.     // find the limits of this radio group
  1019.     for (iFirst = iButton; (iFirst > 0) && (pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP); iFirst--)
  1020.         if (!(pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP))
  1021.             iFirst++;
  1022.     cButtons--;
  1023.     for (iLast = iButton; (iLast < cButtons) && (pAllButtons[iLast].fsStyle & TBSTYLE_GROUP); iLast++);
  1024.     if (!(pAllButtons[iLast].fsStyle & TBSTYLE_GROUP))
  1025.         iLast--;
  1026.     // search for the currently down button and pop it up
  1027.     for (i = iFirst; i <= iLast; i++) {
  1028.         if (i != iButton) {
  1029.             // is this button down?
  1030.             if (pAllButtons[i].fsState & TBSTATE_CHECKED) {
  1031.         pAllButtons[i].fsState &= ~TBSTATE_CHECKED;     // pop it up
  1032.                 InvalidateButton(ptb, &pAllButtons[i]);
  1033.                 break;          // only one button is down right?
  1034.             }
  1035.         }
  1036.     }
  1037. }
  1038. void NEAR PASCAL DestroyStrings(PTBSTATE ptb)
  1039. {
  1040.     PSTR *p;
  1041.     PSTR end = 0, start = 0;
  1042.     int i;
  1043.     p = ptb->pStrings;
  1044.     for (i = 0; i < ptb->nStrings; i++) {
  1045. if (!(*p < end) && (*p > start)) {
  1046.     start = (*p);
  1047.     end = start + LocalSize((HANDLE)*p);
  1048.     LocalFree((HANDLE)*p);
  1049. }
  1050. p++;
  1051.     }
  1052.     LocalFree((HANDLE)ptb->pStrings);
  1053. }
  1054. #define MAXSTRINGSIZE 1024
  1055. int NEAR PASCAL AddStrings(PTBSTATE ptb, WPARAM wParam, LPARAM lParam)
  1056. {
  1057.     int i;
  1058.     HFONT hOldFont;
  1059.     LPSTR lpsz;
  1060.     PSTR  pString, psz;
  1061.     int numstr;
  1062.     PSTR *pFoo;
  1063.     PSTR *pOffset;
  1064.     char cSeparator;
  1065.     int len;
  1066.     int newWidth;
  1067.     // read the string as a resource
  1068.     if (wParam != 0) {
  1069. pString = (PSTR)LocalAlloc(LPTR, MAXSTRINGSIZE);
  1070. if (!pString)
  1071.     return -1;
  1072. i = LoadString((HINSTANCE)wParam, LOWORD(lParam), (LPSTR)pString, MAXSTRINGSIZE);
  1073. if (!i) {
  1074.     LocalFree(pString);
  1075.     return -1;
  1076. }
  1077. // realloc string buffer to actual needed size
  1078. LocalReAlloc(pString, (i + 1) * sizeof(TCHAR), LMEM_MOVEABLE);
  1079. // convert separators to '' and count number of strings
  1080. cSeparator = *pString;
  1081. #ifdef DBCS
  1082. for (numstr = 0, psz = pString + 1, i--; i; i--, psz = AnsiNext(psz)) {
  1083.     {
  1084.       // extra i-- if DBCS
  1085.       if (AnsiPrev(pString, psz)==(psz-2))
  1086.            i--;
  1087.                 if (*psz == cSeparator) {
  1088.                     if (i != 1) // We don't want to count the second terminator as another string
  1089.          numstr++;
  1090.   *psz = 0; // terminate with 0
  1091.       }
  1092.     }
  1093. #else
  1094. for (numstr = 0, psz = pString + 1, i--; i; i--, psz++) {
  1095.     if (*psz == cSeparator) {
  1096.                 if (i != 1) // We don't want to count the second terminator as another string
  1097. numstr++;
  1098. *psz = 0; // terminate with 0
  1099.     }
  1100. #endif
  1101.     // shift string to the left to overwrite separator identifier
  1102.     *(psz - 1) = *psz;
  1103. }
  1104.     }
  1105.     // read explicit string.  copy it into local memory, too.
  1106.     else {
  1107. // find total length and number of strings
  1108. for (i = 0, numstr = 0, lpsz = (LPSTR)lParam;;) {
  1109.     i++;
  1110.     if (*lpsz == 0) {
  1111. numstr++;
  1112. if (*(lpsz + 1) == 0)
  1113.     break;
  1114.     }
  1115.     lpsz++;
  1116. }
  1117. pString = (PSTR)LocalAlloc(LPTR, i);
  1118. if (!pString)
  1119.     return -1;
  1120. hmemcpy(pString, (void FAR *)lParam, i);
  1121.     }
  1122.     // make room for increased string pointer table
  1123.     if (ptb->pStrings)
  1124. pFoo = (PSTR *)LocalReAlloc(ptb->pStrings,
  1125. (ptb->nStrings + numstr) * sizeof(PSTR), LMEM_MOVEABLE);
  1126.     else
  1127. pFoo = (PSTR *)LocalAlloc(LPTR, numstr * sizeof(PSTR));
  1128.     if (!pFoo) {
  1129. LocalFree(pString);
  1130. return -1;
  1131.     }
  1132.     ptb->pStrings = pFoo;
  1133.     // pointer to next open slot in string index table.
  1134.     pOffset = ptb->pStrings + ptb->nStrings;
  1135.     hOldFont = SelectObject(g_hdcMono, ptb->hfontIcon);
  1136.     // fix up string pointer table to deal with the new strings.
  1137.     // check if any string is big enough to necessitate a wider button.
  1138.     newWidth = ptb->iDxBitmap;
  1139.     for (i = 0; i < numstr; i++, pOffset++)
  1140.     {
  1141.         SIZE size;
  1142. *pOffset = pString;
  1143. len = lstrlen(pString);
  1144.         GetTextExtentPoint(g_hdcMono, pString, len, &size);
  1145. if (size.cx > newWidth)
  1146.     newWidth = size.cx;
  1147. pString += len + 1;
  1148.     }
  1149.     if (hOldFont)
  1150. SelectObject(g_hdcMono, hOldFont);
  1151.     // is the world big enough to handle the larger buttons?
  1152.     if (!GrowToolbar(ptb, newWidth, HeightWithString(ptb, ptb->iDyBitmap), TRUE))
  1153.     {
  1154. // back out changes.
  1155. if (ptb->nStrings == 0) {
  1156.     LocalFree(ptb->pStrings);
  1157.     ptb->pStrings = 0;
  1158. }
  1159. else
  1160.     ptb->pStrings = (PSTR *)LocalReAlloc(ptb->pStrings,
  1161.      ptb->nStrings * sizeof(PSTR), LMEM_MOVEABLE);
  1162. LocalFree(pString);
  1163. return -1;
  1164.     }
  1165.     i = ptb->nStrings;
  1166.     ptb->nStrings += numstr;
  1167.     return i; // index of first added string
  1168. }
  1169. #ifdef WIN32
  1170. void NEAR PASCAL MapToStandardBitmaps(HINSTANCE FAR *phinst, UINT FAR * pidBM, int FAR *pnButtons)
  1171. {
  1172.     if (*phinst == HINST_COMMCTRL) {
  1173.         *phinst = g_hinst;
  1174.         // low 2 bits are coded M(mono == ~color) L(large == ~small)
  1175.         //  0 0 -> color small
  1176.         //  0 1 -> color large
  1177.         //  ...
  1178.         //  1 1 -> mono  large
  1179.         switch (*pidBM)
  1180.         {
  1181.         case IDB_STD_SMALL_COLOR:
  1182.         case IDB_STD_LARGE_COLOR:
  1183.         case IDB_STD_SMALL_MONO:
  1184.         case IDB_STD_LARGE_MONO:
  1185.             *pidBM = IDB_STDTB_SMALL_COLOR + (*pidBM & 1);
  1186.             *pnButtons = STD_PRINT + 1;
  1187.             break;
  1188.         case IDB_VIEW_SMALL_COLOR:
  1189.         case IDB_VIEW_LARGE_COLOR:
  1190.         case IDB_VIEW_SMALL_MONO:
  1191.         case IDB_VIEW_LARGE_MONO:
  1192.             *pidBM = IDB_VIEWTB_SMALL_COLOR + (*pidBM & 1);
  1193.             *pnButtons = VIEW_NEWFOLDER + 1;
  1194.             break;
  1195.         }
  1196.     }
  1197. }
  1198. #endif
  1199. /* Adds a new bitmap to the list of BMs available for this toolbar.
  1200.  * Returns the index of the first button in the bitmap or -1 if there
  1201.  * was an error.
  1202.  */
  1203. int NEAR PASCAL AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT idBM)
  1204. {
  1205.   PTBBMINFO pTemp;
  1206.   int nBM, nIndex;
  1207.   // map things to the standard toolbar images
  1208. #ifdef WIN32
  1209.   if (hBMInst == HINST_COMMCTRL) // -1
  1210.   {
  1211.       // set the proper dimensions...
  1212.       if (idBM & 1)
  1213.           SetBitmapSize(ptb, LARGE_DXYBITMAP, LARGE_DXYBITMAP);
  1214.       else
  1215.           SetBitmapSize(ptb, SMALL_DXYBITMAP, SMALL_DXYBITMAP);
  1216.       MapToStandardBitmaps(&hBMInst, &idBM, &nButtons);
  1217.   }
  1218. #endif
  1219.   if (ptb->pBitmaps)
  1220.     {
  1221.       /* Check if the bitmap has already been added
  1222.        */
  1223.       for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps, nIndex=0;
  1224.     nBM>0; --nBM, ++pTemp)
  1225. {
  1226.   if (pTemp->hInst==hBMInst && pTemp->wID==idBM)
  1227.     {
  1228.       /* We already have this bitmap, but have we "registered" all
  1229.        * the buttons in it?
  1230.        */
  1231.       if (pTemp->nButtons >= nButtons)
  1232.   return(nIndex);
  1233.       if (nBM == 1)
  1234. {
  1235.   /* If this is the last bitmap, we can easily increase the
  1236.    * number of buttons without messing anything up.
  1237.    */
  1238.   pTemp->nButtons = nButtons;
  1239.   return(nIndex);
  1240. }
  1241.     }
  1242.   nIndex += pTemp->nButtons;
  1243. }
  1244.       pTemp = (PTBBMINFO)LocalReAlloc(ptb->pBitmaps,
  1245.     (ptb->nBitmaps + 1)*sizeof(TBBMINFO), LMEM_MOVEABLE);
  1246.       if (!pTemp)
  1247.   return(-1);
  1248.       ptb->pBitmaps = pTemp;
  1249.     }
  1250.   else
  1251.     {
  1252.       ptb->pBitmaps = (PTBBMINFO)LocalAlloc(LPTR, sizeof(TBBMINFO));
  1253.       if (!ptb->pBitmaps)
  1254.   return(-1);
  1255.     }
  1256.   pTemp = ptb->pBitmaps + ptb->nBitmaps;
  1257.   pTemp->hInst = hBMInst;
  1258.   pTemp->wID = idBM;
  1259.   pTemp->nButtons = nButtons;
  1260.   pTemp->hbm = NULL;
  1261.   ++ptb->nBitmaps;
  1262.   for (nButtons=0, --pTemp; pTemp>=ptb->pBitmaps; --pTemp)
  1263.       nButtons += pTemp->nButtons;
  1264.   return(nButtons);
  1265. }
  1266. BOOL NEAR PASCAL ReplaceBitmap(PTBSTATE ptb, LPTBREPLACEBITMAP lprb)
  1267. {
  1268.     int nBM;
  1269.     PTBBMINFO pTemp;
  1270. #ifdef WIN32
  1271.     int iTemp;
  1272.     MapToStandardBitmaps(&lprb->hInstOld, &lprb->nIDOld, &iTemp);
  1273.     MapToStandardBitmaps(&lprb->hInstNew, &lprb->nIDNew, &lprb->nButtons);
  1274. #endif
  1275.     for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps;
  1276.          nBM>0; --nBM, ++pTemp)
  1277.     {
  1278.         if (pTemp->hInst==lprb->hInstOld && pTemp->wID==lprb->nIDOld)
  1279.         {
  1280.             // number of buttons must match
  1281.             if (pTemp->hInst && pTemp->hbm)
  1282.                 DeleteObject(pTemp->hbm);
  1283.             pTemp->hbm = NULL;
  1284.             pTemp->hInst = lprb->hInstNew;
  1285.             pTemp->wID = lprb->nIDNew;
  1286.             pTemp->nButtons = lprb->nButtons;
  1287.             FlushButtonCache(ptb);
  1288.             return TRUE;
  1289.         }
  1290.     }
  1291.     return FALSE;
  1292. }
  1293. void FAR PASCAL FlushToolTipsMgr(PTBSTATE ptb) {
  1294.     // change all the rects for the tool tips mgr.  this is
  1295.     // cheap, and we don't do it often, so go ahead
  1296.     // and do them all.
  1297.     if(ptb->hwndToolTips) {
  1298. UINT i;
  1299. TOOLINFO ti;
  1300. PTBBUTTON pButton;
  1301.         ti.cbSize = sizeof(ti);
  1302. ti.hwnd = ptb->hwnd;
  1303.         ti.lpszText = LPSTR_TEXTCALLBACK;
  1304. for ( i = 0, pButton = ptb->Buttons;
  1305.      i < (UINT)ptb->iNumButtons;
  1306.      i++, pButton++) {
  1307.             if (!(pButton->fsStyle & TBSTYLE_SEP)) {
  1308.                 ti.uId = pButton->idCommand;
  1309.                 if (!GetItemRect(ptb, i, &ti.rect))
  1310.                     ti.rect.left = ti.rect.right = ti.rect.top = ti.rect.bottom = 0;
  1311.                 SendMessage(ptb->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  1312.             }
  1313. }
  1314.     }
  1315. }
  1316. BOOL FAR PASCAL InsertButtons(PTBSTATE ptb, UINT uWhere, UINT uButtons, LPTBBUTTON lpButtons)
  1317. {
  1318.     PTBBUTTON pIn, pOut;
  1319.     PTBSTATE ptbNew;
  1320.     // comments by chee (not the original author) so they not be
  1321.     // exactly right... be warned.
  1322.     if (!ptb || !ptb->uStructSize)
  1323. return FALSE;
  1324.     // enlarge the main structure
  1325.     ptbNew = (PTBSTATE)LocalReAlloc(ptb, sizeof(TBSTATE) - sizeof(TBBUTTON)
  1326.       + (ptb->iNumButtons + uButtons) * sizeof(TBBUTTON),
  1327. LMEM_MOVEABLE);
  1328.     if (!ptbNew)
  1329. return FALSE;
  1330. #ifdef DEBUG
  1331.     if (ptbNew != ptb)
  1332. DebugMsg(DM_TRACE, "InsertButtons caused the ptb to change!");
  1333. #endif
  1334.     ptb = ptbNew;
  1335.     SetWindowInt(ptb->hwnd, 0, (int)ptb);
  1336.     // if where points beyond the end, set it at the end
  1337.     if (uWhere > (UINT)ptb->iNumButtons)
  1338. uWhere = ptb->iNumButtons;
  1339.     // move buttons above uWhere up uButton spaces
  1340.     // the uWhere gets inverted and counts to zero..
  1341.     for (pIn=ptb->Buttons+ptb->iNumButtons-1, pOut=pIn+uButtons,
  1342.  uWhere=(UINT)ptb->iNumButtons-uWhere; uWhere>0;
  1343.  --pIn, --pOut, --uWhere)
  1344. *pOut = *pIn;
  1345.     // now do the copy.
  1346.     for (lpButtons=(LPTBBUTTON)((LPSTR)lpButtons+ptb->uStructSize*(uButtons-1)),
  1347.  ptb->iNumButtons+=(int)uButtons;  // init
  1348.  uButtons>0; //test
  1349.  --pOut, lpButtons=(LPTBBUTTON)((LPSTR)lpButtons-ptb->uStructSize), --uButtons)
  1350.     {
  1351. TBInputStruct(ptb, pOut, lpButtons);
  1352. if(ptb->hwndToolTips && !(lpButtons->fsStyle & TBSTYLE_SEP)) {
  1353.     TOOLINFO ti;
  1354.     // don't bother setting the rect because we'll do it below
  1355.     // in FlushToolTipsMgr;
  1356.             ti.cbSize = sizeof(ti);
  1357.     ti.uFlags = 0;
  1358.     ti.hwnd = ptb->hwnd;
  1359.     ti.uId = lpButtons->idCommand;
  1360.             ti.lpszText = LPSTR_TEXTCALLBACK;
  1361.     SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  1362. (LPARAM)(LPTOOLINFO)&ti);
  1363. }
  1364. if ((pOut->fsStyle & TBSTYLE_SEP) && pOut->iBitmap <=0)
  1365.     pOut->iBitmap = g_dxButtonSep;
  1366.     }
  1367.     // flush the cache
  1368.     FlushButtonCache(ptb);
  1369. // Re-compute layout if toolbar is wrapable.
  1370. if (ptb->style & TBSTYLE_WRAPABLE) {
  1371. SendMessage(ptb->hwnd, TB_AUTOSIZE, 0, 0);
  1372. }
  1373.     // We need to completely redraw the toolbar at this point.
  1374.     InvalidateRect(ptb->hwnd, NULL, TRUE);
  1375.     return(TRUE);
  1376. }
  1377. /* Notice that the state structure is not realloc'ed smaller at this
  1378.  * point.  This is a time optimization, and the fact that the structure
  1379.  * will not move is used in other places.
  1380.  */
  1381. BOOL FAR PASCAL DeleteButton(PTBSTATE ptb, UINT uIndex)
  1382. {
  1383.     PTBBUTTON pIn, pOut;
  1384.     if (uIndex >= (UINT)ptb->iNumButtons)
  1385. return FALSE;
  1386.     if (ptb->hwndToolTips) {
  1387. TOOLINFO ti;
  1388.         ti.cbSize = sizeof(ti);
  1389. ti.hwnd = ptb->hwnd;
  1390. ti.uId = ptb->Buttons[uIndex].idCommand;
  1391. SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1392.     }
  1393.     --ptb->iNumButtons;
  1394.     for (pOut=ptb->Buttons+uIndex, pIn=pOut+1;
  1395.  uIndex<(UINT)ptb->iNumButtons; ++uIndex, ++pIn, ++pOut)
  1396. *pOut = *pIn;
  1397.     // flush the cache
  1398.     FlushButtonCache(ptb);
  1399.     // We need to completely redraw the toolbar at this point.
  1400.     InvalidateRect(ptb->hwnd, NULL, TRUE);
  1401.     return TRUE;
  1402. }
  1403. // deal with old TBBUTON structs for compatibility
  1404. void FAR PASCAL TBInputStruct(PTBSTATE ptb, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
  1405. {
  1406.     if (ptb->uStructSize >= sizeof(TBBUTTON))
  1407.     {
  1408.      *pButtonInt = *pButtonExt;
  1409.     }
  1410.     else
  1411.     /* It is assumed the only other possibility is the OLDBUTTON struct */
  1412.     {
  1413.      *(LPOLDTBBUTTON)pButtonInt = *(LPOLDTBBUTTON)pButtonExt;
  1414.      /* We don't care about dwData */
  1415.      pButtonInt->dwData = 0;
  1416.      pButtonInt->iString = -1;
  1417.     }
  1418. }
  1419. void NEAR PASCAL TBOutputStruct(PTBSTATE ptb, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
  1420. {
  1421.     if (ptb->uStructSize >= sizeof(TBBUTTON))
  1422.     {
  1423.      LPSTR pOut;
  1424.      int i;
  1425.      /* Fill the part we know about and fill the rest with 0's
  1426.      */
  1427.      *pButtonExt = *pButtonInt;
  1428.      for (i = ptb->uStructSize - sizeof(TBBUTTON), pOut = (LPSTR)(pButtonExt + 1);
  1429.      i > 0; --i, ++pOut)
  1430.      {
  1431.          *pOut = 0;
  1432.      }
  1433.     }
  1434.     else
  1435.     /* It is assumed the only other possibility is the OLDBUTTON struct */
  1436.     {
  1437.      *(LPOLDTBBUTTON)pButtonExt = *(LPOLDTBBUTTON)pButtonInt;
  1438.     }
  1439. }
  1440. void NEAR PASCAL TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize)
  1441. {
  1442. /* You are not allowed to change this after adding buttons.
  1443. */
  1444. if (ptb && !ptb->iNumButtons)
  1445. {
  1446.             ptb->uStructSize = uStructSize;
  1447. }
  1448. }
  1449. LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  1450. {
  1451.     BOOL fSameButton;
  1452.     PTBBUTTON ptbButton;
  1453.     int iPos;
  1454.     BYTE fsState;
  1455.     DWORD dw;
  1456.     PTBSTATE ptb = (PTBSTATE)GetWindowInt(hWnd, 0);
  1457.     switch (wMsg) {
  1458.     case WM_NCCREATE:
  1459. #define lpcs ((LPCREATESTRUCT)lParam)
  1460.         InitDitherBrush();
  1461.         InitGlobalMetrics(0);
  1462.         InitGlobalColors();
  1463. if (!InitGlobalObjects())
  1464. {
  1465.             FreeGlobalObjects();
  1466.     return 0; // WM_NCCREATE failure is 0
  1467.         }
  1468. /* create the state data for this toolbar */
  1469. ptb = (PTBSTATE)LocalAlloc(LPTR, sizeof(TBSTATE) - sizeof(TBBUTTON));
  1470. if (!ptb)
  1471.     return 0; // WM_NCCREATE failure is 0
  1472. // note, zero init memory from above
  1473.         ptb->hwnd = hWnd;
  1474. ptb->hwndCommand = lpcs->hwndParent;
  1475. ptb->style = lpcs->style;
  1476.         ptb->hfontIcon = NULL;  // initialize to null.
  1477. ptb->uStructSize = 0;
  1478.         // Now Initialize the hfont we will use.
  1479.         TBChangeFont(ptb, 0);
  1480. // grow the button size to the appropriate girth
  1481. if (!SetBitmapSize(ptb, DEFAULTBITMAPX, DEFAULTBITMAPX))
  1482. {
  1483.     LocalFree((HLOCAL)ptb);
  1484.     return 0; // WM_NCCREATE failure is 0
  1485. }
  1486. SetWindowInt(hWnd, 0, (int)ptb);
  1487. if (!(ptb->style & (CCS_TOP | CCS_NOMOVEY | CCS_BOTTOM)))
  1488. {
  1489.     ptb->style |= CCS_TOP;
  1490.     SetWindowLong(hWnd, GWL_STYLE, ptb->style);
  1491. }
  1492. if (ptb->style & TBSTYLE_TOOLTIPS)
  1493. {
  1494.     TOOLINFO ti;
  1495.     // don't bother setting the rect because we'll do it below
  1496.     // in FlushToolTipsMgr;
  1497.             ti.cbSize = sizeof(ti);
  1498.     ti.uFlags = TTF_IDISHWND;
  1499.     ti.hwnd = hWnd;
  1500.     ti.uId = (UINT)hWnd;
  1501.             ti.lpszText = 0;
  1502.     ptb->hwndToolTips = CreateWindow(c_szSToolTipsClass, NULL,
  1503. WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  1504. hWnd, NULL, lpcs->hInstance, NULL);
  1505.     SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  1506. (LPARAM)(LPTOOLINFO)&ti);
  1507.         }
  1508. return TRUE;
  1509.     case WM_DESTROY:
  1510. if (ptb)
  1511.   {
  1512.     PTBBMINFO pTemp;
  1513.     int i;
  1514.     /* Free all the bitmaps before exiting
  1515.      */
  1516.     for (pTemp = ptb->pBitmaps, i = ptb->nBitmaps - 1; i >= 0; ++pTemp, --i)
  1517.       {
  1518. if (pTemp->hInst && pTemp->hbm)
  1519.     DeleteObject(pTemp->hbm);
  1520.       }
  1521.     FlushButtonCache(ptb);
  1522.     if (ptb->nStrings > 0)
  1523. DestroyStrings(ptb);
  1524.             if (ptb->hfontIcon)
  1525.                 DeleteObject(ptb->hfontIcon);
  1526.               if (ptb->pBitmaps)
  1527.                   LocalFree(ptb->pBitmaps);
  1528.     LocalFree((HLOCAL)ptb);
  1529.     SetWindowInt(hWnd, 0, 0);
  1530.   }
  1531. FreeGlobalObjects();
  1532.         TerminateDitherBrush();
  1533. break;
  1534.     case WM_NCCALCSIZE:
  1535.         // let defwindowproc handle the standard borders etc...
  1536. dw = DefWindowProc(hWnd, wMsg, wParam, lParam);
  1537. // add the extra edge at the top of the toolbar to seperate from the menu bar
  1538. if (!(ptb->style & CCS_NODIVIDER))
  1539. {
  1540.     ((NCCALCSIZE_PARAMS FAR *)lParam)->rgrc[0].top += g_cyEdge;
  1541. }
  1542. return dw;
  1543.     case WM_NCHITTEST:
  1544.         return HTCLIENT;
  1545.     case WM_NCACTIVATE: // BUGBUG: really needed? actiave and inactive draw the same.
  1546.     case WM_NCPAINT:
  1547. // old-style toolbars are forced to be without dividers above
  1548. if (!(ptb->style & CCS_NODIVIDER))
  1549. {
  1550.     RECT rc;
  1551.     HDC hdc = GetWindowDC(hWnd);
  1552.     GetWindowRect(hWnd, &rc);
  1553.     MapWindowRect(NULL, hWnd, &rc); // screen -> client
  1554.     rc.bottom = -rc.top; // bottom of NC area
  1555.     rc.top = rc.bottom - g_cyEdge;
  1556.     DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM);
  1557.     ReleaseDC(hWnd, hdc);
  1558. }
  1559. else
  1560.     goto DoDefault;
  1561. break;
  1562.     case WM_PRINTCLIENT:
  1563.     case WM_PAINT:
  1564. {
  1565.     BOOL bFlushToolTipsMgr;
  1566.     ENTERCRITICAL;
  1567.     // BUGUG: out bogus paint code uses lots of globals so this
  1568.     // must be atomic.  bogus hu?
  1569.     bFlushToolTipsMgr = ToolbarPaint(ptb, (HDC)wParam);
  1570.     LEAVECRITICAL;
  1571.     if (bFlushToolTipsMgr) {
  1572. FlushToolTipsMgr(ptb);
  1573.     }
  1574. }
  1575. break;
  1576.     case WM_SYSCOLORCHANGE:
  1577. TB_OnSysColorChange(ptb);
  1578. break;
  1579.     case TB_GETROWS:
  1580. return CountRows(ptb);
  1581.     case TB_SETROWS:
  1582.       {
  1583. RECT rc;
  1584. if (BoxIt(ptb, LOWORD(wParam), HIWORD(wParam), &rc))
  1585. {
  1586.             FlushToolTipsMgr(ptb);
  1587.             SetWindowPos(hWnd, NULL, 0, 0, rc.right, rc.bottom,
  1588.                          SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
  1589.             InvalidateRect(hWnd, NULL, TRUE);
  1590. }
  1591. if (lParam)
  1592.     *((RECT FAR *)lParam) = rc;
  1593.       }
  1594.       break;
  1595.     case TB_AUTOSIZE:
  1596.     case WM_SIZE:
  1597.     {
  1598. HWND hwndParent;
  1599. RECT rc;
  1600. hwndParent = GetParent(hWnd);
  1601. if (!hwndParent)
  1602.     break;
  1603.         if (ptb->style & TBSTYLE_WRAPABLE)
  1604. {
  1605.     RECT rcNew;
  1606.             if ((ptb->style & CCS_NORESIZE) || (ptb->style & CCS_NOPARENTALIGN)) {
  1607.                 GetWindowRect(hWnd, &rc);
  1608.             } else {
  1609.                 GetWindowRect(hwndParent, &rc);
  1610.             }
  1611.     WrapToolbar(ptb, rc.right - rc.left, &rcNew, NULL);
  1612.     FlushToolTipsMgr(ptb);
  1613.         }
  1614.         GetWindowRect(hWnd, &rc);
  1615.         MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 2);
  1616.         NewSize(hWnd, ptb->iButHeight * CountRows(ptb) + g_cxEdge * 2, ptb->style,
  1617.                 rc.left, rc.top, rc.right, rc.bottom);
  1618. break;
  1619.     }
  1620.     case WM_COMMAND:
  1621.     case WM_DRAWITEM:
  1622.     case WM_MEASUREITEM:
  1623.     case WM_VKEYTOITEM:
  1624.     case WM_CHARTOITEM:
  1625. SendMessage(ptb->hwndCommand, wMsg, wParam, lParam);
  1626. break;
  1627.     case WM_RBUTTONDBLCLK:
  1628.         if (!SendNotify(ptb->hwndCommand, hWnd, NM_RDBLCLK, NULL))
  1629.             goto DoDefault;
  1630.         break;
  1631.     case WM_RBUTTONUP:
  1632.         if (!SendNotify(ptb->hwndCommand, hWnd, NM_RCLICK, NULL))
  1633.             goto DoDefault;
  1634.         break;
  1635.     case WM_LBUTTONDBLCLK:
  1636. #ifndef IEWIN31_25
  1637.         iPos = TBHitTest(ptb, LOWORD(lParam), HIWORD(lParam));
  1638. if (iPos < 0 && (ptb->style & CCS_ADJUSTABLE))
  1639.   {
  1640.     iPos = -1 - iPos;
  1641.     CustomizeTB(ptb, iPos);
  1642.         } else {
  1643.             goto HandleLButtonDown;
  1644.         }
  1645. #else
  1646.     goto HandleLButtonDown;
  1647. #endif
  1648. break;
  1649.     case WM_LBUTTONDOWN:
  1650.         RelayToToolTips(ptb->hwndToolTips, hWnd, wMsg, wParam, lParam);
  1651.         iPos = TBHitTest(ptb, LOWORD(lParam), HIWORD(lParam));
  1652. #ifndef IEWIN31_25
  1653. if ((ptb->style & CCS_ADJUSTABLE) &&
  1654.  (((wParam & MK_SHIFT) && !(ptb->style & TBSTYLE_ALTDRAG)) ||
  1655.   ((GetKeyState(VK_MENU) & ~1) && (ptb->style & TBSTYLE_ALTDRAG))))
  1656. {
  1657.     MoveButton(ptb, iPos);
  1658. }
  1659. else 
  1660. #endif
  1661. {
  1662. HandleLButtonDown:
  1663.             if (iPos >= 0 && iPos < ptb->iNumButtons)
  1664.             {
  1665.                 // should this check for the size of the button struct?
  1666.                 ptbButton = ptb->Buttons + iPos;
  1667.                 ptb->pCaptureButton = ptbButton;
  1668.                 SetCapture(hWnd);
  1669.                 if (ptbButton->fsState & TBSTATE_ENABLED)
  1670.                 {
  1671.                     ptbButton->fsState |= TBSTATE_PRESSED;
  1672.                     InvalidateButton(ptb, ptbButton);
  1673.                     UpdateWindow(hWnd);         // imedeate feedback
  1674.                 }
  1675.                 SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_BEGINDRAG);
  1676.             }
  1677.         }
  1678. break;
  1679.     case WM_MOUSEMOVE:
  1680.         RelayToToolTips(ptb->hwndToolTips, hWnd, wMsg, wParam, lParam);
  1681. // if the toolbar has lost the capture for some reason, stop
  1682. if (ptb->pCaptureButton == NULL) {
  1683.             //DebugMsg(DM_TRACE, "Bail because pCaptureButton == NULL");
  1684.     break;
  1685.         }
  1686. if (hWnd != GetCapture())
  1687. {
  1688.             //DebugMsg(DM_TRACE, "cature isn't us");
  1689.     SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_ENDDRAG);
  1690.     // if the button is still pressed, unpress it.
  1691.     if (ptb->pCaptureButton->fsState & TBSTATE_PRESSED)
  1692.      SendMessage(hWnd, TB_PRESSBUTTON, ptb->pCaptureButton->idCommand, 0L);
  1693.     ptb->pCaptureButton = NULL;
  1694. }
  1695. else if (ptb->pCaptureButton->fsState & TBSTATE_ENABLED)
  1696. {
  1697.             //DebugMsg(DM_TRACE, "cature IS us, and state is enabled");
  1698.     iPos = TBHitTest(ptb, LOWORD(lParam), HIWORD(lParam));
  1699.     fSameButton = (iPos >= 0 && ptb->pCaptureButton == ptb->Buttons + iPos);
  1700.     if (fSameButton == !(ptb->pCaptureButton->fsState & TBSTATE_PRESSED))
  1701.     {
  1702.                 //DebugMsg(DM_TRACE, "cature IS us, and Button is different");
  1703.      ptb->pCaptureButton->fsState ^= TBSTATE_PRESSED;
  1704.      InvalidateButton(ptb, ptb->pCaptureButton);
  1705.     }
  1706.         }
  1707. break;
  1708.     case WM_LBUTTONUP:
  1709.         RelayToToolTips(ptb->hwndToolTips, hWnd, wMsg, wParam, lParam);
  1710. if (ptb->pCaptureButton != NULL) {
  1711.     int idCommand = ptb->pCaptureButton->idCommand;
  1712.     ReleaseCapture();
  1713.     SendItemNotify(ptb, idCommand, TBN_ENDDRAG);
  1714.     iPos = TBHitTest(ptb, LOWORD(lParam), HIWORD(lParam));
  1715.     if ((ptb->pCaptureButton->fsState & TBSTATE_ENABLED) && iPos >=0
  1716.      && (ptb->pCaptureButton == ptb->Buttons+iPos)) {
  1717.      ptb->pCaptureButton->fsState &= ~TBSTATE_PRESSED;
  1718.      if (ptb->pCaptureButton->fsStyle & TBSTYLE_CHECK) {
  1719.          if (ptb->pCaptureButton->fsStyle & TBSTYLE_GROUP) {
  1720.           // group buttons already checked can't be force
  1721.           // up by the user.
  1722.           if (ptb->pCaptureButton->fsState & TBSTATE_CHECKED) {
  1723.           ptb->pCaptureButton = NULL;
  1724.           break; // bail!
  1725.           }
  1726.           ptb->pCaptureButton->fsState |= TBSTATE_CHECKED;
  1727.           MakeGroupConsistant(ptb, idCommand);
  1728.          } else {
  1729.           ptb->pCaptureButton->fsState ^= TBSTATE_CHECKED; // toggle
  1730.          }
  1731.          // if we change a button's state, we need to flush the
  1732.          // cache
  1733.          FlushButtonCache(ptb);
  1734.      }
  1735.      InvalidateButton(ptb, ptb->pCaptureButton);
  1736.      ptb->pCaptureButton = NULL;
  1737.      FORWARD_WM_COMMAND(ptb->hwndCommand, idCommand, hWnd, BN_CLICKED, SendMessage);
  1738.     }
  1739.     else {
  1740.      ptb->pCaptureButton = NULL;
  1741.     }
  1742. }
  1743. break;
  1744.     case WM_WININICHANGE:
  1745.         InitGlobalMetrics(wParam);
  1746.         TBChangeFont(ptb, wParam);
  1747.         break;
  1748.     case WM_NOTIFY:
  1749.         switch (((LPNMHDR)lParam)->code) {
  1750.         case TTN_SHOW:
  1751.         case TTN_POP:
  1752.         case TTN_NEEDTEXT:
  1753.             SendMessage(ptb->hwndCommand, WM_NOTIFY, wParam, lParam);
  1754.             break;
  1755.         }
  1756.         break;
  1757.     case WM_STYLECHANGED:
  1758. if (wParam == GWL_STYLE)
  1759. {
  1760.     ptb->style = ((LPSTYLESTRUCT)lParam)->styleNew;
  1761.     DebugMsg(DM_TRACE, "toolbar window style changed %x", ptb->style);
  1762. }
  1763.         return 0;
  1764.     case TB_SETSTATE:
  1765. iPos = PositionFromID(ptb, (int)wParam);
  1766. if (iPos < 0)
  1767.     return FALSE;
  1768. ptbButton = ptb->Buttons + iPos;
  1769. fsState = (BYTE)(LOWORD(lParam) ^ ptbButton->fsState);
  1770.         ptbButton->fsState = (BYTE)LOWORD(lParam);
  1771. if (fsState)
  1772.     // flush the button cache
  1773.     //!!!! this could be much more intelligent
  1774.     FlushButtonCache(ptb);
  1775. if (fsState & TBSTATE_HIDDEN)
  1776.     InvalidateRect(hWnd, NULL, TRUE);
  1777. else if (fsState)
  1778.     InvalidateButton(ptb, ptbButton);
  1779.         return TRUE;
  1780.     // set the cmd ID of a button based on its position
  1781.     case TB_SETCMDID:
  1782. if (wParam >= (UINT)ptb->iNumButtons)
  1783.     return FALSE;
  1784. ptb->Buttons[wParam].idCommand = (UINT)lParam;
  1785. return TRUE;
  1786.     case TB_GETSTATE:
  1787. iPos = PositionFromID(ptb, (int)wParam);
  1788. if (iPos < 0)
  1789.     return -1L;
  1790.         return ptb->Buttons[iPos].fsState;
  1791.     case TB_ENABLEBUTTON:
  1792.     case TB_CHECKBUTTON:
  1793.     case TB_PRESSBUTTON:
  1794.     case TB_HIDEBUTTON:
  1795.     case TB_INDETERMINATE:
  1796.         iPos = PositionFromID(ptb, (int)wParam);
  1797. if (iPos < 0)
  1798.     return FALSE;
  1799.         ptbButton = &ptb->Buttons[iPos];
  1800.         fsState = ptbButton->fsState;
  1801.         if (LOWORD(lParam))
  1802.             ptbButton->fsState |= wStateMasks[wMsg - TB_ENABLEBUTTON];
  1803. else
  1804.             ptbButton->fsState &= ~wStateMasks[wMsg - TB_ENABLEBUTTON];
  1805.         // did this actually change the state?
  1806.         if (fsState != ptbButton->fsState) {
  1807.             // is this button a member of a group?
  1808.     if ((wMsg == TB_CHECKBUTTON) && (ptbButton->fsStyle & TBSTYLE_GROUP))
  1809.         MakeGroupConsistant(ptb, (int)wParam);
  1810.     // flush the button cache
  1811.     //!!!! this could be much more intelligent
  1812.     FlushButtonCache(ptb);
  1813.     if (wMsg == TB_HIDEBUTTON)
  1814. InvalidateRect(hWnd, NULL, TRUE);
  1815.     else
  1816. InvalidateButton(ptb, ptbButton);
  1817.         }
  1818.         return(TRUE);
  1819.     case TB_ISBUTTONENABLED:
  1820.     case TB_ISBUTTONCHECKED:
  1821.     case TB_ISBUTTONPRESSED:
  1822.     case TB_ISBUTTONHIDDEN:
  1823.     case TB_ISBUTTONINDETERMINATE:
  1824.         iPos = PositionFromID(ptb, (int)wParam);
  1825. if (iPos < 0)
  1826.     return(-1L);
  1827.         return (LRESULT)ptb->Buttons[iPos].fsState & wStateMasks[wMsg - TB_ISBUTTONENABLED];
  1828.     case TB_ADDBITMAP:
  1829. #ifdef WIN32
  1830.     case TB_ADDBITMAP32: // only for compatibility with mail
  1831.         #define pab ((LPTBADDBITMAP)lParam)
  1832. return AddBitmap(ptb, wParam, pab->hInst, pab->nID);
  1833. #undef pab
  1834. #else
  1835. return AddBitmap(ptb, wParam, (HINSTANCE)LOWORD(lParam), HIWORD(lParam));
  1836. #endif
  1837.     case TB_REPLACEBITMAP:
  1838.         return ReplaceBitmap(ptb, (LPTBREPLACEBITMAP)lParam);
  1839.     case TB_ADDSTRING:
  1840. return AddStrings(ptb, wParam, lParam);
  1841.     case TB_ADDBUTTONS:
  1842. return InsertButtons(ptb, (UINT)-1, wParam, (LPTBBUTTON)lParam);
  1843.     case TB_INSERTBUTTON:
  1844. return InsertButtons(ptb, wParam, 1, (LPTBBUTTON)lParam);
  1845.     case TB_DELETEBUTTON:
  1846. return DeleteButton(ptb, wParam);
  1847.     case TB_GETBUTTON:
  1848. if (wParam >= (UINT)ptb->iNumButtons)
  1849.     return(FALSE);
  1850. TBOutputStruct(ptb, ptb->Buttons + wParam, (LPTBBUTTON)lParam);
  1851. return TRUE;
  1852.     case TB_BUTTONCOUNT:
  1853. return ptb->iNumButtons;
  1854.     case TB_COMMANDTOINDEX:
  1855.         return PositionFromID(ptb, (int)wParam);
  1856. #ifndef IEWIN31_25
  1857.     case TB_SAVERESTORE:
  1858. #ifdef WIN32
  1859. #define psr ((TBSAVEPARAMS *)lParam)
  1860. return SaveRestoreFromReg(ptb, wParam, psr->hkr, psr->pszSubKey, psr->pszValueName);
  1861. #undef psr
  1862. #else
  1863. return SaveRestore(ptb, wParam, (LPSTR FAR *)lParam);
  1864. #endif
  1865.     case TB_CUSTOMIZE:
  1866. CustomizeTB(ptb, ptb->iNumButtons);
  1867. break;
  1868. #endif
  1869.     case TB_GETITEMRECT:
  1870. return GetItemRect(ptb, wParam, (LPRECT)lParam);
  1871.     case TB_BUTTONSTRUCTSIZE:
  1872.         TBOnButtonStructSize(ptb, wParam);
  1873.         break;
  1874.     case TB_SETBUTTONSIZE:
  1875. return GrowToolbar(ptb, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam), FALSE);
  1876.     case TB_SETBITMAPSIZE:
  1877. return SetBitmapSize(ptb, (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam));
  1878.     case WM_GETFONT:
  1879. return (LRESULT)(UINT)(ptb? ptb->hfontIcon : 0);
  1880.     case TB_GETTOOLTIPS:
  1881.         return (LRESULT)(UINT)ptb->hwndToolTips;
  1882.     case TB_SETTOOLTIPS:
  1883. ptb->hwndToolTips = (HWND)wParam;
  1884. break;
  1885.     case TB_SETPARENT:
  1886. {
  1887. HWND hwndOld = ptb->hwndCommand;
  1888. ptb->hwndCommand = (HWND)wParam;
  1889. return (LRESULT)(UINT)hwndOld;
  1890. }
  1891.     case TB_CHANGEBITMAP:
  1892.     {
  1893.         PTBBMINFO pTemp;
  1894.         int nBitmap;
  1895.         UINT nTot;
  1896.         iPos = PositionFromID(ptb, (int)wParam);
  1897.         if (iPos < 0)
  1898.             return(FALSE);
  1899.         //
  1900.         // Check to see if the new bitmap ID is
  1901.         // valid.
  1902.         //
  1903.         pTemp = ptb->pBitmaps;
  1904.         nTot = 0;
  1905.         for (nBitmap=0; nBitmap < ptb->nBitmaps; nBitmap++) {
  1906.             nTot += pTemp->nButtons;
  1907.             pTemp++;
  1908.         }
  1909.         if (LOWORD(lParam) >= nTot)
  1910.             return FALSE;
  1911.         ptbButton = &ptb->Buttons[iPos];
  1912.         ptbButton->iBitmap = LOWORD(lParam);
  1913.         FlushButtonCache(ptb);
  1914.         InvalidateButton(ptb, ptbButton);
  1915.         return TRUE;
  1916.     }
  1917.     case TB_GETBITMAP:
  1918.         iPos = PositionFromID(ptb, (int)wParam);
  1919.         if (iPos < 0)
  1920.             return(FALSE);
  1921.         ptbButton = &ptb->Buttons[iPos];
  1922.         return ptbButton->iBitmap;
  1923.     case TB_GETBUTTONTEXT:
  1924.         iPos = PositionFromID(ptb, (int)wParam);
  1925.         if (iPos >= 0) {
  1926.             ptbButton = &ptb->Buttons[iPos];
  1927.             if ((ptbButton->iString > -1) && (ptbButton->iString < ptb->nStrings)) {
  1928.                 if (lParam) {
  1929.                     lstrcpy((LPSTR)lParam, ptb->pStrings[ptbButton->iString]);
  1930.                 }
  1931.                 return lstrlen(ptb->pStrings[ptbButton->iString]);
  1932.             }
  1933.         }
  1934.         return -1;
  1935. #ifdef WIN32
  1936.     case TB_GETBITMAPFLAGS:
  1937.         {
  1938. DWORD fFlags = 0;
  1939. HDC hdc = GetDC(NULL);
  1940.         if (GetDeviceCaps(hdc, LOGPIXELSY) >= 120)
  1941.             fFlags |= TBBF_LARGE;
  1942.         ReleaseDC(NULL, hdc);
  1943.         return fFlags;
  1944. }
  1945. #endif
  1946.     default:
  1947. DoDefault:
  1948. return DefWindowProc(hWnd, wMsg, wParam, lParam);
  1949.     }
  1950.     return 0L;
  1951. }