Leaked source code of windows server 2003
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.

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