Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1568 lines
40 KiB

  1. /*
  2. **
  3. ** Toolbar.c
  4. **
  5. ** This is it, the incredibly famous toolbar control. Most of
  6. ** the customization stuff is in another file.
  7. **
  8. */
  9. #include "ctlspriv.h"
  10. #include <windowsx.h>
  11. #define Reference(x) ((x)=(x))
  12. #ifndef _WIN32
  13. // we need both ansi and unicode constant strings
  14. #define SZCODE static char _based(_segname("_CODE"))
  15. #define SZCODEA static char _based(_segname("_CODE"))
  16. #endif
  17. TCHAR aszToolbarClassName[] = TOOLBARCLASSNAME;
  18. SZCODE szUSER[] = TEXT("USER.EXE");
  19. SZCODEA szDrawFrameControl[] = "DrawFrameControl";
  20. SZCODE szKernel[] = TEXT("KERNEL.EXE");
  21. SZCODEA szWriteProfileStruct[] = "WritePrivateProfileStruct";
  22. // these values are defined by the UI gods...
  23. #define DEFAULTBITMAPX 16
  24. #define DEFAULTBITMAPY 15
  25. #define DEFAULTBUTTONX 24
  26. #define DEFAULTBUTTONY 22
  27. // horizontal/vertical space taken up by button chisel, sides,
  28. // and a 1 pixel margin. used in GrowToolbar.
  29. #define XSLOP 7
  30. #define YSLOP 6
  31. #define SLOPTOP 1
  32. #define SLOPBOT 1
  33. #define SLOPLFT 8
  34. static int dxButtonSep = 8;
  35. static int xFirstButton = SLOPLFT; //!!! was 8
  36. static int iInitCount = 0;
  37. static int nSelectedBM = -1;
  38. static HDC hdcGlyphs = NULL; // globals for fast drawing
  39. static HDC hdcMono = NULL;
  40. static HBITMAP hbmMono = NULL;
  41. static HBITMAP hbmDefault = NULL;
  42. static HDC hdcButton = NULL; // contains hbmFace (when it exists)
  43. static HBITMAP hbmFace = NULL;
  44. static int dxFace, dyFace; // current dimensions of hbmFace (2*dxFace)
  45. static HDC hdcFaceCache = NULL; // used for button cache
  46. static HFONT hIconFont = NULL; // font used for strings in buttons
  47. static int yIconFont; // height of the font
  48. static BOOL g_bUseDFC = FALSE; // use DrawFrameControl, if available
  49. static BOOL g_bProfStruct = FALSE; // use PrivateProfileStruct routines
  50. static WORD g_dxOverlap = 1; // overlap between buttons
  51. static WORD wStateMasks[] = {
  52. TBSTATE_ENABLED,
  53. TBSTATE_CHECKED,
  54. TBSTATE_PRESSED,
  55. TBSTATE_HIDDEN,
  56. TBSTATE_INDETERMINATE
  57. };
  58. LRESULT CALLBACK _loadds ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
  59. #define HeightWithString(h) (h + yIconFont + 1)
  60. static BOOL NEAR PASCAL InitGlobalObjects(void)
  61. {
  62. LOGFONT lf;
  63. TEXTMETRIC tm;
  64. HFONT hOldFont;
  65. iInitCount++;
  66. if (iInitCount != 1)
  67. return TRUE;
  68. hdcGlyphs = CreateCompatibleDC(NULL);
  69. if (!hdcGlyphs)
  70. return FALSE;
  71. hdcMono = CreateCompatibleDC(NULL);
  72. if (!hdcMono)
  73. return FALSE;
  74. hbmMono = CreateBitmap(DEFAULTBUTTONX, DEFAULTBUTTONY, 1, 1, NULL);
  75. if (!hbmMono)
  76. return FALSE;
  77. hbmDefault = SelectObject(hdcMono, hbmMono);
  78. hdcButton = CreateCompatibleDC(NULL);
  79. if (!hdcButton)
  80. return FALSE;
  81. hdcFaceCache = CreateCompatibleDC(NULL);
  82. if (!hdcFaceCache)
  83. return FALSE;
  84. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0);
  85. hIconFont = CreateFontIndirect(&lf);
  86. if (!hIconFont)
  87. return FALSE;
  88. hOldFont = SelectObject(hdcMono, hIconFont);
  89. GetTextMetrics(hdcMono, &tm);
  90. yIconFont = tm.tmHeight;
  91. if (hOldFont)
  92. SelectObject(hdcMono, hOldFont);
  93. #if WINVER >= 0x0400
  94. // set a global flag to see if USER will draw for us
  95. if (GetProcAddress(LoadLibrary(szUSER), szDrawFrameControl))
  96. {
  97. g_bUseDFC = TRUE;
  98. g_dxOverlap = 0; // buttons do NOT overlap with new look
  99. }
  100. // set a global flag to see if KERNEL does profile structs
  101. if (GetProcAddress(LoadLibrary(szKernel), szWriteProfileStruct))
  102. g_bProfStruct = TRUE;
  103. #endif
  104. return TRUE;
  105. }
  106. static BOOL NEAR PASCAL FreeGlobalObjects(void)
  107. {
  108. iInitCount--;
  109. if (iInitCount != 0)
  110. return TRUE;
  111. if (hdcMono) {
  112. if (hbmDefault)
  113. SelectObject(hdcMono, hbmDefault);
  114. DeleteDC(hdcMono); // toast the DCs
  115. }
  116. hdcMono = NULL;
  117. if (hdcGlyphs)
  118. DeleteDC(hdcGlyphs);
  119. hdcGlyphs = NULL;
  120. if (hdcFaceCache)
  121. DeleteDC(hdcFaceCache);
  122. hdcFaceCache = NULL;
  123. if (hdcButton) {
  124. if (hbmDefault)
  125. SelectObject(hdcButton, hbmDefault);
  126. DeleteDC(hdcButton);
  127. }
  128. hdcButton = NULL;
  129. if (hbmFace)
  130. DeleteObject(hbmFace);
  131. hbmFace = NULL;
  132. if (hbmMono)
  133. DeleteObject(hbmMono);
  134. hbmMono = NULL;
  135. if (hIconFont)
  136. DeleteObject(hIconFont);
  137. hIconFont = NULL;
  138. }
  139. HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, UINT wID, int nBitmaps,
  140. HINSTANCE hBMInst, UINT wBMID, LPCTBBUTTON lpButtons,
  141. int iNumButtons, int dxButton, int dyButton,
  142. int dxBitmap, int dyBitmap, UINT uStructSize)
  143. {
  144. HWND hwndToolbar;
  145. hwndToolbar = CreateWindow(aszToolbarClassName, NULL, WS_CHILD | ws,
  146. 0, 0, 100, 30, hwnd, (HMENU)wID,
  147. GetWindowInstance(hwnd),NULL);
  148. if (!hwndToolbar)
  149. goto Error1;
  150. SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, uStructSize, 0L);
  151. if (dxBitmap && dyBitmap)
  152. if (!SendMessage(hwndToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(dxBitmap, dyBitmap)))
  153. {
  154. //!!!! do we actually need to deal with this?
  155. DestroyWindow(hwndToolbar);
  156. hwndToolbar = NULL;
  157. goto Error1;
  158. }
  159. if (dxButton && dyButton)
  160. if (!SendMessage(hwndToolbar, TB_SETBUTTONSIZE, 0, MAKELONG(dxButton, dyButton)))
  161. {
  162. //!!!! do we actually need to deal with this?
  163. DestroyWindow(hwndToolbar);
  164. hwndToolbar = NULL;
  165. goto Error1;
  166. }
  167. #ifdef _WIN32
  168. {
  169. TB_ADDBITMAPINFO tbai;
  170. tbai.idResource = wBMID;
  171. tbai.hBitmap = hBMInst;
  172. SendMessage(hwndToolbar, TB_ADDBITMAP, nBitmaps, (LPARAM) &tbai);
  173. }
  174. #else
  175. SendMessage(hwndToolbar, TB_ADDBITMAP, nBitmaps, MAKELONG(hBMInst, wBMID));
  176. #endif
  177. SendMessage(hwndToolbar, TB_ADDBUTTONS, iNumButtons, (LPARAM)lpButtons);
  178. Error1:
  179. return hwndToolbar;
  180. }
  181. BOOL FAR PASCAL InitToolbarClass(HINSTANCE hInstance)
  182. {
  183. WNDCLASS wc;
  184. if (!GetClassInfo(hInstance, aszToolbarClassName, &wc)) {
  185. wc.lpszClassName = aszToolbarClassName;
  186. wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
  187. wc.lpfnWndProc = (WNDPROC)ToolbarWndProc;
  188. wc.cbClsExtra = 0;
  189. wc.cbWndExtra = sizeof(PTBSTATE);
  190. wc.hInstance = hInstance;
  191. wc.hIcon = NULL;
  192. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  193. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  194. wc.lpszMenuName = NULL;
  195. if (!RegisterClass(&wc))
  196. return FALSE;
  197. }
  198. return TRUE;
  199. }
  200. #define BEVEL 2
  201. #define FRAME 1
  202. static void NEAR PASCAL PatB(HDC hdc,int x,int y,int dx,int dy, DWORD rgb)
  203. {
  204. RECT rc;
  205. SetBkColor(hdc,rgb);
  206. rc.left = x;
  207. rc.top = y;
  208. rc.right = x + dx;
  209. rc.bottom = y + dy;
  210. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  211. }
  212. static void NEAR PASCAL DrawString(HDC hdc, int x, int y, int dx, PTSTR pszString)
  213. {
  214. int oldMode;
  215. DWORD oldTextColor;
  216. HFONT oldhFont;
  217. DWORD dwExt;
  218. int len;
  219. oldMode = SetBkMode(hdc, TRANSPARENT);
  220. oldTextColor = SetTextColor(hdc, 0L);
  221. oldhFont = SelectObject(hdc, hIconFont);
  222. len = lstrlen(pszString);
  223. #ifdef _WIN32
  224. {
  225. SIZE size;
  226. GetTextExtentPoint(hdc, (LPTSTR)pszString, len, &size);
  227. dwExt = LOWORD(size.cx) | (LOWORD(size.cy) >> 8);
  228. }
  229. #else
  230. dwExt = GetTextExtent(hdc, (LPTSTR)pszString, len);
  231. #endif
  232. // center the string horizontally
  233. x += (dx - LOWORD(dwExt) - 1)/2;
  234. TextOut(hdc, x, y, (LPTSTR)pszString, len);
  235. if (oldhFont)
  236. SelectObject(hdc, oldhFont);
  237. SetTextColor(hdc, oldTextColor);
  238. SetBkMode(hdc, oldMode);
  239. }
  240. // create a mono bitmap mask:
  241. // 1's where color == COLOR_BTNFACE || COLOR_HILIGHT
  242. // 0's everywhere else
  243. static void NEAR PASCAL CreateMask(PTBSTATE pTBState, PTBBUTTON pTBButton, int xoffset, int yoffset, int dx, int dy)
  244. {
  245. PTSTR pFoo;
  246. // initalize whole area with 1's
  247. PatBlt(hdcMono, 0, 0, dx, dy, WHITENESS);
  248. // create mask based on color bitmap
  249. // convert this to 1's
  250. SetBkColor(hdcGlyphs, rgbFace);
  251. BitBlt(hdcMono, xoffset, yoffset, pTBState->iDxBitmap, pTBState->iDyBitmap,
  252. hdcGlyphs, pTBButton->iBitmap * pTBState->iDxBitmap, 0, SRCCOPY);
  253. // convert this to 1's
  254. SetBkColor(hdcGlyphs, rgbHilight);
  255. // OR in the new 1's
  256. BitBlt(hdcMono, xoffset, yoffset, pTBState->iDxBitmap, pTBState->iDyBitmap,
  257. hdcGlyphs, pTBButton->iBitmap * pTBState->iDxBitmap, 0, SRCPAINT);
  258. if (pTBButton->iString != -1 && (pTBButton->iString < pTBState->nStrings))
  259. {
  260. pFoo = pTBState->pStrings[pTBButton->iString];
  261. DrawString(hdcMono, 1, yoffset + pTBState->iDyBitmap + 1, dx, pFoo);
  262. }
  263. }
  264. /* Given a button number, the corresponding bitmap is loaded and selected in,
  265. * and the Window origin set.
  266. * Returns NULL on Error, 1 if the necessary bitmap is already selected,
  267. * or the old bitmap otherwise.
  268. */
  269. static HBITMAP FAR PASCAL SelectBM(HDC hDC, PTBSTATE pTBState, int nButton)
  270. {
  271. PTBBMINFO pTemp;
  272. HBITMAP hRet;
  273. int nBitmap, nTot;
  274. for (pTemp=pTBState->pBitmaps, nBitmap=0, nTot=0; ; ++pTemp, ++nBitmap)
  275. {
  276. if (nBitmap >= pTBState->nBitmaps)
  277. return(NULL);
  278. if (nButton < nTot+pTemp->nButtons)
  279. break;
  280. nTot += pTemp->nButtons;
  281. }
  282. /* Special case when the required bitmap is already selected
  283. */
  284. if (nBitmap == nSelectedBM)
  285. return((HBITMAP)1);
  286. if (!pTemp->hbm || (hRet=SelectObject(hDC, pTemp->hbm))==NULL)
  287. {
  288. if (pTemp->hbm)
  289. DeleteObject(pTemp->hbm);
  290. if (pTemp->hInst)
  291. pTemp->hbm = CreateMappedBitmap(pTemp->hInst, pTemp->wID,
  292. TRUE, NULL, 0);
  293. else
  294. pTemp->hbm = (HBITMAP)pTemp->wID;
  295. if (!pTemp->hbm || (hRet=SelectObject(hDC, pTemp->hbm))==NULL)
  296. return(NULL);
  297. }
  298. nSelectedBM = nBitmap;
  299. #ifdef _WIN32
  300. SetWindowOrgEx(hDC, nTot * pTBState->iDxBitmap, 0, NULL);
  301. #else // _WIN32
  302. SetWindowOrg(hDC, nTot * pTBState->iDxBitmap, 0);
  303. #endif
  304. return(hRet);
  305. }
  306. static void FAR PASCAL DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, WORD state, WORD wButtType)
  307. {
  308. #if WINVER >= 0x0400
  309. RECT r1;
  310. #endif
  311. // face color
  312. PatB(hdc, x, y, dx, dy, rgbFace);
  313. #if WINVER >= 0x0400
  314. if (g_bUseDFC)
  315. {
  316. r1.left = x;
  317. r1.top = y;
  318. r1.right = x + dx;
  319. r1.bottom = y + dy;
  320. DrawFrameControl(hdc, &r1, wButtType,
  321. (state & TBSTATE_PRESSED) ? DFCS_PUSHED : 0);
  322. }
  323. else
  324. #endif
  325. {
  326. if (state & TBSTATE_PRESSED) {
  327. PatB(hdc, x + 1, y, dx - 2, 1, rgbFrame);
  328. PatB(hdc, x + 1, y + dy - 1, dx - 2, 1, rgbFrame);
  329. PatB(hdc, x, y + 1, 1, dy - 2, rgbFrame);
  330. PatB(hdc, x + dx - 1, y +1, 1, dy - 2, rgbFrame);
  331. PatB(hdc, x + 1, y + 1, 1, dy-2, rgbShadow);
  332. PatB(hdc, x + 1, y + 1, dx-2, 1, rgbShadow);
  333. }
  334. else {
  335. PatB(hdc, x + 1, y, dx - 2, 1, rgbFrame);
  336. PatB(hdc, x + 1, y + dy - 1, dx - 2, 1, rgbFrame);
  337. PatB(hdc, x, y + 1, 1, dy - 2, rgbFrame);
  338. PatB(hdc, x + dx - 1, y + 1, 1, dy - 2, rgbFrame);
  339. dx -= 2;
  340. dy -= 2;
  341. PatB(hdc, x + 1, y + 1, 1, dy - 1, rgbHilight);
  342. PatB(hdc, x + 1, y + 1, dx - 1, 1, rgbHilight);
  343. PatB(hdc, x + dx, y + 1, 1, dy, rgbShadow);
  344. PatB(hdc, x + 1, y + dy, dx, 1, rgbShadow);
  345. PatB(hdc, x + dx - 1, y + 2, 1, dy - 2, rgbShadow);
  346. PatB(hdc, x + 2, y + dy - 1, dx - 2, 1, rgbShadow);
  347. }
  348. }
  349. }
  350. #define DSPDxax 0x00E20746
  351. #define PSDPxax 0x00B8074A
  352. #define FillBkColor(hdc, prc) ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL)
  353. static void NEAR PASCAL DrawFace(PTBSTATE pTBState, PTBBUTTON ptButton, HDC hdc, int x, int y,
  354. int offx, int offy, int dx)
  355. {
  356. PTSTR pFoo;
  357. BitBlt(hdc, x + offx, y + offy, pTBState->iDxBitmap, pTBState->iDyBitmap,
  358. hdcGlyphs, ptButton->iBitmap * pTBState->iDxBitmap, 0, SRCCOPY);
  359. if (ptButton->iString != -1 && (ptButton->iString < pTBState->nStrings))
  360. {
  361. pFoo = pTBState->pStrings[ptButton->iString];
  362. DrawString(hdc, x + 1, y + offy + pTBState->iDyBitmap + 1, dx, pFoo);
  363. }
  364. }
  365. static void FAR PASCAL DrawButton(HDC hdc, int x, int y, int dx, int dy, PTBSTATE pTBState, PTBBUTTON ptButton, BOOL bFaceCache)
  366. {
  367. int yOffset;
  368. HBRUSH hbrOld, hbr;
  369. BOOL bMaskCreated = FALSE;
  370. BYTE state;
  371. int xButton = 0; // assume button is down
  372. int dxFace, dyFace;
  373. int xCenterOffset;
  374. dxFace = dx - 4;
  375. dyFace = dy - 4;
  376. // make local copy of state and do proper overriding
  377. state = ptButton->fsState;
  378. if (state & TBSTATE_INDETERMINATE) {
  379. if (state & TBSTATE_PRESSED)
  380. state &= ~TBSTATE_INDETERMINATE;
  381. else if (state & TBSTATE_ENABLED)
  382. state = TBSTATE_INDETERMINATE;
  383. else
  384. state &= ~TBSTATE_INDETERMINATE;
  385. }
  386. // get the proper button look-- up or down.
  387. if (!(state & (TBSTATE_PRESSED | TBSTATE_CHECKED))) {
  388. xButton = dx; // use 'up' version of button
  389. }
  390. if (bFaceCache)
  391. BitBlt(hdc, x, y, dx, dy, hdcButton, xButton, 0, SRCCOPY);
  392. else
  393. DrawBlankButton(hdc, x, y, dx, dy, state, pTBState->wButtonType);
  394. // move coordinates inside border and away from upper left highlight.
  395. // the extents change accordingly.
  396. x += 2;
  397. y += 2;
  398. if (!SelectBM(hdcGlyphs, pTBState, ptButton->iBitmap))
  399. return;
  400. // calculate offset of face from (x,y). y is always from the top,
  401. // so the offset is easy. x needs to be centered in face.
  402. yOffset = 1;
  403. xCenterOffset = (dxFace - pTBState->iDxBitmap)/2;
  404. if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
  405. {
  406. // pressed state moves down and to the right
  407. xCenterOffset++;
  408. yOffset++;
  409. }
  410. // now put on the face
  411. if (state & TBSTATE_ENABLED) {
  412. // regular version
  413. DrawFace(pTBState, ptButton, hdc, x, y, xCenterOffset, yOffset, dxFace);
  414. } else {
  415. // disabled version (or indeterminate)
  416. bMaskCreated = TRUE;
  417. CreateMask(pTBState, ptButton, xCenterOffset, yOffset, dxFace, dyFace);
  418. SetTextColor(hdc, 0L); // 0's in mono -> 0 (for ROP)
  419. SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
  420. // draw glyph's white understrike
  421. if (!(state & TBSTATE_INDETERMINATE)) {
  422. hbr = CreateSolidBrush(rgbHilight);
  423. if (hbr) {
  424. hbrOld = SelectObject(hdc, hbr);
  425. if (hbrOld) {
  426. // draw hilight color where we have 0's in the mask
  427. BitBlt(hdc, x + 1, y + 1, dxFace, dyFace, hdcMono, 0, 0, PSDPxax);
  428. SelectObject(hdc, hbrOld);
  429. }
  430. DeleteObject(hbr);
  431. }
  432. }
  433. // gray out glyph
  434. hbr = CreateSolidBrush(rgbShadow);
  435. if (hbr) {
  436. hbrOld = SelectObject(hdc, hbr);
  437. if (hbrOld) {
  438. // draw the shadow color where we have 0's in the mask
  439. BitBlt(hdc, x, y, dxFace, dyFace, hdcMono, 0, 0, PSDPxax);
  440. SelectObject(hdc, hbrOld);
  441. }
  442. DeleteObject(hbr);
  443. }
  444. if (state & TBSTATE_CHECKED) {
  445. BitBlt(hdcMono, 1, 1, dxFace - 1, dyFace - 1, hdcMono, 0, 0, SRCAND);
  446. }
  447. }
  448. if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) {
  449. hbrOld = SelectObject(hdc, hbrDither);
  450. if (hbrOld) {
  451. if (!bMaskCreated)
  452. CreateMask(pTBState, ptButton, xCenterOffset, yOffset, dxFace, dyFace);
  453. SetTextColor(hdc, 0L); // 0 -> 0
  454. SetBkColor(hdc, 0x00FFFFFF); // 1 -> 1
  455. // only draw the dither brush where the mask is 1's
  456. BitBlt(hdc, x, y, dxFace, dyFace, hdcMono, 0, 0, DSPDxax);
  457. SelectObject(hdc, hbrOld);
  458. }
  459. }
  460. }
  461. static void NEAR PASCAL FlushButtonCache(PTBSTATE pTBState)
  462. {
  463. if (pTBState->hbmCache) {
  464. DeleteObject(pTBState->hbmCache);
  465. pTBState->hbmCache = 0;
  466. }
  467. }
  468. // make sure that hbmMono is big enough to do masks for this
  469. // size of button. if not, fail.
  470. static BOOL NEAR PASCAL CheckMonoMask(int width, int height)
  471. {
  472. BITMAP bm;
  473. HBITMAP hbmTemp;
  474. GetObject(hbmMono, sizeof(BITMAP), &bm);
  475. if (width > bm.bmWidth || height > bm.bmHeight) {
  476. hbmTemp = CreateBitmap(width, height, 1, 1, NULL);
  477. if (!hbmTemp)
  478. return FALSE;
  479. SelectObject(hdcMono, hbmTemp);
  480. DeleteObject(hbmMono);
  481. hbmMono = hbmTemp;
  482. }
  483. return TRUE;
  484. }
  485. /*
  486. ** GrowToolbar
  487. **
  488. ** Attempt to grow the button size.
  489. **
  490. ** The calling function can either specify a new internal measurement
  491. ** or a new external measurement.
  492. */
  493. static BOOL NEAR PASCAL GrowToolbar(PTBSTATE pTBState, int newButWidth, int newButHeight, BOOL bInside)
  494. {
  495. // if growing based on inside measurement, get full size
  496. if (bInside) {
  497. newButHeight += YSLOP;
  498. newButWidth += XSLOP;
  499. // if toolbar already has strings, don't shrink width it because it
  500. // might clip room for the string
  501. if ((newButWidth < pTBState->iButWidth) && pTBState->nStrings)
  502. newButWidth = pTBState->iButWidth;
  503. }
  504. else {
  505. if (newButHeight < pTBState->iButHeight)
  506. newButHeight = pTBState->iButHeight;
  507. if (newButWidth < pTBState->iButWidth)
  508. newButWidth = pTBState->iButWidth;
  509. }
  510. // if the size of the toolbar is actually growing, see if shadow
  511. // bitmaps can be made sufficiently large.
  512. if ((newButWidth > pTBState->iButWidth) || (newButHeight > pTBState->iButHeight)) {
  513. if (!CheckMonoMask(newButWidth, newButHeight))
  514. return(FALSE);
  515. }
  516. pTBState->iButWidth = newButWidth;
  517. pTBState->iButHeight = newButHeight;
  518. //!!!ACK ACK ACK ACK
  519. #if 0
  520. // bar height has 2 pixels above, 3 below
  521. pTBState->iBarHeight = pTBState->iButHeight + 5;
  522. pTBState->iYPos = 2;
  523. #else
  524. pTBState->iBarHeight = pTBState->iButHeight + SLOPTOP+SLOPBOT;
  525. pTBState->iYPos = SLOPTOP;
  526. #endif
  527. return TRUE;
  528. }
  529. static BOOL NEAR PASCAL SetBitmapSize(PTBSTATE pTBState, int width, int height)
  530. {
  531. int realh = height;
  532. if (pTBState->nStrings)
  533. realh = HeightWithString(height);
  534. if (GrowToolbar(pTBState, width, realh, TRUE)) {
  535. pTBState->iDxBitmap = width;
  536. pTBState->iDyBitmap = height;
  537. return TRUE;
  538. }
  539. return FALSE;
  540. }
  541. static void NEAR PASCAL UpdateTBState(PTBSTATE pTBState)
  542. {
  543. int i;
  544. PTBBMINFO pBitmap;
  545. if (pTBState->nSysColorChanges!=nSysColorChanges)
  546. {
  547. /* Reset all of the bitmaps if the sys colors have changed
  548. * since the last time the bitmaps were created.
  549. */
  550. for (i=pTBState->nBitmaps-1, pBitmap=pTBState->pBitmaps; i>=0;
  551. --i, ++pBitmap)
  552. {
  553. if (pBitmap->hInst && pBitmap->hbm)
  554. {
  555. DeleteObject(pBitmap->hbm);
  556. pBitmap->hbm = NULL;
  557. }
  558. }
  559. FlushButtonCache(pTBState);
  560. // now we're updated to latest color scheme
  561. pTBState->nSysColorChanges = nSysColorChanges;
  562. }
  563. }
  564. #define CACHE 0x01
  565. #define BUILD 0x02
  566. static void NEAR PASCAL ToolbarPaint(HWND hWnd, PTBSTATE pTBState)
  567. {
  568. RECT rc;
  569. HDC hdc;
  570. PAINTSTRUCT ps;
  571. int iButton, xButton, yButton;
  572. int cButtons = pTBState->iNumButtons;
  573. PTBBUTTON pAllButtons = pTBState->Buttons;
  574. HBITMAP hbmOldGlyphs;
  575. int xCache = 0;
  576. WORD wFlags = 0;
  577. int iCacheWidth = 0;
  578. HBITMAP hbmTemp;
  579. BOOL bFaceCache = TRUE; // assume face cache exists
  580. int dx,dy;
  581. CheckSysColors();
  582. UpdateTBState(pTBState);
  583. hdc = BeginPaint(hWnd, &ps);
  584. GetClientRect(hWnd, &rc);
  585. if (!rc.right)
  586. goto Error1;
  587. dx = pTBState->iButWidth;
  588. dy = pTBState->iButHeight;
  589. // setup global stuff for fast painting
  590. /* We need to kick-start the bitmap selection process.
  591. */
  592. nSelectedBM = -1;
  593. hbmOldGlyphs = SelectBM(hdcGlyphs, pTBState, 0);
  594. if (!hbmOldGlyphs)
  595. goto Error1;
  596. yButton = pTBState->iYPos;
  597. rc.top = yButton;
  598. rc.bottom = yButton + dy;
  599. if (!(pTBState->hbmCache)) {
  600. // calculate the width of the cache.
  601. for (iButton = 0; iButton < cButtons; iButton++) {
  602. if (!(pAllButtons[iButton].fsState & TBSTATE_HIDDEN) &&
  603. !(pAllButtons[iButton].fsStyle & TBSTYLE_SEP))
  604. iCacheWidth += pTBState->iButWidth;
  605. }
  606. pTBState->hbmCache = CreateCompatibleBitmap(hdcGlyphs, iCacheWidth, dy);
  607. wFlags |= BUILD;
  608. // if needed, create or enlarge bitmap for pre-building button states
  609. if (!(hbmFace && (dx <= dxFace) && (dy <= dyFace))) {
  610. hbmTemp = CreateCompatibleBitmap(hdcGlyphs, 2*dx, dy);
  611. if (hbmTemp) {
  612. SelectObject(hdcButton, hbmTemp);
  613. if (hbmFace)
  614. DeleteObject(hbmFace);
  615. hbmFace = hbmTemp;
  616. dxFace = dx;
  617. dyFace = dy;
  618. }
  619. else
  620. bFaceCache = FALSE;
  621. }
  622. }
  623. if (pTBState->hbmCache) {
  624. SelectObject(hdcFaceCache,pTBState->hbmCache);
  625. wFlags |= CACHE;
  626. }
  627. else
  628. wFlags = 0;
  629. if (bFaceCache) {
  630. DrawBlankButton(hdcButton, 0, 0, dx, dy, TBSTATE_PRESSED, pTBState->wButtonType);
  631. DrawBlankButton(hdcButton, dx, 0, dx, dy, 0, pTBState->wButtonType);
  632. }
  633. for (iButton = 0, xButton = xFirstButton;
  634. iButton < cButtons;
  635. iButton++) {
  636. PTBBUTTON ptbButton = &pAllButtons[iButton];
  637. if (ptbButton->fsState & TBSTATE_HIDDEN) {
  638. /* Do nothing */ ;
  639. } else if (ptbButton->fsStyle & TBSTYLE_SEP) {
  640. xButton += ptbButton->iBitmap;
  641. } else {
  642. if (wFlags & BUILD)
  643. DrawButton(hdcFaceCache, xCache, 0, dx, dy, pTBState, ptbButton, bFaceCache);
  644. rc.left = xButton;
  645. rc.right = xButton + dx;
  646. if (RectVisible(hdc, &rc)) {
  647. if ((wFlags & CACHE) && !(ptbButton->fsState & TBSTATE_PRESSED))
  648. BitBlt(hdc, xButton, yButton, dx, dy,
  649. hdcFaceCache, xCache, 0, SRCCOPY);
  650. else
  651. DrawButton(hdc, xButton, yButton, dx, dy, pTBState, ptbButton, bFaceCache);
  652. }
  653. // advance the "pointer" in the cache
  654. xCache += dx;
  655. xButton += (dx - g_dxOverlap);
  656. }
  657. }
  658. if (wFlags & CACHE)
  659. SelectObject(hdcFaceCache, hbmDefault);
  660. SelectObject(hdcGlyphs, hbmOldGlyphs);
  661. Error1:
  662. EndPaint(hWnd, &ps);
  663. }
  664. static BOOL NEAR PASCAL GetItemRect(PTBSTATE pTBState, UINT uButton, LPRECT lpRect)
  665. {
  666. UINT iButton, xPos;
  667. PTBBUTTON pButton;
  668. if (uButton>=(UINT)pTBState->iNumButtons
  669. || (pTBState->Buttons[uButton].fsState&TBSTATE_HIDDEN))
  670. {
  671. return(FALSE);
  672. }
  673. xPos = xFirstButton;
  674. for (iButton=0, pButton=pTBState->Buttons; iButton<uButton;
  675. ++iButton, ++pButton)
  676. {
  677. if (pButton->fsState & TBSTATE_HIDDEN)
  678. {
  679. /* Do nothing */ ;
  680. }
  681. else if (pButton->fsStyle & TBSTYLE_SEP)
  682. {
  683. xPos += pButton->iBitmap;
  684. }
  685. else
  686. {
  687. xPos += (pTBState->iButWidth - g_dxOverlap);
  688. }
  689. }
  690. /* pButton should now point at the required button, and xPos should be
  691. * its left edge. Note that we already checked if the button was
  692. * hidden above.
  693. */
  694. lpRect->left = xPos;
  695. lpRect->right = xPos + (pButton->fsStyle&TBSTYLE_SEP
  696. ? pButton->iBitmap : pTBState->iButWidth);
  697. lpRect->top = pTBState->iYPos;
  698. lpRect->bottom = lpRect->top + pTBState->iButHeight;
  699. return(TRUE);
  700. }
  701. static void NEAR PASCAL InvalidateButton(HWND hwnd, PTBSTATE pTBState, PTBBUTTON pButtonToPaint)
  702. {
  703. RECT rc;
  704. if (GetItemRect(pTBState, pButtonToPaint-pTBState->Buttons, &rc))
  705. {
  706. InvalidateRect(hwnd, &rc, FALSE);
  707. }
  708. }
  709. static int FAR PASCAL TBHitTest(PTBSTATE pTBState, int xPos, int yPos)
  710. {
  711. int iButton;
  712. int cButtons = pTBState->iNumButtons;
  713. PTBBUTTON pButton;
  714. xPos -= xFirstButton;
  715. if (xPos < 0)
  716. return(-1);
  717. yPos -= pTBState->iYPos;
  718. for (iButton=0, pButton=pTBState->Buttons; iButton<cButtons;
  719. ++iButton, ++pButton)
  720. {
  721. if (pButton->fsState & TBSTATE_HIDDEN)
  722. /* Do nothing */ ;
  723. else if (pButton->fsStyle & TBSTYLE_SEP)
  724. xPos -= pButton->iBitmap;
  725. else
  726. xPos -= (pTBState->iButWidth - g_dxOverlap);
  727. if (xPos < 0)
  728. {
  729. if (pButton->fsStyle&TBSTYLE_SEP
  730. || (UINT)yPos>=(UINT)pTBState->iButHeight)
  731. break;
  732. return(iButton);
  733. }
  734. }
  735. return(-1 - iButton);
  736. }
  737. static int FAR PASCAL PositionFromID(PTBSTATE pTBState, int id)
  738. {
  739. int i;
  740. int cButtons = pTBState->iNumButtons;
  741. PTBBUTTON pAllButtons = pTBState->Buttons;
  742. for (i = 0; i < cButtons; i++)
  743. if (pAllButtons[i].idCommand == id)
  744. return i; // position found
  745. return -1; // ID not found!
  746. }
  747. // check a radio button by button index.
  748. // the button matching idCommand was just pressed down. this forces
  749. // up all other buttons in the group.
  750. // this does not work with buttons that are forced up with
  751. static void NEAR PASCAL MakeGroupConsistant(HWND hWnd, PTBSTATE pTBState, int idCommand)
  752. {
  753. int i, iFirst, iLast, iButton;
  754. int cButtons = pTBState->iNumButtons;
  755. PTBBUTTON pAllButtons = pTBState->Buttons;
  756. iButton = PositionFromID(pTBState, idCommand);
  757. if (iButton < 0)
  758. return;
  759. // assertion
  760. // if (!(pAllButtons[iButton].fsStyle & TBSTYLE_CHECK))
  761. // return;
  762. // did the pressed button just go down?
  763. if (!(pAllButtons[iButton].fsState & TBSTATE_CHECKED))
  764. return; // no, can't do anything
  765. // find the limits of this radio group
  766. for (iFirst = iButton; (iFirst > 0) && (pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP); iFirst--)
  767. if (!(pAllButtons[iFirst].fsStyle & TBSTYLE_GROUP))
  768. iFirst++;
  769. cButtons--;
  770. for (iLast = iButton; (iLast < cButtons) && (pAllButtons[iLast].fsStyle & TBSTYLE_GROUP); iLast++);
  771. if (!(pAllButtons[iLast].fsStyle & TBSTYLE_GROUP))
  772. iLast--;
  773. // search for the currently down button and pop it up
  774. for (i = iFirst; i <= iLast; i++) {
  775. if (i != iButton) {
  776. // is this button down?
  777. if (pAllButtons[i].fsState & TBSTATE_CHECKED) {
  778. pAllButtons[i].fsState &= ~TBSTATE_CHECKED; // pop it up
  779. InvalidateButton(hWnd, pTBState, &pAllButtons[i]);
  780. break; // only one button is down right?
  781. }
  782. }
  783. }
  784. }
  785. static void NEAR PASCAL DestroyStrings(PTBSTATE pTBState)
  786. {
  787. PTSTR *p;
  788. PTSTR end = 0, start = 0;
  789. int i;
  790. p = pTBState->pStrings;
  791. for (i = 0; i < pTBState->nStrings; i++) {
  792. if (!(*p < end) && (*p > start)) {
  793. start = (*p);
  794. end = start + LocalSize((HANDLE)*p);
  795. LocalFree((HANDLE)*p);
  796. }
  797. p++;
  798. i++;
  799. }
  800. LocalFree((HANDLE)pTBState->pStrings);
  801. }
  802. /* Adds a new bitmap to the list of BMs available for this toolbar.
  803. * Returns the index of the first button in the bitmap or -1 if there
  804. * was an error.
  805. */
  806. static int NEAR PASCAL AddBitmap(PTBSTATE pTBState, int nButtons,
  807. HINSTANCE hBMInst, UINT wBMID)
  808. {
  809. PTBBMINFO pTemp;
  810. int nBM, nIndex;
  811. if (pTBState->pBitmaps)
  812. {
  813. /* Check if the bitmap has already been added
  814. */
  815. for (nBM=pTBState->nBitmaps, pTemp=pTBState->pBitmaps, nIndex=0;
  816. nBM>0; --nBM, ++pTemp)
  817. {
  818. if (pTemp->hInst==hBMInst && pTemp->wID==wBMID)
  819. {
  820. /* We already have this bitmap, but have we "registered" all
  821. * the buttons in it?
  822. */
  823. if (pTemp->nButtons >= nButtons)
  824. return(nIndex);
  825. if (nBM == 1)
  826. {
  827. /* If this is the last bitmap, we can easily increase the
  828. * number of buttons without messing anything up.
  829. */
  830. pTemp->nButtons = nButtons;
  831. return(nIndex);
  832. }
  833. }
  834. nIndex += pTemp->nButtons;
  835. }
  836. pTemp = (PTBBMINFO)LocalReAlloc(pTBState->pBitmaps,
  837. (pTBState->nBitmaps+1)*sizeof(TBBMINFO), LMEM_MOVEABLE);
  838. if (!pTemp)
  839. return(-1);
  840. pTBState->pBitmaps = pTemp;
  841. }
  842. else
  843. {
  844. pTBState->pBitmaps = (PTBBMINFO)LocalAlloc(LPTR, sizeof(TBBMINFO));
  845. if (!pTBState->pBitmaps)
  846. return(-1);
  847. }
  848. pTemp = pTBState->pBitmaps + pTBState->nBitmaps;
  849. pTemp->hInst = hBMInst;
  850. pTemp->wID = wBMID;
  851. pTemp->nButtons = nButtons;
  852. pTemp->hbm = NULL;
  853. ++pTBState->nBitmaps;
  854. for (nButtons=0, --pTemp; pTemp>=pTBState->pBitmaps; --pTemp)
  855. nButtons += pTemp->nButtons;
  856. return(nButtons);
  857. }
  858. static BOOL NEAR PASCAL InsertButtons(HWND hWnd, PTBSTATE pTBState,
  859. UINT uWhere, UINT uButtons, LPTBBUTTON lpButtons)
  860. {
  861. PTBBUTTON pIn, pOut;
  862. if (!pTBState || !pTBState->uStructSize)
  863. return(FALSE);
  864. pTBState = (PTBSTATE)LocalReAlloc(pTBState, sizeof(TBSTATE)-sizeof(TBBUTTON)
  865. + (pTBState->iNumButtons+uButtons)*sizeof(TBBUTTON), LMEM_MOVEABLE);
  866. if (!pTBState)
  867. return(FALSE);
  868. SETWINDOWPOINTER(hWnd, PTBSTATE, pTBState);
  869. if (uWhere > (UINT)pTBState->iNumButtons)
  870. uWhere = pTBState->iNumButtons;
  871. for (pIn=pTBState->Buttons+pTBState->iNumButtons-1, pOut=pIn+uButtons,
  872. uWhere=(UINT)pTBState->iNumButtons-uWhere; uWhere>0;
  873. --pIn, --pOut, --uWhere)
  874. *pOut = *pIn;
  875. for (lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons+pTBState->uStructSize*(uButtons-1)), pTBState->iNumButtons+=(int)uButtons; uButtons>0;
  876. --pOut, lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons-pTBState->uStructSize), --uButtons)
  877. {
  878. TBInputStruct(pTBState, pOut, lpButtons);
  879. if ((pOut->fsStyle&TBSTYLE_SEP) && pOut->iBitmap<=0)
  880. pOut->iBitmap = dxButtonSep;
  881. }
  882. // flush the cache
  883. FlushButtonCache(pTBState);
  884. /* We need to completely redraw the toolbar at this point.
  885. */
  886. InvalidateRect(hWnd, NULL, TRUE);
  887. return(TRUE);
  888. }
  889. /* Notice that the state structure is not realloc'ed smaller at this
  890. * point. This is a time optimization, and the fact that the structure
  891. * will not move is used in other places.
  892. */
  893. static BOOL NEAR PASCAL DeleteButton(HWND hWnd, PTBSTATE pTBState, UINT uIndex)
  894. {
  895. PTBBUTTON pIn, pOut;
  896. if (uIndex >= (UINT)pTBState->iNumButtons)
  897. return(FALSE);
  898. --pTBState->iNumButtons;
  899. for (pOut=pTBState->Buttons+uIndex, pIn=pOut+1;
  900. uIndex<(UINT)pTBState->iNumButtons; ++uIndex, ++pIn, ++pOut)
  901. *pOut = *pIn;
  902. // flush the cache
  903. FlushButtonCache(pTBState);
  904. /* We need to completely redraw the toolbar at this point.
  905. */
  906. InvalidateRect(hWnd, NULL, TRUE);
  907. return(TRUE);
  908. }
  909. static void FAR PASCAL TBInputStruct(PTBSTATE pTBState, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
  910. {
  911. if (pTBState->uStructSize >= sizeof(TBBUTTON))
  912. {
  913. *pButtonInt = *pButtonExt;
  914. }
  915. else
  916. /* It is assumed the only other possibility is the OLDBUTTON struct */
  917. {
  918. *(LPOLDTBBUTTON)pButtonInt = *(LPOLDTBBUTTON)pButtonExt;
  919. /* We don't care about dwData */
  920. pButtonInt->iString = -1;
  921. }
  922. }
  923. static void FAR PASCAL TBOutputStruct(PTBSTATE pTBState, LPTBBUTTON pButtonInt, LPTBBUTTON pButtonExt)
  924. {
  925. if (pTBState->uStructSize >= sizeof(TBBUTTON))
  926. {
  927. LPBYTE pOut;
  928. int i;
  929. /* Fill the part we know about and fill the rest with 0's
  930. */
  931. *pButtonExt = *pButtonInt;
  932. for (i=pTBState->uStructSize-sizeof(TBBUTTON), pOut=(LPBYTE)(pButtonExt+1);
  933. i>0; --i, ++pOut)
  934. {
  935. *pOut = 0;
  936. }
  937. }
  938. else
  939. /* It is assumed the only other possibility is the OLDBUTTON struct */
  940. {
  941. *(LPOLDTBBUTTON)pButtonExt = *(LPOLDTBBUTTON)pButtonInt;
  942. }
  943. }
  944. LRESULT CALLBACK _loadds ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  945. {
  946. BOOL fSameButton;
  947. PTBBUTTON ptbButton;
  948. PTBSTATE pTBState;
  949. int iPos;
  950. BYTE fsState;
  951. #if WINVER >= 0x0400
  952. DWORD dw;
  953. #endif
  954. pTBState = GETWINDOWPOINTER(hWnd, PTBSTATE);
  955. switch (wMsg) {
  956. case WM_CREATE:
  957. #define lpcs ((LPCREATESTRUCT)lParam)
  958. if (!CreateDitherBrush(FALSE))
  959. return -1;
  960. if (!InitGlobalObjects()) {
  961. FreeGlobalObjects();
  962. return -1;
  963. }
  964. /* create the state data for this toolbar */
  965. pTBState = ALLOCWINDOWPOINTER(PTBSTATE, sizeof(TBSTATE)-sizeof(TBBUTTON));
  966. if (!pTBState)
  967. return -1;
  968. /* The struct is initialized to all NULL when created.
  969. */
  970. pTBState->hwndCommand = lpcs->hwndParent;
  971. pTBState->uStructSize = 0;
  972. // grow the button size to the appropriate girth
  973. if (!SetBitmapSize(pTBState, DEFAULTBITMAPX, DEFAULTBITMAPX))
  974. return -1;
  975. SETWINDOWPOINTER(hWnd, PTBSTATE, pTBState);
  976. if (!(lpcs->style&(CCS_TOP|CCS_NOMOVEY|CCS_BOTTOM)))
  977. {
  978. lpcs->style |= CCS_TOP;
  979. SetWindowLong(hWnd, GWL_STYLE, lpcs->style);
  980. }
  981. break;
  982. case WM_DESTROY:
  983. if (pTBState)
  984. {
  985. PTBBMINFO pTemp;
  986. int i;
  987. /* Free all the bitmaps before exiting
  988. */
  989. for (pTemp=pTBState->pBitmaps, i=pTBState->nBitmaps-1; i>=0;
  990. ++pTemp, --i)
  991. {
  992. if (pTemp->hInst && pTemp->hbm)
  993. DeleteObject(pTemp->hbm);
  994. }
  995. FlushButtonCache(pTBState);
  996. if (pTBState->nStrings > 0)
  997. DestroyStrings(pTBState);
  998. FREEWINDOWPOINTER(pTBState);
  999. SETWINDOWPOINTER(hWnd, PTBSTATE, 0);
  1000. }
  1001. FreeGlobalObjects();
  1002. FreeDitherBrush();
  1003. break;
  1004. case WM_NCCALCSIZE:
  1005. #if WINVER >= 0x0400
  1006. /*
  1007. * This is sent when the window manager wants to find out
  1008. * how big our client area is to be. If we have a mini-caption
  1009. * then we trap this message and calculate the cleint area rect,
  1010. * which is the client area rect calculated by DefWindowProc()
  1011. * minus the width/height of the mini-caption bar
  1012. */
  1013. // let defwindowproc handle the standard borders etc...
  1014. dw = DefWindowProc(hWnd, wMsg, wParam, lParam ) ;
  1015. if (!(GetWindowLong(hWnd, GWL_STYLE) & CCS_NODIVIDER))
  1016. {
  1017. NCCALCSIZE_PARAMS FAR *lpNCP;
  1018. lpNCP = (NCCALCSIZE_PARAMS FAR *)lParam;
  1019. lpNCP->rgrc[0].top += 2;
  1020. }
  1021. return dw;
  1022. #endif
  1023. break;
  1024. case WM_NCACTIVATE:
  1025. case WM_NCPAINT:
  1026. #if WINVER >= 0x0400
  1027. // old-style toolbars are forced to be without dividers above
  1028. if (!(GetWindowLong(hWnd, GWL_STYLE) & CCS_NODIVIDER))
  1029. {
  1030. HDC hdc;
  1031. RECT rc;
  1032. hdc = GetWindowDC(hWnd);
  1033. GetWindowRect(hWnd, &rc);
  1034. ScreenToClient(hWnd, (LPPOINT)&(rc.left));
  1035. ScreenToClient(hWnd, (LPPOINT)&(rc.right));
  1036. rc.bottom = (-rc.top); // bottom of NC area
  1037. rc.top = rc.bottom - (2 * GetSystemMetrics(SM_CYBORDER));
  1038. DrawBorder(hdc, &rc, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM);
  1039. ReleaseDC(hWnd, hdc);
  1040. }
  1041. else
  1042. goto DoDefault;
  1043. #endif
  1044. break;
  1045. case WM_PAINT:
  1046. ToolbarPaint(hWnd, pTBState);
  1047. break;
  1048. case WM_HSCROLL: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  1049. case WM_COMMAND:
  1050. case WM_DRAWITEM:
  1051. case WM_MEASUREITEM:
  1052. case WM_VKEYTOITEM:
  1053. case WM_CHARTOITEM:
  1054. SendMessage(pTBState->hwndCommand, wMsg, wParam, lParam);
  1055. break;
  1056. #ifdef _WIN32
  1057. case WM_CTLCOLORBTN:
  1058. case WM_CTLCOLORDLG:
  1059. case WM_CTLCOLOREDIT:
  1060. case WM_CTLCOLORLISTBOX:
  1061. case WM_CTLCOLORMSGBOX:
  1062. case WM_CTLCOLORSCROLLBAR:
  1063. case WM_CTLCOLORSTATIC:
  1064. #else
  1065. case WM_CTLCOLOR:
  1066. #endif
  1067. //!!!!! ack use COLOR_BTNFACE
  1068. return (LRESULT)(UINT)GetStockObject(LTGRAY_BRUSH);
  1069. case WM_LBUTTONDOWN:
  1070. iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
  1071. #if 0
  1072. if ((wParam&MK_SHIFT) &&(GetWindowLong(hWnd, GWL_STYLE)&CCS_ADJUSTABLE))
  1073. {
  1074. MoveButton(hWnd, pTBState, iPos);
  1075. } else
  1076. #endif
  1077. if (iPos >= 0)
  1078. {
  1079. ptbButton = pTBState->Buttons + iPos;
  1080. pTBState->pCaptureButton = ptbButton;
  1081. SetCapture(hWnd);
  1082. if (ptbButton->fsState & TBSTATE_ENABLED)
  1083. {
  1084. ptbButton->fsState |= TBSTATE_PRESSED;
  1085. InvalidateButton(hWnd, pTBState, ptbButton);
  1086. UpdateWindow(hWnd); // imedeate feedback
  1087. }
  1088. #ifdef _WIN32
  1089. SendMessage(pTBState->hwndCommand, WM_COMMAND, (UINT)GetWindowLong(hWnd, GWL_ID), MAKELONG(pTBState->pCaptureButton->idCommand, TBN_BEGINDRAG));
  1090. #else
  1091. SendMessage(pTBState->hwndCommand, WM_COMMAND, GETWINDOWID(hWnd), MAKELONG(pTBState->pCaptureButton->idCommand, TBN_BEGINDRAG));
  1092. #endif
  1093. }
  1094. break;
  1095. case WM_MOUSEMOVE:
  1096. // if the toolbar has lost the capture for some reason, stop
  1097. if ((hWnd != GetCapture()) && (pTBState->pCaptureButton != NULL)) {
  1098. #ifdef _WIN32
  1099. SendMessage(pTBState->hwndCommand, WM_COMMAND, (UINT)GetWindowLong(hWnd, GWL_ID),
  1100. MAKELONG(pTBState->pCaptureButton->idCommand, TBN_ENDDRAG));
  1101. #else
  1102. SendMessage(pTBState->hwndCommand, WM_COMMAND, GETWINDOWID(hWnd),
  1103. MAKELONG(pTBState->pCaptureButton->idCommand, TBN_ENDDRAG));
  1104. #endif
  1105. // if the button is still pressed, unpress it.
  1106. if (pTBState->pCaptureButton->fsState & TBSTATE_PRESSED)
  1107. SendMessage(hWnd, TB_PRESSBUTTON, pTBState->pCaptureButton->idCommand, 0L);
  1108. pTBState->pCaptureButton = NULL;
  1109. }
  1110. else if (pTBState->pCaptureButton!=NULL
  1111. && (pTBState->pCaptureButton->fsState & TBSTATE_ENABLED)) {
  1112. iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
  1113. fSameButton = (iPos>=0
  1114. && pTBState->pCaptureButton==pTBState->Buttons+iPos);
  1115. if (fSameButton == !(pTBState->pCaptureButton->fsState & TBSTATE_PRESSED)) {
  1116. pTBState->pCaptureButton->fsState ^= TBSTATE_PRESSED;
  1117. InvalidateButton(hWnd, pTBState, pTBState->pCaptureButton);
  1118. }
  1119. }
  1120. break;
  1121. case WM_LBUTTONUP:
  1122. if (pTBState->pCaptureButton != NULL) {
  1123. int idCommand;
  1124. idCommand = pTBState->pCaptureButton->idCommand;
  1125. ReleaseCapture();
  1126. #ifdef _WIN32
  1127. SendMessage(pTBState->hwndCommand, WM_COMMAND, (UINT)GetWindowLong(hWnd, GWL_ID), MAKELONG(idCommand, TBN_ENDDRAG));
  1128. #else
  1129. SendMessage(pTBState->hwndCommand, WM_COMMAND, GETWINDOWID(hWnd), MAKELONG(idCommand, TBN_ENDDRAG));
  1130. #endif
  1131. iPos = TBHitTest(pTBState, LOWORD(lParam), HIWORD(lParam));
  1132. if ((pTBState->pCaptureButton->fsState&TBSTATE_ENABLED) && iPos>=0
  1133. && (pTBState->pCaptureButton==pTBState->Buttons+iPos)) {
  1134. pTBState->pCaptureButton->fsState &= ~TBSTATE_PRESSED;
  1135. if (pTBState->pCaptureButton->fsStyle & TBSTYLE_CHECK) {
  1136. if (pTBState->pCaptureButton->fsStyle & TBSTYLE_GROUP) {
  1137. // group buttons already checked can't be force
  1138. // up by the user.
  1139. if (pTBState->pCaptureButton->fsState & TBSTATE_CHECKED) {
  1140. pTBState->pCaptureButton = NULL;
  1141. break; // bail!
  1142. }
  1143. pTBState->pCaptureButton->fsState |= TBSTATE_CHECKED;
  1144. MakeGroupConsistant(hWnd, pTBState, idCommand);
  1145. } else {
  1146. pTBState->pCaptureButton->fsState ^= TBSTATE_CHECKED; // toggle
  1147. }
  1148. // if we change a button's state, we need to flush the
  1149. // cache
  1150. FlushButtonCache(pTBState);
  1151. }
  1152. InvalidateButton(hWnd, pTBState, pTBState->pCaptureButton);
  1153. pTBState->pCaptureButton = NULL;
  1154. SendMessage(pTBState->hwndCommand, WM_COMMAND, idCommand, 0L);
  1155. }
  1156. else {
  1157. pTBState->pCaptureButton = NULL;
  1158. }
  1159. }
  1160. break;
  1161. case TB_SETSTATE:
  1162. iPos = PositionFromID(pTBState, (int)wParam);
  1163. if (iPos < 0)
  1164. return(FALSE);
  1165. ptbButton = pTBState->Buttons + iPos;
  1166. fsState = (BYTE)(LOWORD(lParam) ^ ptbButton->fsState);
  1167. ptbButton->fsState = (BYTE)LOWORD(lParam);
  1168. if (fsState)
  1169. // flush the button cache
  1170. //!!!! this could be much more intelligent
  1171. FlushButtonCache(pTBState);
  1172. if (fsState & TBSTATE_HIDDEN)
  1173. InvalidateRect(hWnd, NULL, TRUE);
  1174. else if (fsState)
  1175. InvalidateButton(hWnd, pTBState, ptbButton);
  1176. return(TRUE);
  1177. case TB_GETSTATE:
  1178. iPos = PositionFromID(pTBState, (int)wParam);
  1179. if (iPos < 0)
  1180. return(-1L);
  1181. return(pTBState->Buttons[iPos].fsState);
  1182. case TB_ENABLEBUTTON:
  1183. case TB_CHECKBUTTON:
  1184. case TB_PRESSBUTTON:
  1185. case TB_HIDEBUTTON:
  1186. case TB_INDETERMINATE:
  1187. iPos = PositionFromID(pTBState, (int)wParam);
  1188. if (iPos < 0)
  1189. return(FALSE);
  1190. ptbButton = &pTBState->Buttons[iPos];
  1191. fsState = ptbButton->fsState;
  1192. if (LOWORD(lParam))
  1193. ptbButton->fsState |= wStateMasks[wMsg - TB_ENABLEBUTTON];
  1194. else
  1195. ptbButton->fsState &= ~wStateMasks[wMsg - TB_ENABLEBUTTON];
  1196. // did this actually change the state?
  1197. if (fsState != ptbButton->fsState) {
  1198. // is this button a member of a group?
  1199. if ((wMsg == TB_CHECKBUTTON) && (ptbButton->fsStyle & TBSTYLE_GROUP))
  1200. MakeGroupConsistant(hWnd, pTBState, (int)wParam);
  1201. // flush the button cache
  1202. //!!!! this could be much more intelligent
  1203. FlushButtonCache(pTBState);
  1204. if (wMsg == TB_HIDEBUTTON)
  1205. InvalidateRect(hWnd, NULL, TRUE);
  1206. else
  1207. InvalidateButton(hWnd, pTBState, ptbButton);
  1208. }
  1209. return(TRUE);
  1210. case TB_ISBUTTONENABLED:
  1211. case TB_ISBUTTONCHECKED:
  1212. case TB_ISBUTTONPRESSED:
  1213. case TB_ISBUTTONHIDDEN:
  1214. case TB_ISBUTTONINDETERMINATE:
  1215. iPos = PositionFromID(pTBState, (int)wParam);
  1216. if (iPos < 0)
  1217. return(-1L);
  1218. return (LRESULT)pTBState->Buttons[iPos].fsState
  1219. & wStateMasks[wMsg - TB_ISBUTTONENABLED];
  1220. case TB_ADDBITMAP:
  1221. #ifdef _WIN32
  1222. {
  1223. TB_ADDBITMAPINFO * ptbai;
  1224. ptbai = (TB_ADDBITMAPINFO *)lParam;
  1225. return AddBitmap(pTBState, wParam, ptbai->hBitmap, ptbai->idResource);
  1226. }
  1227. #else
  1228. return(AddBitmap(pTBState, wParam,
  1229. (HINSTANCE)LOWORD(lParam), HIWORD(lParam)));
  1230. #endif
  1231. case TB_ADDBUTTONS:
  1232. return(InsertButtons(hWnd, pTBState, (UINT)-1, wParam,
  1233. (LPTBBUTTON)lParam));
  1234. case TB_INSERTBUTTON:
  1235. return(InsertButtons(hWnd, pTBState, wParam, 1, (LPTBBUTTON)lParam));
  1236. case TB_DELETEBUTTON:
  1237. return(DeleteButton(hWnd, pTBState, wParam));
  1238. case TB_GETBUTTON:
  1239. if (wParam >= (UINT)pTBState->iNumButtons)
  1240. return(FALSE);
  1241. TBOutputStruct(pTBState, pTBState->Buttons+wParam, (LPTBBUTTON)lParam);
  1242. return(TRUE);
  1243. case TB_BUTTONCOUNT:
  1244. return(pTBState->iNumButtons);
  1245. case TB_COMMANDTOINDEX:
  1246. return(PositionFromID(pTBState, (int)wParam));
  1247. case TB_GETITEMRECT:
  1248. return(MAKELRESULT(GetItemRect(pTBState, wParam, (LPRECT)lParam), 0));
  1249. break;
  1250. case TB_BUTTONSTRUCTSIZE:
  1251. /* You are not allowed to change this after adding buttons.
  1252. */
  1253. if (!pTBState || pTBState->iNumButtons)
  1254. {
  1255. break;
  1256. }
  1257. pTBState->uStructSize = wParam;
  1258. break;
  1259. case TB_SETBUTTONSIZE:
  1260. if (!LOWORD(lParam))
  1261. lParam = MAKELONG(DEFAULTBUTTONX, HIWORD(lParam));
  1262. if (!HIWORD(lParam))
  1263. lParam = MAKELONG(LOWORD(lParam), DEFAULTBUTTONY);
  1264. return(GrowToolbar(pTBState, LOWORD(lParam), HIWORD(lParam), FALSE));
  1265. case TB_SETBITMAPSIZE:
  1266. return(SetBitmapSize(pTBState, LOWORD(lParam), HIWORD(lParam)));
  1267. case TB_SETBUTTONTYPE:
  1268. pTBState->wButtonType = wParam;
  1269. break;
  1270. default:
  1271. #if WINVER >= 0x0400
  1272. DoDefault:
  1273. #endif
  1274. return DefWindowProc(hWnd, wMsg, wParam, lParam);
  1275. }
  1276. return 0L;
  1277. }