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.

7109 lines
214 KiB

  1. /*
  2. ** Toolbar.c
  3. **
  4. ** This is it, the incredibly famous toolbar control. Most of
  5. ** the customization stuff is in another file.
  6. */
  7. #include "ctlspriv.h"
  8. #include "toolbar.h"
  9. #include "image.h"
  10. #include <limits.h>
  11. #define __IOleControl_INTERFACE_DEFINED__ // There is a conflich with the IOleControl's def of CONTROLINFO
  12. #include "shlobj.h"
  13. #define TBP_ONRELEASECAPTURE (WM_USER + 0x500)
  14. #define TBIMAGELIST
  15. // these values are defined by the UI gods...
  16. #define DEFAULTBITMAPX 16
  17. #define DEFAULTBITMAPY 15
  18. #define LIST_GAP (g_cxEdge * 2)
  19. #define DROPDOWN_GAP (g_cxEdge * 2)
  20. #define CX_TOP_FUDGE (g_cxEdge * 2)
  21. #define SMALL_DXYBITMAP 16 // new dx dy for sdt images
  22. #define LARGE_DXYBITMAP 24
  23. #define DEFAULTBUTTONX 24
  24. #define DEFAULTBUTTONY 22
  25. // the insert mark is 6 pixels high/wide depending on horizontal or vertical mode...
  26. #define INSERTMARKSIZE 6
  27. const int g_dxButtonSep = 8;
  28. const int s_xFirstButton = 0; // was 8 in 3.1
  29. #define s_dxOverlap 0 // was 1 in 3.1
  30. #define USE_MIXED_BUTTONS(ptb) (((ptb)->dwStyleEx & TBSTYLE_EX_MIXEDBUTTONS) && ((ptb)->ci.style & TBSTYLE_LIST))
  31. #define BTN_NO_SHOW_TEXT(ptb, ptbb) (!(ptb)->nTextRows || (USE_MIXED_BUTTONS(ptb) && !((ptbb)->fsStyle & BTNS_SHOWTEXT)))
  32. #define BTN_IS_AUTOSIZE(ptb, ptbb) (((ptbb)->fsStyle & BTNS_AUTOSIZE) || (USE_MIXED_BUTTONS(ptb) && !((ptbb)->fsStyle & BTNS_SEP)))
  33. #define DRAW_MONO_BTN(ptb, state) (!(state & TBSTATE_ENABLED) || ((ptb->ci.style & WS_DISABLED) && ptb->ci.iVersion >= 5))
  34. // Globals - since all of these globals are used durring a paint we have to
  35. // take a criticial section around all toolbar paints. can we do better?
  36. //
  37. const UINT wStateMasks[] = {
  38. TBSTATE_ENABLED,
  39. TBSTATE_CHECKED,
  40. TBSTATE_PRESSED,
  41. TBSTATE_HIDDEN,
  42. TBSTATE_INDETERMINATE,
  43. TBSTATE_MARKED
  44. };
  45. #define TBISSTRINGPTR(iString) (((iString) != -1) && (!IS_INTRESOURCE(iString)))
  46. #define TBDraw_State(ptbdraw) ((ptbdraw)->tbcd.nmcd.uItemState)
  47. LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
  48. void TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize);
  49. BOOL SetBitmapSize(PTBSTATE ptb, int width, int height);
  50. int AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT_PTR wBMID);
  51. void TBBuildImageList(PTBSTATE ptb);
  52. BOOL GetInsertMarkRect(PTBSTATE ptb, LPRECT lpRect, BOOL fHorizMode);
  53. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTONDATA pTBButton);
  54. UINT TBGetDrawTextFlags(PTBSTATE ptb, UINT uiStyle, LPTBBUTTONDATA);
  55. BOOL TBGetMaxSize( PTBSTATE ptb, LPSIZE lpsize );
  56. void TBGetItem(PTBSTATE ptb,LPTBBUTTONDATA ptButton, LPNMTBDISPINFO ptbdi);
  57. #define GT_INSIDE 0x0001
  58. #define GT_MASKONLY 0x0002
  59. BOOL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, UINT flags);
  60. //Pager Control Functions
  61. LRESULT TB_OnScroll(PTBSTATE ptb, LPNMHDR pnm);
  62. LRESULT TB_OnPagerControlNotify(PTBSTATE ptb,LPNMHDR pnm);
  63. void TBAutoSize(PTBSTATE ptb);
  64. LRESULT TB_OnCalcSize(PTBSTATE ptb, LPNMHDR pnm);
  65. #define TBInvalidateImageList(ptb) ((ptb)->fHimlValid = FALSE)
  66. #define TBHasStrings(ptb) ((ptb)->nStrings || (ptb)->fNoStringPool)
  67. __inline BOOL TB_IsDropDown(LPTBBUTTONDATA ptbb)
  68. {
  69. BOOL fRet = (ptbb->fsStyle & (BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN));
  70. return fRet;
  71. }
  72. __inline BOOL TB_HasDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  73. {
  74. BOOL fRet = (((ptb->dwStyleEx & TBSTYLE_EX_DRAWDDARROWS) &&
  75. (ptbb->fsStyle & BTNS_DROPDOWN)) ||
  76. (ptbb->fsStyle & BTNS_WHOLEDROPDOWN));
  77. return fRet;
  78. }
  79. __inline BOOL TB_HasSplitDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  80. {
  81. // If the button is both BTNS_DROPDOWN and BTNS_WHOLEDROPDOWN,
  82. // BTNS_WHOLEDROPDOWN wins.
  83. BOOL fRet = ((ptb->dwStyleEx & TBSTYLE_EX_DRAWDDARROWS) &&
  84. (ptbb->fsStyle & BTNS_DROPDOWN) &&
  85. !(ptbb->fsStyle & BTNS_WHOLEDROPDOWN));
  86. return fRet;
  87. }
  88. __inline BOOL TB_HasUnsplitDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  89. {
  90. BOOL fRet = (ptbb->fsStyle & BTNS_WHOLEDROPDOWN);
  91. return fRet;
  92. }
  93. __inline BOOL TB_HasTopDDArrow(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  94. {
  95. BOOL fRet = (!(ptb->ci.style & TBSTYLE_LIST) &&
  96. TB_HasUnsplitDDArrow(ptb, ptbb) &&
  97. (ptb->nTextRows > 0) && TB_StrForButton(ptb, ptbb));
  98. return fRet;
  99. }
  100. BOOL TBIsHotTrack(PTBSTATE ptb, LPTBBUTTONDATA ptButton, UINT state)
  101. {
  102. BOOL fHotTrack = FALSE;
  103. if ((ptb->ci.style & TBSTYLE_FLAT) && (&ptb->Buttons[ptb->iHot]==ptButton))
  104. fHotTrack = TRUE;
  105. // The following is in place to prevent hot tracking during the following conds:
  106. // - drag & drop toolbar customization
  107. // - when the mouse capture is on a particular button-press.
  108. // This does _not_ drop out of the loop because we don't want to break update
  109. // behavior; thus we'll have a little flickering on refresh as we pass over
  110. // these buttons.
  111. if (!(state & TBSTATE_PRESSED) && (GetKeyState (VK_LBUTTON) < 0) &&
  112. GetCapture() == ptb->ci.hwnd)
  113. {
  114. fHotTrack = FALSE;
  115. }
  116. if (!fHotTrack && (ptb->iPressedDD == ptButton - ptb->Buttons))
  117. fHotTrack = TRUE;
  118. return fHotTrack;
  119. }
  120. UINT StateFromCDIS(UINT uItemState)
  121. {
  122. UINT state = 0;
  123. if (uItemState & CDIS_CHECKED)
  124. state |= TBSTATE_CHECKED;
  125. if (uItemState & CDIS_SELECTED)
  126. state |= TBSTATE_PRESSED;
  127. if (!(uItemState & CDIS_DISABLED))
  128. state |= TBSTATE_ENABLED;
  129. if (uItemState & CDIS_MARKED)
  130. state |= TBSTATE_MARKED;
  131. if (uItemState & CDIS_INDETERMINATE)
  132. state |= TBSTATE_INDETERMINATE;
  133. return state;
  134. }
  135. UINT CDISFromState(UINT state)
  136. {
  137. UINT uItemState = 0;
  138. // Here are the TBSTATE - to - CDIS mappings:
  139. //
  140. // TBSTATE_CHECKED = CDIS_CHECKED
  141. // TBSTATE_PRESSED = CDIS_SELECTED
  142. // !TBSTATE_ENABLED = CDIS_DISABLED
  143. // TBSTATE_MARKED = CDIS_MARKED
  144. // TBSTATE_INDETERMINATE = CDIS_INDETERMINATE
  145. //
  146. // Hot tracked item = CDIS_HOT
  147. //
  148. if (state & TBSTATE_CHECKED)
  149. uItemState |= CDIS_CHECKED;
  150. if (state & TBSTATE_PRESSED)
  151. uItemState |= CDIS_SELECTED;
  152. if (!(state & TBSTATE_ENABLED))
  153. uItemState |= CDIS_DISABLED;
  154. if (state & TBSTATE_MARKED)
  155. uItemState |= CDIS_MARKED;
  156. if (state & TBSTATE_INDETERMINATE)
  157. uItemState |= CDIS_INDETERMINATE;
  158. return uItemState;
  159. }
  160. void FlushToolTipsMgrNow(PTBSTATE ptb);
  161. void TB_ForceCreateTooltips(PTBSTATE ptb)
  162. {
  163. if (ptb->ci.style & TBSTYLE_TOOLTIPS && !ptb->hwndToolTips)
  164. {
  165. TOOLINFO ti;
  166. // don't bother setting the rect because we'll do it below
  167. // in TBInvalidateItemRects;
  168. ti.cbSize = sizeof(ti);
  169. ti.uFlags = TTF_IDISHWND;
  170. ti.hwnd = ptb->ci.hwnd;
  171. ti.uId = (UINT_PTR)ptb->ci.hwnd;
  172. ti.lpszText = 0;
  173. ptb->hwndToolTips = CreateWindow(c_szSToolTipsClass, NULL,
  174. WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  175. ptb->ci.hwnd, NULL, HINST_THISDLL, NULL);
  176. if (ptb->hwndToolTips) {
  177. int i;
  178. NMTOOLTIPSCREATED nm;
  179. CCSetInfoTipWidth(ptb->ci.hwnd, ptb->hwndToolTips);
  180. SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  181. (LPARAM)(LPTOOLINFO)&ti);
  182. nm.hwndToolTips = ptb->hwndToolTips;
  183. CCSendNotify(&ptb->ci, NM_TOOLTIPSCREATED, &nm.hdr);
  184. // don't bother setting the rect because we'll do it below
  185. // in TBInvalidateItemRects;
  186. ti.uFlags = 0;
  187. ti.lpszText = LPSTR_TEXTCALLBACK;
  188. for (i = 0; i < ptb->iNumButtons; i++) {
  189. if (!(ptb->Buttons[i].fsStyle & BTNS_SEP)) {
  190. ti.uId = ptb->Buttons[i].idCommand;
  191. SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  192. (LPARAM)(LPTOOLINFO)&ti);
  193. }
  194. }
  195. FlushToolTipsMgrNow(ptb);
  196. }
  197. }
  198. }
  199. void TBRelayToToolTips(PTBSTATE ptb, UINT wMsg, WPARAM wParam, LPARAM lParam)
  200. {
  201. TB_ForceCreateTooltips(ptb);
  202. if (ptb->hwndToolTips) {
  203. RelayToToolTips(ptb->hwndToolTips, ptb->ci.hwnd, wMsg, wParam, lParam);
  204. }
  205. }
  206. LRESULT ToolbarDragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
  207. {
  208. PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
  209. LRESULT lres;
  210. switch (code)
  211. {
  212. case DPX_DRAGHIT:
  213. if (lp)
  214. {
  215. POINT pt;
  216. int item;
  217. pt.x = ((POINTL *)lp)->x;
  218. pt.y = ((POINTL *)lp)->y;
  219. MapWindowPoints(NULL, ptb->ci.hwnd, &pt, 1);
  220. item = TBHitTest(ptb, pt.x, pt.y);
  221. if (0 <= item && item < ptb->iNumButtons)
  222. lres = (LRESULT)ptb->Buttons[item].idCommand;
  223. else
  224. lres = (LRESULT)-1;
  225. }
  226. else
  227. lres = -1;
  228. break;
  229. case DPX_GETOBJECT:
  230. lres = (LRESULT)GetItemObject(&ptb->ci, TBN_GETOBJECT, &IID_IDropTarget, (LPNMOBJECTNOTIFY)lp);
  231. break;
  232. case DPX_SELECT:
  233. if ((int)wp >= 0)
  234. {
  235. NMTBHOTITEM nmhi;
  236. nmhi.idNew = (int) wp;
  237. if (!CCSendNotify(&ptb->ci, TBN_DRAGOVER, &nmhi.hdr))
  238. {
  239. SendMessage(ptb->ci.hwnd, TB_MARKBUTTON, wp,
  240. MAKELPARAM((lp != DROPEFFECT_NONE), 0));
  241. }
  242. }
  243. lres = 0;
  244. break;
  245. default:
  246. lres = -1;
  247. break;
  248. }
  249. return lres;
  250. }
  251. int TBMixedButtonHeight(PTBSTATE ptb, int iIndex)
  252. {
  253. int iHeight;
  254. LPTBBUTTONDATA ptbb = &(ptb->Buttons[iIndex]);
  255. if (ptbb->fsStyle & BTNS_SHOWTEXT) // text and icon
  256. iHeight = max(ptb->iDyBitmap, ptb->dyIconFont);
  257. else // icon, no text
  258. iHeight = ptb->iDyBitmap;
  259. return iHeight;
  260. }
  261. int TBMixedButtonsHeight(PTBSTATE ptb)
  262. {
  263. int i;
  264. int iHeightMax = 0;
  265. int iHeight;
  266. ASSERT(ptb->ci.style & TBSTYLE_LIST);
  267. ASSERT(USE_MIXED_BUTTONS(ptb));
  268. for (i = 0; i < ptb->iNumButtons; i++) {
  269. iHeight = TBMixedButtonHeight(ptb, i);
  270. iHeightMax = max(iHeightMax, iHeight);
  271. }
  272. return iHeightMax;
  273. }
  274. int HeightWithString(PTBSTATE ptb, int h)
  275. {
  276. if (USE_MIXED_BUTTONS(ptb))
  277. {
  278. int hMixed = TBMixedButtonsHeight(ptb);
  279. return (max(h, hMixed));
  280. }
  281. else if (ptb->ci.style & TBSTYLE_LIST)
  282. return (max(h, ptb->dyIconFont));
  283. else if (ptb->dyIconFont)
  284. return (h + ptb->dyIconFont + 1);
  285. else
  286. return (h);
  287. }
  288. int TBGetSepHeight(PTBSTATE ptb, LPTBBUTTONDATA pbtn)
  289. {
  290. ASSERT(pbtn->fsStyle & BTNS_SEP);
  291. if (ptb->ci.style & (CCS_VERT | TBSTYLE_FLAT) )
  292. return pbtn->DUMMYUNION_MEMBER(cxySep);
  293. else
  294. return pbtn->DUMMYUNION_MEMBER(cxySep) * 2 / 3;
  295. }
  296. UINT TBWidthOfString(PTBSTATE ptb, LPTBBUTTONDATA ptbb, HDC hdc)
  297. {
  298. UINT uiWidth = 0;
  299. LPTSTR pstr = TB_StrForButton(ptb, ptbb);
  300. if (pstr)
  301. {
  302. HDC hdcCreated = NULL;
  303. HFONT hOldFont;
  304. UINT uiStyle;
  305. RECT rcText = {0,0,1000,10};
  306. if (!hdc)
  307. {
  308. hdcCreated = GetDC(ptb->ci.hwnd);
  309. hdc = hdcCreated;
  310. }
  311. hOldFont = SelectObject(hdc, ptb->hfontIcon);
  312. uiStyle = DT_CALCRECT | TBGetDrawTextFlags(ptb, 0, ptbb);
  313. DrawText(hdc, pstr, -1, &rcText, uiStyle);
  314. uiWidth += rcText.right;
  315. SelectObject(hdc, hOldFont);
  316. if (hdcCreated)
  317. ReleaseDC(ptb->ci.hwnd, hdcCreated);
  318. }
  319. return uiWidth;
  320. }
  321. // TBDDArrowAdjustment(ptb, ptbb): the amount by which we change the width of
  322. // this button to accomodate the drop-down arrow. not necessarily the same as
  323. // ptb->dxDDArrowChar.
  324. int TBDDArrowAdjustment(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  325. {
  326. int iAdjust = 0;
  327. if (TB_HasDDArrow(ptb, ptbb))
  328. {
  329. // If a whole dd, non-autosize button, then we'll just use the standard
  330. // button width which ought to have room for this button (i.e., return 0).
  331. if (!TB_HasTopDDArrow(ptb, ptbb) || BTN_IS_AUTOSIZE(ptb, ptbb))
  332. {
  333. iAdjust += (WORD)ptb->dxDDArrowChar;
  334. if (TB_HasUnsplitDDArrow(ptb, ptbb))
  335. {
  336. // subtract off a bit since there won't be a border
  337. // around dd arrow part of this button
  338. iAdjust -= 2 * g_cxEdge;
  339. if (ptbb->iBitmap != I_IMAGENONE)
  340. {
  341. // nudge over a bit more to overlap bitmap border padding
  342. iAdjust -= g_cxEdge;
  343. }
  344. }
  345. if (TB_HasTopDDArrow(ptb, ptbb))
  346. {
  347. // If string width >= icon width + iAdjust, then no need
  348. // to add extra space for the arrow.
  349. if ((int)TBWidthOfString(ptb, ptbb, NULL) >= ptb->iDxBitmap + iAdjust)
  350. iAdjust = 0;
  351. }
  352. }
  353. }
  354. return max(iAdjust, 0);
  355. }
  356. int TBWidthOfButton(PTBSTATE ptb, LPTBBUTTONDATA pButton, HDC hdc)
  357. {
  358. RECT rc;
  359. if (BTN_IS_AUTOSIZE(ptb, pButton)) {
  360. // if they've set this button for autosize, calculate it and cache
  361. // it in cx
  362. if (BTN_NO_SHOW_TEXT(ptb, pButton)) {
  363. pButton->cx = 0;
  364. goto CalcIconWidth;
  365. }
  366. if (pButton->cx == 0) {
  367. UINT uiStringWidth = TBWidthOfString(ptb, pButton, hdc);
  368. pButton->cx = (WORD) ptb->xPad + uiStringWidth;
  369. if (uiStringWidth) {
  370. // Since we have a string for this button, we need to add
  371. // some padding around it.
  372. if ((ptb->ci.style & TBSTYLE_LIST) && TB_HasSplitDDArrow(ptb, pButton))
  373. pButton->cx += (WORD) ptb->iDropDownGap;
  374. else
  375. pButton->cx += 2 * g_cxEdge;
  376. }
  377. CalcIconWidth:
  378. if (pButton->iBitmap != I_IMAGENONE) {
  379. if (ptb->ci.style & TBSTYLE_LIST) {
  380. pButton->cx += ptb->iDxBitmap + ptb->iListGap;
  381. if (BTN_NO_SHOW_TEXT(ptb, pButton))
  382. pButton->cx += g_cxEdge * 2;
  383. }
  384. else {
  385. // Use wider of string width (pButton->cx so far) and bitmap width.
  386. pButton->cx = max(pButton->cx, ptb->iDxBitmap + ptb->xPad);
  387. }
  388. }
  389. pButton->cx += (USHORT)TBDDArrowAdjustment(ptb, pButton);
  390. }
  391. }
  392. if (pButton->cx) {
  393. return (int)pButton->cx;
  394. } else if (pButton->fsStyle & BTNS_SEP) {
  395. if (ptb->ci.style & CCS_VERT) {
  396. GetWindowRect(ptb->ci.hwnd, &rc);
  397. return RECTWIDTH(rc);
  398. } else {
  399. // Compat: Corel (Font navigator) expects the separators to be
  400. // 8 pixels wide. So do not return pButton->cxySep here, since
  401. // that can be calculated differently depending on the flat style.
  402. //
  403. // No. owner draw items are added by specifying separator, and
  404. // the iBitmap width which is then copied down to cxySep.
  405. // the preserving of size for corel needs to be done at that point.
  406. return pButton->DUMMYUNION_MEMBER(cxySep);
  407. }
  408. } else if (!(TBSTYLE_EX_VERTICAL & ptb->dwStyleEx)) {
  409. return ptb->iButWidth + TBDDArrowAdjustment(ptb, pButton);
  410. } else {
  411. return ptb->iButWidth;
  412. }
  413. }
  414. UINT TBGetDrawTextFlags(PTBSTATE ptb, UINT uiStyle, TBBUTTONDATA* ptbb)
  415. {
  416. if (ptb->nTextRows > 1)
  417. uiStyle |= DT_WORDBREAK | DT_EDITCONTROL;
  418. else
  419. uiStyle |= DT_SINGLELINE;
  420. if (ptb->ci.style & TBSTYLE_LIST)
  421. {
  422. uiStyle |= DT_LEFT | DT_VCENTER | DT_SINGLELINE;
  423. }
  424. else
  425. {
  426. uiStyle |= DT_CENTER;
  427. }
  428. uiStyle &= ~(ptb->uDrawTextMask);
  429. uiStyle |= ptb->uDrawText;
  430. if (ptbb->fsStyle & BTNS_NOPREFIX)
  431. uiStyle |= DT_NOPREFIX;
  432. if (CCGetUIState(&(ptb->ci)) & UISF_HIDEACCEL)
  433. {
  434. uiStyle |= DT_HIDEPREFIX;
  435. }
  436. return uiStyle;
  437. }
  438. BOOL TBRecalc(PTBSTATE ptb)
  439. {
  440. TEXTMETRIC tm = {0};
  441. int i;
  442. HDC hdc;
  443. int cxMax = 0, cxMask, cy;
  444. HFONT hOldFont=NULL;
  445. if (ptb->fRedrawOff) {
  446. // redraw is off; defer recalc until redraw is turned back on
  447. ptb->fRecalc = TRUE;
  448. return TRUE; // The recalc "succeeded" - actual work will happen later
  449. }
  450. ptb->dyIconFont = 0;
  451. if (!TBHasStrings(ptb) || !ptb->nTextRows ) {
  452. cxMax = ptb->iDxBitmap;
  453. cxMask = cxMax;
  454. } else {
  455. SIZE size = {0};
  456. LPCTSTR pstr;
  457. RECT rcText = {0,0,0,0};
  458. int cxExtra = ptb->xPad;
  459. ptb->iButWidth = 0;
  460. hdc = GetDC(ptb->ci.hwnd);
  461. if (!hdc)
  462. return(FALSE);
  463. if (ptb->hfontIcon)
  464. hOldFont = SelectObject(hdc, ptb->hfontIcon);
  465. GetTextMetrics(hdc, &tm);
  466. if (ptb->nTextRows)
  467. ptb->dyIconFont = (tm.tmHeight * ptb->nTextRows) +
  468. (tm.tmExternalLeading * (ptb->nTextRows - 1)); // add an edge ?
  469. if (ptb->ci.style & TBSTYLE_LIST)
  470. cxExtra += ptb->iDxBitmap + ptb->iListGap;
  471. // default to the image size...
  472. cxMax = ptb->iDxBitmap;
  473. // walk strings to find max width
  474. for (i = 0; i < ptb->iNumButtons; i++)
  475. {
  476. if (ptb->Buttons[i].fsState & TBSTATE_HIDDEN)
  477. continue;
  478. if (BTN_IS_AUTOSIZE(ptb, &ptb->Buttons[i]))
  479. ptb->Buttons[i].cx = 0;
  480. pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
  481. if (pstr)
  482. {
  483. if ( ptb->ci.iVersion < 5 )
  484. {
  485. // we used to use GetTextExtentPoint instead of DrawText. This function would include the width
  486. // of the "&" character if it was present. As a result, it returned larger values and thus created
  487. // wider buttons. Without this extra fudge certain buttons will be about 6 pixels too narrow.
  488. GetTextExtentPoint(hdc, pstr, lstrlen(pstr), &size);
  489. }
  490. else
  491. {
  492. // wordbreak is not allowed in the calcrect w/ singleline
  493. UINT uiStyle = DT_CALCRECT | DT_SINGLELINE | (TBGetDrawTextFlags(ptb, 0, &ptb->Buttons[i]) & ~DT_WORDBREAK);
  494. RECT rcTemp = {0,0,0,0};
  495. rcTemp.bottom = ptb->dyIconFont;
  496. DrawText(hdc, pstr, -1, &rcTemp, uiStyle);
  497. size.cx = RECTWIDTH(rcTemp);
  498. size.cy = RECTHEIGHT(rcTemp);
  499. // BUGBUG: size.cy stuff is fishy -- last one wins
  500. }
  501. }
  502. else
  503. {
  504. size.cx = 0;
  505. }
  506. if (TB_HasTopDDArrow(ptb, &ptb->Buttons[i])) {
  507. int iBmpWithArrow = CX_TOP_FUDGE + ptb->iDxBitmap + ptb->dxDDArrowChar;
  508. size.cx = max(size.cx, iBmpWithArrow);
  509. }
  510. else if ((ptb->dwStyleEx & TBSTYLE_EX_VERTICAL) &&
  511. TB_HasDDArrow(ptb, &ptb->Buttons[i])) {
  512. // for vertical toolbars, buttons with drop-down arrows
  513. // are drawn with the same width as normal buttons, so
  514. // we need to figure them into our max width calculation.
  515. size.cx += ptb->dxDDArrowChar;
  516. }
  517. if (cxMax < size.cx)
  518. cxMax = size.cx;
  519. }
  520. // if cxMax is less than the iButMinWidth - dxBitmap (if LIST) then
  521. // cxMax = iButMinWidth
  522. if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
  523. cxMax = ptb->iButMinWidth - cxExtra;
  524. cxMask = cxMax;
  525. // Is the cxMax + dxBitmap (if LIST) more than the max width ?
  526. if (ptb->iButMaxWidth && (ptb->iButMaxWidth < (cxMax + cxExtra)))
  527. {
  528. int cyMax = 0;
  529. int cxTemp = 0;
  530. cxMax = ptb->iButMaxWidth - cxExtra;
  531. // But leave cxMask at its old value since AUTOSIZE buttons
  532. // are exempt from button truncation. This exemption is a bug,
  533. // but IE4 shipped that way so we're stuck with it. (You can
  534. // tell it's a bug because we go ahead and flip TBSTATE_ELLIPSIS
  535. // even on AUTOSIZE buttons, only to "forget" about the ellipsis
  536. // in TBWidthOfString().)
  537. // walk strings to set the TBSTATE_ELLIPSES
  538. for (i = 0; i < ptb->iNumButtons; i++)
  539. {
  540. BOOL fEllipsed = FALSE;
  541. UINT uiStyle;
  542. if (ptb->Buttons[i].fsState & TBSTATE_HIDDEN)
  543. continue;
  544. if (BTN_NO_SHOW_TEXT(ptb, &ptb->Buttons[i]))
  545. pstr = NULL;
  546. else
  547. {
  548. pstr = TB_StrForButton(ptb, &ptb->Buttons[i]);
  549. uiStyle = DT_CALCRECT | TBGetDrawTextFlags(ptb, 0, &ptb->Buttons[i]);
  550. }
  551. if (pstr)
  552. {
  553. int cxMaxText;
  554. if ((ptb->dwStyleEx & TBSTYLE_EX_VERTICAL) &&
  555. TB_HasDDArrow(ptb, &ptb->Buttons[i]))
  556. {
  557. // if a drop-down button on a vertical toolbar,
  558. // need to make space for drop-down arrow
  559. cxMaxText = cxMax - ptb->dxDDArrowChar;
  560. }
  561. else
  562. {
  563. cxMaxText = cxMax;
  564. }
  565. // DrawText doesn't like it when cxMaxText <= 0
  566. cxMaxText = max(cxMaxText, 1);
  567. rcText.bottom = ptb->dyIconFont;
  568. rcText.right = cxMaxText;
  569. DrawText(hdc, pstr, -1, &rcText, uiStyle);
  570. if (ptb->nTextRows > 1)
  571. {
  572. // width is width of text plus width we might
  573. // have lopped off for drop-down arrow
  574. int cx = rcText.right + (cxMax - cxMaxText);
  575. if (cx > cxTemp)
  576. {
  577. // this is our new multiline text hack max
  578. cxTemp = cx;
  579. }
  580. fEllipsed = (BOOL)(rcText.bottom > ptb->dyIconFont);
  581. }
  582. else
  583. fEllipsed = (BOOL)(rcText.right > cxMaxText);
  584. if (cyMax < rcText.bottom)
  585. cyMax = rcText.bottom;
  586. }
  587. if (fEllipsed)
  588. ptb->Buttons[i].fsState |= TBSTATE_ELLIPSES;
  589. else
  590. ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
  591. }
  592. if (cxTemp && (ptb->nTextRows > 1 ))
  593. cxMax = cxTemp;
  594. // Set the text height to the tallest text, with the top end being the number
  595. // of rows specified by MAXTEXTROWS
  596. if (ptb->dyIconFont > cyMax)
  597. ptb->dyIconFont = cyMax;
  598. }
  599. else
  600. {
  601. for (i = 0; i < ptb->iNumButtons; i++)
  602. ptb->Buttons[i].fsState &= ~TBSTATE_ELLIPSES;
  603. if ((ptb->nTextRows) && ptb->iNumButtons && (ptb->dyIconFont > size.cy))
  604. ptb->dyIconFont = size.cy;
  605. }
  606. if (ptb->iButMinWidth && (ptb->iButMinWidth > (cxMax + cxExtra)))
  607. cxMax = ptb->iButMinWidth - cxExtra;
  608. if (hOldFont)
  609. SelectObject(hdc, hOldFont);
  610. ReleaseDC(ptb->ci.hwnd, hdc);
  611. }
  612. //
  613. // Need to call GrowToolbar twice, once to grow the mask, and again
  614. // to grow the buttons. (Yes, this is sick.)
  615. //
  616. cy = HeightWithString(ptb, ptb->iDyBitmap);
  617. if (!GrowToolbar(ptb, max(cxMax, cxMask), cy, GT_INSIDE | GT_MASKONLY))
  618. return(FALSE);
  619. return(GrowToolbar(ptb, cxMax, cy, GT_INSIDE));
  620. }
  621. BOOL TBChangeFont(PTBSTATE ptb, WPARAM wParam, HFONT hFont)
  622. {
  623. LOGFONT lf;
  624. BOOL fWasFontCreated = ptb->fFontCreated;
  625. if ((wParam != 0) && (wParam != SPI_SETICONTITLELOGFONT) && (wParam != SPI_SETNONCLIENTMETRICS))
  626. return(FALSE);
  627. if (!SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0))
  628. return(FALSE);
  629. if (!hFont) {
  630. if (!(hFont = CreateFontIndirect(&lf)))
  631. return(FALSE);
  632. ptb->fFontCreated = TRUE;
  633. } else {
  634. ptb->fFontCreated = FALSE;
  635. }
  636. if (ptb->hfontIcon && fWasFontCreated)
  637. DeleteObject(ptb->hfontIcon);
  638. ptb->hfontIcon = hFont;
  639. return(TBRecalc(ptb));
  640. }
  641. void TBSetFont(PTBSTATE ptb, HFONT hFont, BOOL fInval)
  642. {
  643. TBChangeFont(ptb, 0, hFont);
  644. if (fInval)
  645. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  646. }
  647. HWND WINAPI CreateToolbarEx(HWND hwnd, DWORD ws, UINT wID, int nBitmaps,
  648. HINSTANCE hBMInst, UINT_PTR wBMID, LPCTBBUTTON lpButtons,
  649. int iNumButtons, int dxButton, int dyButton,
  650. int dxBitmap, int dyBitmap, UINT uStructSize)
  651. {
  652. HWND hwndToolbar = CreateWindow(c_szToolbarClass, NULL, WS_CHILD | ws,
  653. 0, 0, 100, 30, hwnd, IntToPtr_(HMENU, wID), HINST_THISDLL, NULL);
  654. if (hwndToolbar)
  655. {
  656. TBADDBITMAP tbab;
  657. // Need to send messages, instead of call our internal functions, because we may have created
  658. // a v6 window above.
  659. SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)uStructSize, (LPARAM)0);
  660. if ((dxBitmap && dyBitmap &&
  661. !SendMessage(hwndToolbar, TB_SETBITMAPSIZE, (WPARAM)0, MAKELPARAM(dxBitmap, dyBitmap))) ||
  662. (dxButton && dyButton &&
  663. !SendMessage(hwndToolbar, TB_SETBITMAPSIZE, (WPARAM)0, MAKELPARAM(dxButton, dyButton))))
  664. {
  665. //!!!! do we actually need to deal with this?
  666. DestroyWindow(hwndToolbar);
  667. hwndToolbar = NULL;
  668. goto Error;
  669. }
  670. tbab.hInst = hBMInst;
  671. tbab.nID = wBMID;
  672. SendMessage(hwndToolbar, TB_ADDBITMAP, (WPARAM)nBitmaps, (LPARAM)&tbab);
  673. SendMessage(hwndToolbar, TB_ADDBUTTONS, (WPARAM)iNumButtons, (LPARAM)lpButtons);
  674. }
  675. Error:
  676. return hwndToolbar;
  677. }
  678. /* This is no longer declared in COMMCTRL.H. It only exists for compatibility
  679. ** with existing apps; new apps must use CreateToolbarEx.
  680. */
  681. HWND WINAPI CreateToolbar(HWND hwnd, DWORD ws, UINT wID, int nBitmaps, HINSTANCE hBMInst, UINT_PTR wBMID, LPCTBBUTTON lpButtons, int iNumButtons)
  682. {
  683. // old-style toolbar, so no divider.
  684. return CreateToolbarEx(hwnd, ws | CCS_NODIVIDER, wID, nBitmaps, hBMInst, wBMID,
  685. lpButtons, iNumButtons, 0, 0, 0, 0, sizeof(OLDTBBUTTON));
  686. }
  687. #pragma code_seg(CODESEG_INIT)
  688. BOOL InitToolbarClass(HINSTANCE hInstance)
  689. {
  690. WNDCLASS wc;
  691. wc.lpfnWndProc = ToolbarWndProc;
  692. wc.lpszClassName = c_szToolbarClass;
  693. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  694. wc.cbClsExtra = 0;
  695. wc.cbWndExtra = sizeof(PTBSTATE);
  696. wc.hInstance = hInstance; // use DLL instance if in DLL
  697. wc.hIcon = NULL;
  698. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  699. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  700. wc.lpszMenuName = NULL;
  701. RegisterClass(&wc);
  702. return TRUE;
  703. }
  704. #pragma code_seg()
  705. void PatB(HDC hdc,int x,int y,int dx,int dy, DWORD rgb)
  706. {
  707. RECT rc;
  708. SetBkColor(hdc,rgb);
  709. rc.left = x;
  710. rc.top = y;
  711. rc.right = x + dx;
  712. rc.bottom = y + dy;
  713. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  714. }
  715. // Parameter fHighlight determines whether to draw text highlighted, for
  716. // new TBSTATE_MARKED
  717. //
  718. void DrawString(HDC hdc, int x, int y, int dx, int dy, PTSTR pszString,
  719. BOOL fHighlight, TBDRAWITEM * ptbdraw)
  720. {
  721. int oldMode;
  722. COLORREF oldBkColor;
  723. COLORREF oldTextColor;
  724. RECT rcText;
  725. UINT uiStyle = 0;
  726. PTBSTATE ptb;
  727. LPTBBUTTONDATA ptbb;
  728. ASSERT(ptbdraw);
  729. ptb = ptbdraw->ptb;
  730. ptbb = ptbdraw->pbutton;
  731. if (!(ptb->ci.style & TBSTYLE_LIST) && ((ptb->iDyBitmap + ptb->yPad + g_cyEdge) >= ptb->iButHeight))
  732. // there's no room to show the text -- bail out
  733. return;
  734. if (BTN_NO_SHOW_TEXT(ptb, ptbb))
  735. // don't show text for this button -- bail out
  736. return;
  737. if (fHighlight)
  738. {
  739. oldMode = SetBkMode (hdc, ptbdraw->tbcd.nHLStringBkMode);
  740. oldBkColor = SetBkColor (hdc, ptbdraw->tbcd.clrMark);
  741. oldTextColor = SetTextColor (hdc, ptbdraw->tbcd.clrTextHighlight);
  742. }
  743. else
  744. oldMode = SetBkMode(hdc, ptbdraw->tbcd.nStringBkMode);
  745. uiStyle = TBGetDrawTextFlags(ptb, DT_END_ELLIPSIS, ptbb);
  746. // If we're ex_vertical want to center the text
  747. if (!(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  748. {
  749. if (ptb->ci.style & TBSTYLE_LIST)
  750. {
  751. dy = max(ptb->dyIconFont, ptb->iDyBitmap);
  752. }
  753. else
  754. {
  755. if (!dy || ptb->dyIconFont < dy)
  756. dy = ptb->dyIconFont;
  757. }
  758. }
  759. SetRect( &rcText, x, y, x + dx, y + dy);
  760. DrawText(hdc, (LPTSTR)pszString, -1, &rcText, uiStyle);
  761. SetBkMode(hdc, oldMode);
  762. if (fHighlight)
  763. {
  764. SetBkColor (hdc, oldBkColor);
  765. SetTextColor (hdc, oldTextColor);
  766. }
  767. }
  768. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTONDATA pTBButton)
  769. {
  770. if (TBISSTRINGPTR(pTBButton->iString))
  771. return (LPTSTR)pTBButton->iString;
  772. else {
  773. if (pTBButton->iString != -1 &&
  774. pTBButton->iString < ptb->nStrings)
  775. return ptb->pStrings[pTBButton->iString];
  776. return NULL;
  777. }
  778. }
  779. HIMAGELIST TBGetImageList(PTBSTATE ptb, int iMode, int iIndex)
  780. {
  781. HIMAGELIST himl = NULL;
  782. ASSERT(iMode <= HIML_MAX);
  783. if (iIndex >= 0 && iIndex < ptb->cPimgs) {
  784. himl = ptb->pimgs[iIndex].himl[iMode];
  785. }
  786. return himl;
  787. }
  788. //
  789. // v5 toolbars support multiple imagelists. To use images from an alternate
  790. // imagelist, set the imagelist handle via TB_SETIMAGELIST(iIndex, himlAlt)
  791. // and set your button's iImage to MAKELONG(iImage, iIndex).
  792. //
  793. // APP COMPAT: GroupWise 5.5 passes garbage as the iIndex (even though it
  794. // was documented as "must be zero"), so we enable this functionality
  795. // only for v5 toolbars. IE4 ignored the iIndex, which is why they got
  796. // away with it up until now.
  797. //
  798. #define MAX_TBIMAGELISTS 20 // arbitrary limit
  799. HIMAGELIST TBSetImageList(PTBSTATE ptb, int iMode, int iIndex, HIMAGELIST himl)
  800. {
  801. HIMAGELIST himlOld = NULL;
  802. // Watch out for app compat or for totally bogus parameters
  803. if (ptb->ci.iVersion < 5 || iIndex < 0 || iIndex >= MAX_TBIMAGELISTS)
  804. iIndex = 0;
  805. ASSERT(iMode <= HIML_MAX);
  806. if (iIndex >= ptb->cPimgs)
  807. {
  808. // asking for more than we have, realloc.
  809. void *p = CCLocalReAlloc(ptb->pimgs, (iIndex+1) * SIZEOF(TBIMAGELISTS));
  810. if (p)
  811. {
  812. ptb->pimgs = (TBIMAGELISTS*)p;
  813. ZeroMemory(&ptb->pimgs[ptb->cPimgs], (iIndex + 1 - ptb->cPimgs)*SIZEOF(TBIMAGELISTS));
  814. ptb->cPimgs = iIndex + 1; // iIndex is 0 based, but cPimgs is 1 based (it's a count, not an index)
  815. }
  816. }
  817. if (iIndex < ptb->cPimgs)
  818. {
  819. himlOld = ptb->pimgs[iIndex].himl[iMode];
  820. ptb->pimgs[iIndex].himl[iMode] = himl;
  821. }
  822. return himlOld;
  823. }
  824. // create a mono bitmap mask:
  825. // 1's where color == COLOR_BTNFACE || COLOR_3DHILIGHT
  826. // 0's everywhere else
  827. void CreateMask(int xoffset, int yoffset, int dx, int dy, BOOL fDrawGlyph, TBDRAWITEM * ptbdraw)
  828. {
  829. LPTSTR psz;
  830. HIMAGELIST himl;
  831. PTBSTATE ptb = ptbdraw->ptb;
  832. LPTBBUTTONDATA pTBButton = ptbdraw->pbutton;
  833. // initalize whole area with 1's
  834. PatBlt(ptb->hdcMono, 0, 0, dx, dy, WHITENESS);
  835. // create mask based on color bitmap
  836. // convert this to 1's
  837. himl = TBGetImageList(ptb, HIML_NORMAL, ptbdraw->iIndex);
  838. if (fDrawGlyph && himl)
  839. {
  840. IMAGELISTDRAWPARAMS imldp = {0};
  841. imldp.cbSize = sizeof(imldp);
  842. imldp.himl = himl;
  843. imldp.i = ptbdraw->iImage;
  844. imldp.hdcDst = ptb->hdcMono;
  845. imldp.x = xoffset;
  846. imldp.y = yoffset;
  847. imldp.cx = 0;
  848. imldp.cy = 0;
  849. imldp.xBitmap= 0;
  850. imldp.yBitmap= 0;
  851. imldp.rgbBk = g_clrBtnFace;
  852. imldp.rgbFg = CLR_DEFAULT;
  853. imldp.fStyle = ILD_ROP | ILD_MASK;
  854. imldp.dwRop = SRCCOPY;
  855. ImageList_DrawIndirect(&imldp);
  856. imldp.fStyle = ILD_ROP | ILD_IMAGE;
  857. imldp.rgbBk = g_clrBtnHighlight;
  858. imldp.dwRop = SRCPAINT;
  859. ImageList_DrawIndirect(&imldp);
  860. }
  861. psz = TB_StrForButton(ptb, pTBButton);
  862. if (psz)
  863. {
  864. xoffset = 1;
  865. yoffset = 1;
  866. if (ptb->ci.style & TBSTYLE_LIST)
  867. {
  868. if (!(pTBButton->iBitmap == I_IMAGENONE &&
  869. (pTBButton->fsStyle & BTNS_AUTOSIZE)))
  870. {
  871. xoffset += ptb->iDxBitmap + ptb->iListGap;
  872. dx -= ptb->iDxBitmap + ptb->iListGap;
  873. }
  874. }
  875. else
  876. {
  877. yoffset += ptb->iDyBitmap + 1;
  878. dy -= ptb->iDyBitmap + 1;
  879. }
  880. if (!(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  881. {
  882. dx -= g_cxEdge;
  883. dy -= g_cyEdge;
  884. }
  885. // The FALSE in 4th param is so we don't get a box in the mask.
  886. DrawString(ptb->hdcMono, xoffset, yoffset, dx, dy, psz,
  887. FALSE, ptbdraw);
  888. }
  889. }
  890. void DrawBlankButton(HDC hdc, int x, int y, int dx, int dy, TBDRAWITEM * ptbdraw)
  891. {
  892. RECT r1;
  893. UINT state;
  894. // face color
  895. // The Office toolbar sends us bitmaps that are smaller than they claim they are
  896. // So we need to do the PatB or the window background shows through around the
  897. // edges of the button bitmap -jjk
  898. ASSERT(ptbdraw);
  899. state = ptbdraw->state;
  900. if (!(state & TBSTATE_CHECKED))
  901. PatB(hdc, x, y, dx, dy, ptbdraw->tbcd.clrBtnFace);
  902. if ( !(ptbdraw->dwCustom & TBCDRF_NOEDGES))
  903. {
  904. r1.left = x;
  905. r1.top = y;
  906. r1.right = x + dx;
  907. r1.bottom = y + dy;
  908. DrawEdge(hdc, &r1, (state & (TBSTATE_CHECKED | TBSTATE_PRESSED)) ? EDGE_SUNKEN : EDGE_RAISED, BF_RECT | BF_SOFT);
  909. }
  910. }
  911. // these are raster ops
  912. #define DSPDxax 0x00E20746 // BUGBUG: not used
  913. #define PSDPxax 0x00B8074A
  914. HWND g_hwndDebug = NULL;
  915. void DrawFace(HDC hdc, int x, int y, int offx, int offy, int dxText,
  916. int dyText, TBDRAWITEM * ptbdraw)
  917. {
  918. LPTSTR psz;
  919. IMAGELISTDRAWPARAMS imldp = {0};
  920. BOOL fHotTrack = FALSE;
  921. UINT state;
  922. PTBSTATE ptb;
  923. LPTBBUTTONDATA ptButton;
  924. BOOL fImage; // !fImage means no image (as opposed to a blank image)
  925. ASSERT(ptbdraw);
  926. ptb = ptbdraw->ptb;
  927. ptButton = ptbdraw->pbutton;
  928. // AutosizeTextNoImage
  929. if ((ptb->ci.style & TBSTYLE_LIST) &&
  930. (ptbdraw->iImage == I_IMAGENONE) &&
  931. (ptButton->fsStyle & BTNS_AUTOSIZE)) {
  932. fImage = FALSE;
  933. } else {
  934. fImage = TRUE;
  935. }
  936. state = ptbdraw->state;
  937. if (state & TBSTATE_ENABLED)
  938. {
  939. fHotTrack = ptbdraw->fHotTrack;
  940. if (ptb->ci.style & TBSTYLE_FLAT)
  941. {
  942. UINT bdr = 0;
  943. if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED))
  944. bdr = BDR_SUNKENOUTER;
  945. else if (fHotTrack)
  946. bdr = BDR_RAISEDINNER;
  947. if (bdr)
  948. {
  949. RECT rc;
  950. TB_GetItemRect(ptb, (UINT)(ptButton - ptb->Buttons), &rc);
  951. if (TB_HasSplitDDArrow(ptb, ptButton))
  952. rc.right -= ptb->dxDDArrowChar;
  953. if (!(ptbdraw->dwCustom & TBCDRF_NOEDGES) && ptb)
  954. CCDrawEdge(hdc, &rc, bdr, BF_RECT, &(ptb->clrsc));
  955. }
  956. }
  957. }
  958. imldp.himl = NULL;
  959. if (fHotTrack || (state & TBSTATE_CHECKED)) {
  960. imldp.himl = TBGetImageList(ptb, HIML_HOT, ptbdraw->iIndex);
  961. if (!imldp.himl)
  962. imldp.himl = TBGetImageList(ptb, HIML_NORMAL, ptbdraw->iIndex);
  963. } else if (DRAW_MONO_BTN(ptb, state) && (imldp.himl = TBGetImageList(ptb, HIML_DISABLED, ptbdraw->iIndex))) {
  964. // assigned in if statement
  965. } else if (imldp.himl = TBGetImageList(ptb, HIML_NORMAL, ptbdraw->iIndex)) {
  966. // assigned in if statement
  967. }
  968. if (imldp.himl && (ptbdraw->iImage != -1) && fImage)
  969. {
  970. COLORREF rgbBk = ptbdraw->tbcd.clrBtnFace;
  971. if (ptb->ci.style & TBSTYLE_TRANSPARENT)
  972. rgbBk = CLR_NONE;
  973. if (ptb->dwStyleEx & TBSTYLE_EX_INVERTIBLEIMAGELIST)
  974. rgbBk = CLR_DEFAULT;
  975. imldp.cbSize = sizeof(imldp);
  976. imldp.i = ptbdraw->iImage;
  977. imldp.hdcDst = hdc;
  978. imldp.x = x + offx;
  979. imldp.y = y + offy;
  980. imldp.cx = 0;
  981. imldp.cy = 0;
  982. imldp.xBitmap= 0;
  983. imldp.yBitmap= 0;
  984. imldp.rgbBk = rgbBk;
  985. imldp.rgbFg = CLR_DEFAULT;
  986. imldp.fStyle = ILD_NORMAL;
  987. if (state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE))
  988. imldp.fStyle = ILD_TRANSPARENT;
  989. #ifdef TBHIGHLIGHT_GLYPH
  990. if ((state & TBSTATE_MARKED) && !(ptbdraw->dwCustom & TBCDRF_NOMARK))
  991. imldp.fStyle = ILD_TRANSPARENT | ILD_BLEND50;
  992. #endif
  993. if (ptbdraw->dwCustom & TBCDRF_BLENDICON)
  994. imldp.fStyle = ILD_TRANSPARENT | ILD_BLEND50;
  995. ImageList_DrawIndirect(&imldp);
  996. #ifdef DEBUG
  997. if (g_hwndDebug == ptb->ci.hwnd) {
  998. imldp.hdcDst = GetDC(NULL);
  999. ImageList_DrawIndirect(&imldp);
  1000. ReleaseDC(NULL, imldp.hdcDst);
  1001. }
  1002. #endif
  1003. }
  1004. psz = TB_StrForButton(ptb, ptButton);
  1005. if (psz)
  1006. {
  1007. BOOL bHighlight = (state & TBSTATE_MARKED) && (ptb->ci.style & TBSTYLE_LIST) &&
  1008. !(ptbdraw->dwCustom & TBCDRF_NOMARK);
  1009. if ((state & (TBSTATE_PRESSED | TBSTATE_CHECKED)) &&
  1010. !(ptbdraw->dwCustom & TBCDRF_NOOFFSET))
  1011. {
  1012. x++;
  1013. if (ptb->ci.style & TBSTYLE_LIST)
  1014. y++;
  1015. }
  1016. if (ptb->ci.style & TBSTYLE_LIST)
  1017. {
  1018. if (fImage)
  1019. {
  1020. x += ptb->iDxBitmap + ptb->iListGap;
  1021. dxText -= ptb->iDxBitmap + ptb->iListGap;
  1022. }
  1023. else
  1024. {
  1025. // fudge for I_IMAGENONE buttons
  1026. x += g_cxEdge;
  1027. }
  1028. }
  1029. else
  1030. {
  1031. y += offy + ptb->iDyBitmap;
  1032. dyText -= offy + ptb->iDyBitmap;
  1033. }
  1034. DrawString(hdc, x + 1, y + 1, dxText, dyText, psz, bHighlight, ptbdraw);
  1035. }
  1036. }
  1037. void InitTBDrawItem(TBDRAWITEM * ptbdraw, PTBSTATE ptb, LPTBBUTTONDATA pbutton,
  1038. UINT state, BOOL fHotTrack, int dxText, int dyText)
  1039. {
  1040. NMTBCUSTOMDRAW * ptbcd;
  1041. NMCUSTOMDRAW * pnmcd;
  1042. ASSERT(ptbdraw);
  1043. ptbdraw->ptb = ptb;
  1044. ptbdraw->pbutton = pbutton;
  1045. ptbdraw->fHotTrack = fHotTrack;
  1046. ptbdraw->iIndex = GET_HIML_INDEX(pbutton->DUMMYUNION_MEMBER(iBitmap));
  1047. ptbdraw->iImage = GET_IMAGE_INDEX(pbutton->DUMMYUNION_MEMBER(iBitmap));
  1048. ptbdraw->state = state;
  1049. ptbcd = &ptbdraw->tbcd;
  1050. ptbcd->hbrMonoDither = g_hbrMonoDither;
  1051. ptbcd->hbrLines = GetStockObject(BLACK_BRUSH);
  1052. ptbcd->hpenLines = GetStockObject(BLACK_PEN);
  1053. ptbcd->clrMark = g_clrHighlight;
  1054. ptbcd->clrBtnHighlight = g_clrBtnHighlight;
  1055. ptbcd->clrTextHighlight = g_clrHighlightText;
  1056. ptbcd->clrBtnFace = g_clrBtnFace;
  1057. ptbcd->nStringBkMode = TRANSPARENT;
  1058. ptbcd->nHLStringBkMode = OPAQUE;
  1059. ptbcd->clrText = g_clrBtnText;
  1060. SetRect(&ptbcd->rcText, 0, 0, dxText, dyText);
  1061. pnmcd = (NMCUSTOMDRAW *)ptbcd;
  1062. pnmcd->uItemState = CDISFromState(state);
  1063. if ((ptb->ci.style & TBSTYLE_FLAT) && fHotTrack)
  1064. pnmcd->uItemState |= CDIS_HOT;
  1065. }
  1066. void DrawButton(HDC hdc, int x, int y, PTBSTATE ptb, LPTBBUTTONDATA ptButton, BOOL fActive)
  1067. {
  1068. // BUGBUG: cleanup -- separate layout calculation & rendering
  1069. int yOffset;
  1070. HBRUSH hbrOld;
  1071. UINT state;
  1072. int dxFace, dyFace;
  1073. int dxText, dyText;
  1074. int xCenterOffset;
  1075. int dx = TBWidthOfButton(ptb, ptButton, hdc);
  1076. HFONT oldhFont;
  1077. int dy = ptb->iButHeight;
  1078. TBDRAWITEM tbdraw = { 0 };
  1079. NMTBCUSTOMDRAW * ptbcd = &tbdraw.tbcd;
  1080. NMCUSTOMDRAW * pnmcd = (NMCUSTOMDRAW *)ptbcd;
  1081. COLORREF clrSave;
  1082. BOOL fHotTrack;
  1083. HFONT hFontNoAntiAlias = NULL;
  1084. state = (UINT)ptButton->fsState;
  1085. // make local copy of state and do proper overriding
  1086. if (state & TBSTATE_INDETERMINATE) {
  1087. if (state & TBSTATE_PRESSED)
  1088. state &= ~TBSTATE_INDETERMINATE;
  1089. else if (state & TBSTATE_ENABLED)
  1090. state = TBSTATE_INDETERMINATE;
  1091. else
  1092. state &= ~TBSTATE_INDETERMINATE;
  1093. }
  1094. if (!fActive) {
  1095. state &= ~TBSTATE_ENABLED;
  1096. }
  1097. fHotTrack = TBIsHotTrack(ptb, ptButton, state);
  1098. pnmcd->hdc = hdc;
  1099. pnmcd->dwItemSpec = ptButton->idCommand;
  1100. pnmcd->uItemState = 0;
  1101. pnmcd->lItemlParam = (LPARAM)ptButton->dwData;
  1102. SetRect(&pnmcd->rc, x, y, x + dx, y + dy);
  1103. dxText = dx - (3 * g_cxEdge);
  1104. if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1105. {
  1106. dyText = dy;
  1107. }
  1108. else
  1109. {
  1110. dyText = dy - (2 * g_cyEdge);
  1111. }
  1112. InitTBDrawItem(&tbdraw, ptb, ptButton, state, fHotTrack, dxText, dyText);
  1113. tbdraw.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, (NMCUSTOMDRAW *)ptbcd);
  1114. // We gotta update our concept of hotness
  1115. tbdraw.fHotTrack = fHotTrack = pnmcd->uItemState & CDIS_HOT;
  1116. if (!(tbdraw.dwCustom & CDRF_SKIPDEFAULT ))
  1117. {
  1118. // Get the state back from what custom draw may have set
  1119. state = tbdraw.state = StateFromCDIS(pnmcd->uItemState);
  1120. dxFace = dx - (2 * g_cxEdge);
  1121. dyFace = dy - (2 * g_cyEdge);
  1122. dxText = ptbcd->rcText.right - ptbcd->rcText.left;
  1123. dyText = ptbcd->rcText.bottom - ptbcd->rcText.top;
  1124. if (TB_HasDDArrow(ptb, ptButton) && !TB_HasTopDDArrow(ptb, ptButton)) {
  1125. int iAdjust = TBDDArrowAdjustment(ptb, ptButton);
  1126. dxFace -= iAdjust;
  1127. dxText -= iAdjust;
  1128. }
  1129. // Should we display the font using the GDI AntiAliasing?
  1130. if (!ptb->fAntiAlias)
  1131. {
  1132. // No. Must be doing drag and drop. We don't want to AntiAlias because the
  1133. // Purple color key will show through and it looks ugly.
  1134. LOGFONT lfFont;
  1135. if (GetObject(ptb->hfontIcon, sizeof(lfFont), &lfFont))
  1136. {
  1137. lfFont.lfQuality = NONANTIALIASED_QUALITY;
  1138. hFontNoAntiAlias = CreateFontIndirect(&lfFont);
  1139. }
  1140. }
  1141. if (hFontNoAntiAlias)
  1142. oldhFont = SelectObject(hdc, hFontNoAntiAlias);
  1143. else
  1144. oldhFont = SelectObject(hdc, ptb->hfontIcon);
  1145. clrSave = SetTextColor(hdc, ptbcd->clrText);
  1146. if (!(ptb->ci.style & TBSTYLE_FLAT))
  1147. DrawBlankButton(hdc, x, y, dx, dy, &tbdraw);
  1148. // move coordinates inside border and away from upper left highlight.
  1149. // the extents change accordingly.
  1150. x += g_cxEdge;
  1151. y += g_cyEdge;
  1152. if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1153. {
  1154. yOffset = (ptb->iButHeight - ptb->iDyBitmap) / 2;
  1155. }
  1156. else
  1157. {
  1158. // calculate offset of face from (x,y). y is always from the top,
  1159. // so the offset is easy. x needs to be centered in face.
  1160. // center it taking the padding into account the padding area
  1161. yOffset = (ptb->yPad - (2 * g_cyEdge)) / 2;
  1162. }
  1163. if (yOffset < 0)
  1164. yOffset = 0;
  1165. if ((ptb->ci.style & TBSTYLE_LIST) && !BTN_NO_SHOW_TEXT(ptb, ptButton)) {
  1166. xCenterOffset = ptb->xPad / 2;
  1167. } else if (TB_HasTopDDArrow(ptb, ptButton)) {
  1168. //
  1169. // Layout of "top dropdown" buttons looks like this:
  1170. //
  1171. // icon
  1172. // fudge | dropdown arrow
  1173. // | | |
  1174. // v v v
  1175. // +-+-+-------+--+-+
  1176. // | | | | | |
  1177. // | | | | | |
  1178. // +-+-+-------+--+-+
  1179. // | <text> |
  1180. // +----------------+
  1181. //
  1182. // |<--- dxFace --->|
  1183. //
  1184. // xCenterOffset is the offset at which to start drawing the icon.
  1185. //
  1186. xCenterOffset = (dxFace + CX_TOP_FUDGE - (ptb->iDxBitmap + ptb->dxDDArrowChar)) / 2;
  1187. } else {
  1188. xCenterOffset = (dxFace - ptb->iDxBitmap) / 2;
  1189. }
  1190. if (state & (TBSTATE_PRESSED | TBSTATE_CHECKED) &&
  1191. !(tbdraw.dwCustom & TBCDRF_NOOFFSET))
  1192. {
  1193. // pressed state moves down and to the right
  1194. xCenterOffset++;
  1195. yOffset++;
  1196. }
  1197. // draw the dithered background
  1198. if ((!fHotTrack || ptb->ci.iVersion < 5) &&
  1199. (((state & (TBSTATE_CHECKED | TBSTATE_INDETERMINATE)) ||
  1200. ((state & TBSTATE_MARKED) &&
  1201. !(ptb->ci.style & TBSTYLE_FLAT) &&
  1202. !(tbdraw.dwCustom & TBCDRF_NOMARK)))))
  1203. {
  1204. //Custom Draw can set hbrMonoDither to be NULL. Validate it before using it
  1205. hbrOld = ptbcd->hbrMonoDither ? SelectObject(hdc, ptbcd->hbrMonoDither) : NULL;
  1206. if (hbrOld)
  1207. {
  1208. COLORREF clrText, clrBack;
  1209. #ifdef TBHIGHLIGHT_BACK
  1210. if (state & TBSTATE_MARKED)
  1211. clrText = SetTextColor(hdc, ptbcd->clrMark);
  1212. else
  1213. #endif
  1214. clrText = SetTextColor(hdc, ptbcd->clrBtnHighlight); // 0 -> 0
  1215. clrBack = SetBkColor(hdc, ptbcd->clrBtnFace); // 1 -> 1
  1216. // only draw the dither brush where the mask is 1's
  1217. PatBlt(hdc, x, y, dxFace, dyFace, PATCOPY);
  1218. SelectObject(hdc, hbrOld);
  1219. SetTextColor(hdc, clrText);
  1220. SetBkColor(hdc, clrBack);
  1221. }
  1222. }
  1223. // Paint the background of the hot-tracked item if the
  1224. // custom draw said so
  1225. if ((tbdraw.dwCustom & TBCDRF_HILITEHOTTRACK) && fHotTrack)
  1226. {
  1227. PatB(hdc, pnmcd->rc.left, pnmcd->rc.top,
  1228. pnmcd->rc.right - pnmcd->rc.left, pnmcd->rc.bottom - pnmcd->rc.top,
  1229. ptbcd->clrHighlightHotTrack);
  1230. }
  1231. tbdraw.iImage = ptButton->DUMMYUNION_MEMBER(iBitmap);
  1232. if((ptButton->DUMMYUNION_MEMBER(iBitmap) == I_IMAGECALLBACK) && ptb->fHimlNative)
  1233. {
  1234. NMTBDISPINFO tbgdi = {0};
  1235. tbgdi.dwMask = TBNF_IMAGE;
  1236. TBGetItem(ptb,ptButton,&tbgdi);
  1237. tbdraw.iImage = tbgdi.iImage;
  1238. }
  1239. tbdraw.iIndex = GET_HIML_INDEX(tbdraw.iImage);
  1240. tbdraw.iImage = GET_IMAGE_INDEX(tbdraw.iImage);
  1241. // Now put on the face.
  1242. // TODO: Validate himlDisabled and ensure that the index is in range
  1243. if (!DRAW_MONO_BTN(ptb, state) ||
  1244. TBGetImageList(ptb, HIML_DISABLED, tbdraw.iIndex))
  1245. {
  1246. // regular version
  1247. int yStart = y;
  1248. if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1249. yStart -= g_cyEdge;
  1250. DrawFace(hdc, x, yStart, xCenterOffset, yOffset, dxText, dyText, &tbdraw);
  1251. }
  1252. if (DRAW_MONO_BTN(ptb, state))
  1253. {
  1254. HBITMAP hbmOld;
  1255. //initialize the monochrome dc
  1256. if (!ptb->hdcMono) {
  1257. ptb->hdcMono = CreateCompatibleDC(hdc);
  1258. if (!ptb->hdcMono)
  1259. return;
  1260. SetTextColor(ptb->hdcMono, 0L);
  1261. SelectObject(ptb->hdcMono, ptb->hfontIcon);
  1262. }
  1263. hbmOld = SelectObject(ptb->hdcMono, ptb->hbmMono);
  1264. //
  1265. // If we a mirrored DC, mirror the Memory DC so that
  1266. // text written on the bitmap won't get flipped.
  1267. //
  1268. if ((IS_DC_RTL_MIRRORED(hdc)) &&
  1269. (!(IS_DC_RTL_MIRRORED(ptb->hdcMono))))
  1270. {
  1271. SET_DC_RTL_MIRRORED(ptb->hdcMono);
  1272. }
  1273. // disabled version (or indeterminate)
  1274. CreateMask(xCenterOffset, yOffset, dxFace, dyFace, (TBGetImageList(ptb, HIML_DISABLED, tbdraw.iIndex) == NULL), &tbdraw);
  1275. SetTextColor(hdc, 0L); // 0's in mono -> 0 (for ROP)
  1276. SetBkColor(hdc, 0x00FFFFFF); // 1's in mono -> 1
  1277. // draw glyph's etched-effect
  1278. if (!(state & TBSTATE_INDETERMINATE) &&
  1279. !(tbdraw.dwCustom & TBCDRF_NOETCHEDEFFECT)) {
  1280. hbrOld = SelectObject(hdc, g_hbrBtnHighlight);
  1281. if (hbrOld) {
  1282. // draw hilight color where we have 0's in the mask
  1283. BitBlt(hdc, x + 1, y + 1, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
  1284. SelectObject(hdc, hbrOld);
  1285. }
  1286. }
  1287. // gray out glyph
  1288. hbrOld = SelectObject(hdc, g_hbrBtnShadow);
  1289. if (hbrOld) {
  1290. // draw the shadow color where we have 0's in the mask
  1291. BitBlt(hdc, x, y, dxFace, dyFace, ptb->hdcMono, 0, 0, PSDPxax);
  1292. SelectObject(hdc, hbrOld);
  1293. }
  1294. if (state & TBSTATE_CHECKED) {
  1295. BitBlt(ptb->hdcMono, 1, 1, dxFace - 1, dyFace - 1, ptb->hdcMono, 0, 0, SRCAND);
  1296. }
  1297. SelectObject(ptb->hdcMono, hbmOld);
  1298. }
  1299. if (TB_HasDDArrow(ptb, ptButton))
  1300. {
  1301. WORD wDSAFlags = DCHF_TRANSPARENT | DCHF_FLIPPED;
  1302. BOOL fPressedDD = ((ptb->Buttons + ptb->iPressedDD) == ptButton);
  1303. RECT rc;
  1304. if (TB_HasTopDDArrow(ptb, ptButton)) {
  1305. // position the dd arrow up next to the bitmap
  1306. rc.left = x + xCenterOffset + ptb->iDxBitmap;
  1307. rc.right = rc.left + ptb->dxDDArrowChar;
  1308. rc.top = y + yOffset;
  1309. rc.bottom = rc.top + ptb->iDyBitmap;
  1310. }
  1311. else
  1312. {
  1313. // position the dd arrow to the right of the text & bitmap
  1314. TB_GetItemRect(ptb, (UINT)(ptButton - ptb->Buttons), &rc);
  1315. rc.left = rc.right - ptb->dxDDArrowChar;
  1316. }
  1317. if (TB_HasUnsplitDDArrow(ptb, ptButton)) {
  1318. // if a non-split dd arrow, don't draw a border.
  1319. wDSAFlags |= DCHF_NOBORDER;
  1320. }
  1321. if (DRAW_MONO_BTN(ptb, state)) {
  1322. // DFCS_INACTIVE means "draw the arrow part grayed"
  1323. wDSAFlags |= DCHF_INACTIVE;
  1324. }
  1325. // if TB_HasTopDDArrow, we've already offset rect, so don't draw DCHF_PUSHED
  1326. else if ((fPressedDD || (state & (TBSTATE_CHECKED | TBSTATE_PRESSED))) &&
  1327. !TB_HasTopDDArrow(ptb, ptButton)) {
  1328. // DCHF_PUSHED means "offset the arrow and draw indented border"
  1329. wDSAFlags |= DCHF_PUSHED;
  1330. }
  1331. else if (fHotTrack || !(ptb->ci.style & TBSTYLE_FLAT)) {
  1332. // DCHF_HOT means "draw raised border"
  1333. // non-flat dropdown arrows are either pushed or hot
  1334. wDSAFlags |= DCHF_HOT;
  1335. }
  1336. DrawScrollArrow(hdc, &rc, wDSAFlags);
  1337. }
  1338. SelectObject(hdc, oldhFont);
  1339. SetTextColor(hdc, clrSave);
  1340. if (hFontNoAntiAlias)
  1341. {
  1342. DeleteObject(hFontNoAntiAlias);
  1343. }
  1344. }
  1345. if (tbdraw.dwCustom & CDRF_NOTIFYPOSTPAINT)
  1346. CICustomDrawNotify(&ptb->ci, CDDS_ITEMPOSTPAINT, (NMCUSTOMDRAW *)ptbcd);
  1347. }
  1348. // make sure that g_hbmMono is big enough to do masks for this
  1349. // size of button. if not, fail.
  1350. BOOL CheckMonoMask(PTBSTATE ptb, int width, int height)
  1351. {
  1352. BITMAP bm;
  1353. HBITMAP hbmTemp;
  1354. if (ptb->hbmMono) {
  1355. GetObject(ptb->hbmMono, sizeof(BITMAP), &bm);
  1356. if (width <= bm.bmWidth && height <= bm.bmHeight) {
  1357. return TRUE;
  1358. }
  1359. }
  1360. // Add a bit of fudge to keep this from being reallocated too often.
  1361. hbmTemp = CreateMonoBitmap(width+8, height+8);
  1362. if (!hbmTemp)
  1363. return FALSE;
  1364. if (ptb->hbmMono)
  1365. DeleteObject(ptb->hbmMono);
  1366. ptb->hbmMono = hbmTemp;
  1367. return TRUE;
  1368. }
  1369. /*
  1370. ** GrowToolbar
  1371. **
  1372. ** Attempt to grow the button size.
  1373. **
  1374. ** The calling function can either specify a new internal measurement
  1375. ** (GT_INSIDE) or a new external measurement.
  1376. **
  1377. ** GT_MASKONLY updates the mono mask and nothing else.
  1378. */
  1379. BOOL GrowToolbar(PTBSTATE ptb, int newButWidth, int newButHeight, UINT flags)
  1380. {
  1381. if (!newButWidth)
  1382. newButWidth = DEFAULTBUTTONX;
  1383. if (!newButHeight)
  1384. newButHeight = DEFAULTBUTTONY;
  1385. // if growing based on inside measurement, get full size
  1386. if (flags & GT_INSIDE)
  1387. {
  1388. if (ptb->ci.style & TBSTYLE_LIST)
  1389. newButWidth += ptb->iDxBitmap + ptb->iListGap;
  1390. newButHeight += ptb->yPad;
  1391. newButWidth += ptb->xPad;
  1392. // if toolbar already has strings, don't shrink width it because it
  1393. // might clip room for the string
  1394. if ((newButWidth < ptb->iButWidth) && ptb->nStrings &&
  1395. (ptb->ci.iVersion < 5 || ptb->nTextRows > 0))
  1396. newButWidth = ptb->iButWidth;
  1397. }
  1398. else {
  1399. if (newButHeight == -1)
  1400. newButHeight = ptb->iButHeight;
  1401. if (newButWidth == -1)
  1402. newButWidth = ptb->iButWidth;
  1403. if (newButHeight < ptb->iDyBitmap + ptb->yPad)
  1404. newButHeight = ptb->iDyBitmap + ptb->yPad;
  1405. if (newButWidth < ptb->iDxBitmap + ptb->xPad)
  1406. newButWidth = ptb->iDxBitmap + ptb->xPad;
  1407. }
  1408. // if the size of the toolbar is actually growing, see if shadow
  1409. // bitmaps can be made sufficiently large.
  1410. if (!ptb->hbmMono || (newButWidth > ptb->iButWidth) || (newButHeight > ptb->iButHeight)) {
  1411. if (!CheckMonoMask(ptb, newButWidth, newButHeight))
  1412. return(FALSE);
  1413. }
  1414. if (flags & GT_MASKONLY)
  1415. return(TRUE);
  1416. if (!(flags & GT_INSIDE) && ((ptb->iButWidth != newButWidth) || (ptb->iButHeight != newButHeight)))
  1417. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1418. ptb->iButWidth = newButWidth;
  1419. ptb->iButHeight = newButHeight;
  1420. // bar height has 2 pixels above, 2 below
  1421. if (ptb->ci.style & TBSTYLE_FLAT)
  1422. ptb->iYPos = 0;
  1423. else
  1424. ptb->iYPos = 2;
  1425. TBInvalidateItemRects(ptb);
  1426. return TRUE;
  1427. }
  1428. BOOL SetBitmapSize(PTBSTATE ptb, int width, int height)
  1429. {
  1430. int realh;
  1431. if (!width)
  1432. width = 1;
  1433. if (!height)
  1434. height = 1;
  1435. if (width == -1)
  1436. width = ptb->iDxBitmap;
  1437. if (height == -1)
  1438. height = ptb->iDyBitmap;
  1439. realh = height;
  1440. if ((ptb->iDxBitmap == width) && (ptb->iDyBitmap == height))
  1441. return TRUE;
  1442. if (TBHasStrings(ptb))
  1443. realh = HeightWithString(ptb, height);
  1444. if (GrowToolbar(ptb, width, realh, GT_INSIDE)) {
  1445. ptb->iDxBitmap = width;
  1446. ptb->iDyBitmap = height;
  1447. // the size changed, we need to rebuild the imagelist
  1448. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1449. TBInvalidateImageList(ptb);
  1450. return TRUE;
  1451. }
  1452. return FALSE;
  1453. }
  1454. void TB_OnSysColorChange(PTBSTATE ptb)
  1455. {
  1456. int i;
  1457. InitGlobalColors();
  1458. // Reset all of the bitmaps
  1459. for (i = 0; i < ptb->cPimgs; i++) {
  1460. HIMAGELIST himl = TBGetImageList(ptb, HIML_NORMAL, i);
  1461. if (himl)
  1462. ImageList_SetBkColor(himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  1463. himl = TBGetImageList(ptb, HIML_HOT, i);
  1464. if (himl)
  1465. ImageList_SetBkColor(himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  1466. }
  1467. }
  1468. #define CACHE 0x01
  1469. #define BUILD 0x02
  1470. void PASCAL ReleaseMonoDC(PTBSTATE ptb)
  1471. {
  1472. if (ptb->hdcMono) {
  1473. SelectObject(ptb->hdcMono, g_hfontSystem);
  1474. DeleteDC(ptb->hdcMono);
  1475. ptb->hdcMono = NULL;
  1476. }
  1477. }
  1478. void TB_OnEraseBkgnd(PTBSTATE ptb, HDC hdc)
  1479. {
  1480. NMTBCUSTOMDRAW tbcd = { 0 };
  1481. DWORD dwRes = FALSE;
  1482. tbcd.nmcd.hdc = hdc;
  1483. if (ptb->ci.style & TBSTYLE_CUSTOMERASE) {
  1484. ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREERASE, (NMCUSTOMDRAW *)&tbcd);
  1485. } else {
  1486. ptb->ci.dwCustom = CDRF_DODEFAULT;
  1487. }
  1488. if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
  1489. {
  1490. // for transparent toolbars, forward erase background to parent
  1491. // but handle thru DefWindowProc in the event parent doesn't paint
  1492. if (!(ptb->ci.style & TBSTYLE_TRANSPARENT) ||
  1493. !CCForwardEraseBackground(ptb->ci.hwnd, hdc))
  1494. DefWindowProc(ptb->ci.hwnd, WM_ERASEBKGND, (WPARAM) hdc, 0);
  1495. }
  1496. if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTERASE)
  1497. CICustomDrawNotify(&ptb->ci, CDDS_POSTERASE, (NMCUSTOMDRAW *)&tbcd);
  1498. }
  1499. void PASCAL DrawInsertMark(HDC hdc, LPRECT prc, BOOL fHorizMode, COLORREF clr)
  1500. {
  1501. HPEN hPnMark = CreatePen(PS_SOLID, 1, clr);
  1502. HPEN hOldPn;
  1503. POINT rgPoint[4];
  1504. if (!hPnMark)
  1505. hPnMark = (HPEN)GetStockObject(BLACK_PEN); // fallback to draw with black pen
  1506. hOldPn = (HPEN)SelectObject(hdc, (HGDIOBJ)hPnMark);
  1507. if ( fHorizMode )
  1508. {
  1509. int iXCentre = (prc->left + prc->right) /2;
  1510. rgPoint[0].x = iXCentre + 1;
  1511. rgPoint[0].y = prc->top + 2;
  1512. rgPoint[1].x = iXCentre + 3;
  1513. rgPoint[1].y = prc->top;
  1514. rgPoint[2].x = iXCentre - 2;
  1515. rgPoint[2].y = prc->top;
  1516. rgPoint[3].x = iXCentre;
  1517. rgPoint[3].y = prc->top + 2;
  1518. // draw the top bit...
  1519. Polyline( hdc, rgPoint, 4 );
  1520. rgPoint[0].x = iXCentre;
  1521. rgPoint[0].y = prc->top;
  1522. rgPoint[1].x = iXCentre;
  1523. rgPoint[1].y = prc->bottom - 1;
  1524. rgPoint[2].x = iXCentre + 1;
  1525. rgPoint[2].y = prc->bottom - 1;
  1526. rgPoint[3].x = iXCentre + 1;
  1527. rgPoint[3].y = prc->top;
  1528. // draw the middle...
  1529. Polyline( hdc, rgPoint, 4 );
  1530. rgPoint[0].x = iXCentre + 1;
  1531. rgPoint[0].y = prc->bottom - 3;
  1532. rgPoint[1].x = iXCentre + 3;
  1533. rgPoint[1].y = prc->bottom - 1;
  1534. rgPoint[2].x = iXCentre - 2;
  1535. rgPoint[2].y = prc->bottom - 1;
  1536. rgPoint[3].x = iXCentre;
  1537. rgPoint[3].y = prc->bottom - 3;
  1538. // draw the bottom bit...
  1539. Polyline( hdc, rgPoint, 4 );
  1540. }
  1541. else
  1542. {
  1543. int iYCentre = (prc->top + prc->bottom) /2;
  1544. rgPoint[0].x = prc->left + 2;
  1545. rgPoint[0].y = iYCentre;
  1546. rgPoint[1].x = prc->left;
  1547. rgPoint[1].y = iYCentre - 2;
  1548. rgPoint[2].x = prc->left;
  1549. rgPoint[2].y = iYCentre + 3;
  1550. rgPoint[3].x = prc->left + 2;
  1551. rgPoint[3].y = iYCentre + 1;
  1552. // draw the top bit...
  1553. Polyline( hdc, rgPoint, 4 );
  1554. rgPoint[0].x = prc->left;
  1555. rgPoint[0].y = iYCentre;
  1556. rgPoint[1].x = prc->right - 1;
  1557. rgPoint[1].y = iYCentre;
  1558. rgPoint[2].x = prc->right - 1;
  1559. rgPoint[2].y = iYCentre + 1;
  1560. rgPoint[3].x = prc->left;
  1561. rgPoint[3].y = iYCentre + 1;
  1562. // draw the middle...
  1563. Polyline( hdc, rgPoint, 4 );
  1564. rgPoint[0].x = prc->right - 3;
  1565. rgPoint[0].y = iYCentre;
  1566. rgPoint[1].x = prc->right - 1;
  1567. rgPoint[1].y = iYCentre - 2;
  1568. rgPoint[2].x = prc->right - 1;
  1569. rgPoint[2].y = iYCentre + 3;
  1570. rgPoint[3].x = prc->right - 3;
  1571. rgPoint[3].y = iYCentre + 1;
  1572. // draw the bottom bit...
  1573. Polyline( hdc, rgPoint, 4 );
  1574. }
  1575. SelectObject( hdc, hOldPn );
  1576. DeleteObject((HGDIOBJ)hPnMark);
  1577. }
  1578. BOOL TBIsRectClipped(PTBSTATE ptb, LPRECT prc)
  1579. {
  1580. RECT rc;
  1581. RECT rcTB;
  1582. if (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN)
  1583. CopyRect(&rcTB, &ptb->rc);
  1584. else
  1585. GetClientRect(ptb->ci.hwnd, &rcTB);
  1586. if (IntersectRect(&rc, &rcTB, prc)) {
  1587. if (EqualRect(prc, &rc))
  1588. return FALSE;
  1589. }
  1590. return TRUE;
  1591. }
  1592. BOOL TBShouldDrawButton(PTBSTATE ptb, LPRECT prcBtn, HDC hdc)
  1593. {
  1594. // don't bother drawing buttons that aren't in the dc clipping region
  1595. if (RectVisible(hdc, prcBtn)) {
  1596. if (ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
  1597. return !TBIsRectClipped(ptb, prcBtn);
  1598. else
  1599. return TRUE;
  1600. }
  1601. return FALSE;
  1602. }
  1603. // goin horizontal . . .
  1604. void DrawToolbarH(PTBSTATE ptb, HDC hdc, LPRECT prc)
  1605. {
  1606. int iButton, xButton, yButton, cxBar;
  1607. LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  1608. cxBar = prc->right - prc->left;
  1609. yButton = ptb->iYPos;
  1610. prc->top = ptb->iYPos;
  1611. prc->bottom = ptb->iYPos + ptb->iButHeight; // BUGBUG (scotth): what if first btn is a separator?
  1612. for (iButton = 0, xButton = ptb->xFirstButton;
  1613. iButton < ptb->iNumButtons; iButton++)
  1614. {
  1615. LPTBBUTTONDATA pButton = &pAllButtons[iButton];
  1616. if (!(pButton->fsState & TBSTATE_HIDDEN))
  1617. {
  1618. int cxButton = TBWidthOfButton(ptb, pButton, hdc);
  1619. // Is there anything to draw?
  1620. if (!(pButton->fsStyle & BTNS_SEP) || (ptb->ci.style & TBSTYLE_FLAT))
  1621. {
  1622. // Yes
  1623. prc->left = xButton;
  1624. prc->right = xButton + cxButton;
  1625. if (TBShouldDrawButton(ptb, prc, hdc))
  1626. {
  1627. // Draw separator?
  1628. if (pButton->fsStyle & BTNS_SEP)
  1629. {
  1630. // Yes; must be a flat separator. Is this toolbar vertical?
  1631. if (ptb->ci.style & CCS_VERT)
  1632. {
  1633. // Yes; draw a horizontal separator. Center w/in the
  1634. // button rect
  1635. int iSave = prc->top;
  1636. prc->top += (TBGetSepHeight(ptb, pButton) - 1) / 2;
  1637. InflateRect(prc, -g_cxEdge, 0);
  1638. CCDrawEdge(hdc, prc, EDGE_ETCHED, BF_TOP, &(ptb->clrsc));
  1639. InflateRect(prc, g_cxEdge, 0);
  1640. prc->top = iSave;
  1641. }
  1642. else
  1643. {
  1644. // No; draw a vertical separator
  1645. prc->left += (cxButton - 1) / 2;
  1646. InflateRect(prc, 0, -g_cyEdge);
  1647. CCDrawEdge(hdc, prc, EDGE_ETCHED, BF_LEFT, &(ptb->clrsc));
  1648. InflateRect(prc, 0, g_cyEdge);
  1649. }
  1650. }
  1651. else
  1652. {
  1653. // No
  1654. DrawButton(hdc, xButton, yButton, ptb, pButton, ptb->fActive);
  1655. }
  1656. }
  1657. }
  1658. xButton += (cxButton - s_dxOverlap);
  1659. if (pButton->fsState & TBSTATE_WRAP)
  1660. {
  1661. int dy;
  1662. if (pButton->fsStyle & BTNS_SEP)
  1663. {
  1664. if (ptb->ci.style & CCS_VERT)
  1665. dy = TBGetSepHeight(ptb, pButton);
  1666. else
  1667. {
  1668. if (ptb->ci.style & TBSTYLE_FLAT)
  1669. {
  1670. // Draw a separator across the entire toolbar to separate rows.
  1671. // For horizontal toolbars only.
  1672. RECT rcMid;
  1673. rcMid.top = prc->top + ptb->iButHeight + ((TBGetSepHeight(ptb, pButton) - 1) / 2);
  1674. rcMid.bottom = rcMid.top + g_cxEdge;
  1675. rcMid.left = g_cxEdge;
  1676. rcMid.right = cxBar - g_cxEdge;
  1677. CCDrawEdge(hdc, &rcMid, EDGE_ETCHED, BF_TOP, &(ptb->clrsc));
  1678. }
  1679. dy = ptb->iButHeight + TBGetSepHeight(ptb, pButton);
  1680. }
  1681. }
  1682. else
  1683. dy = ptb->iButHeight;
  1684. xButton = ptb->xFirstButton;
  1685. yButton += dy;
  1686. prc->top += dy;
  1687. prc->bottom += dy;
  1688. }
  1689. }
  1690. }
  1691. }
  1692. // goin vertical . . .
  1693. void DrawToolbarV(PTBSTATE ptb, HDC hdc, LPRECT prc)
  1694. {
  1695. int iButton, xButton, yButton, cyBar;
  1696. LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  1697. NMTBCUSTOMDRAW tbcd = { 0 };
  1698. LPTBBUTTONDATA pButton = pAllButtons;
  1699. cyBar = prc->bottom - prc->top;
  1700. xButton = ptb->xFirstButton;
  1701. prc->left = xButton;
  1702. prc->right = prc->left + ptb->iButWidth;
  1703. for (iButton = 0, yButton = 0;
  1704. iButton < ptb->iNumButtons; iButton++, pButton++)
  1705. {
  1706. if (!(pButton->fsState & TBSTATE_HIDDEN))
  1707. {
  1708. // Is there anything to draw?
  1709. if (!(pButton->fsStyle & BTNS_SEP) || (ptb->ci.style & TBSTYLE_FLAT))
  1710. {
  1711. int cyButton;
  1712. if (pButton->fsStyle & BTNS_SEP)
  1713. cyButton = TBGetSepHeight(ptb, pButton);
  1714. else
  1715. cyButton = ptb->iButHeight;
  1716. prc->top = yButton;
  1717. prc->bottom = yButton + cyButton;
  1718. if (TBShouldDrawButton(ptb, prc, hdc))
  1719. {
  1720. // Draw separator?
  1721. if (pButton->fsStyle & BTNS_SEP)
  1722. {
  1723. DWORD dwCustRet;
  1724. NMTBCUSTOMDRAW tbcd = { 0 };
  1725. tbcd.nmcd.hdc = hdc;
  1726. tbcd.nmcd.dwItemSpec = -1;
  1727. CopyRect(&tbcd.nmcd.rc, prc);
  1728. dwCustRet = CICustomDrawNotify(&ptb->ci, CDDS_ITEMPREPAINT, (NMCUSTOMDRAW *)&tbcd);
  1729. if ( !(CDRF_SKIPDEFAULT & dwCustRet) )
  1730. {
  1731. // Yes; must be a flat separator.
  1732. InflateRect(prc, -g_cxEdge, 0);
  1733. CCDrawEdge(hdc, prc, EDGE_ETCHED, BF_TOP, &(ptb->clrsc));
  1734. InflateRect(prc, g_cxEdge, 0);
  1735. }
  1736. }
  1737. else
  1738. {
  1739. // No
  1740. DrawButton(hdc, xButton, yButton, ptb, pButton, ptb->fActive);
  1741. }
  1742. }
  1743. yButton += cyButton;
  1744. }
  1745. if (pButton->fsState & TBSTATE_WRAP)
  1746. {
  1747. int dx;
  1748. if (ptb->ci.style & TBSTYLE_FLAT)
  1749. {
  1750. // Draw a separator vertival across the entire toolbar to separate cols.
  1751. // For vertical toolbars only.
  1752. RECT rcMid;
  1753. rcMid.top = ptb->rc.top + g_cxEdge;
  1754. rcMid.bottom = ptb->rc.bottom - g_cxEdge;
  1755. rcMid.left = xButton + ptb->iButWidth;
  1756. rcMid.right = rcMid.left + g_cxEdge;
  1757. CCDrawEdge(hdc, &rcMid, EDGE_ETCHED, BF_LEFT, &(ptb->clrsc));
  1758. }
  1759. dx = ptb->iButWidth + g_cxEdge;
  1760. yButton = 0;
  1761. xButton += dx;
  1762. prc->left += dx;
  1763. prc->right += dx;
  1764. }
  1765. }
  1766. }
  1767. }
  1768. COLORREF TB_GetInsertMarkColor(PTBSTATE ptb)
  1769. {
  1770. if (ptb->clrim == CLR_DEFAULT)
  1771. return g_clrBtnText;
  1772. else
  1773. return ptb->clrim;
  1774. }
  1775. void TBPaint(PTBSTATE ptb, HDC hdcIn)
  1776. {
  1777. RECT rc;
  1778. HDC hdc;
  1779. PAINTSTRUCT ps;
  1780. NMTBCUSTOMDRAW tbcd = { 0 };
  1781. GetClientRect(ptb->ci.hwnd, &rc);
  1782. if (hdcIn)
  1783. {
  1784. hdc = hdcIn;
  1785. }
  1786. else
  1787. hdc = BeginPaint(ptb->ci.hwnd, &ps);
  1788. if (!rc.right)
  1789. goto Error1;
  1790. tbcd.nmcd.hdc = hdc;
  1791. tbcd.nmcd.rc = rc;
  1792. ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREPAINT, (NMCUSTOMDRAW *)&tbcd);
  1793. if (!(ptb->ci.dwCustom & CDRF_SKIPDEFAULT))
  1794. {
  1795. if (!ptb->fHimlValid)
  1796. TBBuildImageList(ptb);
  1797. if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1798. DrawToolbarV(ptb, hdc, &rc);
  1799. else
  1800. DrawToolbarH(ptb, hdc, &rc);
  1801. if (ptb->iInsert!=-1)
  1802. {
  1803. BOOL fHorizMode = !(ptb->ci.style & CCS_VERT);
  1804. RECT rc;
  1805. if (GetInsertMarkRect(ptb, &rc, fHorizMode))
  1806. {
  1807. DrawInsertMark(hdc, &rc, fHorizMode, TB_GetInsertMarkColor(ptb));
  1808. }
  1809. }
  1810. ReleaseMonoDC(ptb);
  1811. }
  1812. if (ptb->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  1813. {
  1814. tbcd.nmcd.hdc = hdc;
  1815. tbcd.nmcd.uItemState = 0;
  1816. tbcd.nmcd.lItemlParam = 0;
  1817. CICustomDrawNotify(&ptb->ci, CDDS_POSTPAINT, (NMCUSTOMDRAW *)&tbcd);
  1818. }
  1819. Error1:
  1820. if (hdcIn == NULL)
  1821. EndPaint(ptb->ci.hwnd, &ps);
  1822. }
  1823. void TB_GetItemDropDownRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
  1824. {
  1825. TB_GetItemRect(ptb,uButton,lpRect);
  1826. lpRect->left = lpRect->right - ptb->dxDDArrowChar;
  1827. }
  1828. int TBHeightOfButton(PTBSTATE ptb, LPTBBUTTONDATA ptbb)
  1829. {
  1830. int dy;
  1831. if ((ptbb->fsStyle & BTNS_SEP) &&
  1832. (ptbb->fsState & TBSTATE_WRAP || ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  1833. {
  1834. if (!(ptb->ci.style & CCS_VERT) && !(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  1835. {
  1836. dy = TBGetSepHeight(ptb, ptbb) + ptb->iButHeight;
  1837. }
  1838. else
  1839. {
  1840. dy = TBGetSepHeight(ptb, ptbb);
  1841. }
  1842. }
  1843. else
  1844. {
  1845. dy = ptb->iButHeight;
  1846. }
  1847. return dy;
  1848. }
  1849. void TB_CalcItemRects(PTBSTATE ptb)
  1850. {
  1851. int iButton, xPos, yPos;
  1852. ASSERT(!ptb->fItemRectsValid);
  1853. xPos = ptb->xFirstButton;
  1854. yPos = ptb->iYPos;
  1855. for (iButton = 0; iButton < ptb->iNumButtons; iButton++)
  1856. {
  1857. int xPosButton;
  1858. LPTBBUTTONDATA pButton = &ptb->Buttons[iButton];
  1859. if (!(pButton->fsState & TBSTATE_HIDDEN))
  1860. {
  1861. if ((pButton->fsState & TBSTATE_WRAP) && (pButton->fsStyle & BTNS_SEP))
  1862. xPosButton = ptb->xFirstButton;
  1863. else
  1864. xPosButton = xPos;
  1865. pButton->pt.x = xPosButton;
  1866. pButton->pt.y = yPos;
  1867. if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  1868. {
  1869. if (pButton->fsState & TBSTATE_WRAP)
  1870. {
  1871. xPos += (ptb->iButWidth + g_cxEdge); // to not overwrite the edge.
  1872. yPos = 0;
  1873. }
  1874. else if (pButton->fsStyle & BTNS_SEP)
  1875. yPos += (TBGetSepHeight(ptb, pButton));
  1876. else
  1877. yPos += ptb->iButHeight;
  1878. }
  1879. else // standard horizontal toolbar.
  1880. {
  1881. xPos += TBWidthOfButton(ptb, pButton, NULL) - s_dxOverlap;
  1882. if (pButton->fsState & TBSTATE_WRAP)
  1883. {
  1884. yPos += ptb->iButHeight;
  1885. if (pButton->fsStyle & BTNS_SEP)
  1886. {
  1887. if (ptb->ci.style & CCS_VERT) {
  1888. yPos -= ptb->iButHeight;
  1889. }
  1890. yPos += (TBGetSepHeight(ptb, pButton));
  1891. }
  1892. xPos = ptb->xFirstButton;
  1893. }
  1894. }
  1895. }
  1896. }
  1897. }
  1898. BOOL TB_GetItemRect(PTBSTATE ptb, UINT uButton, LPRECT lpRect)
  1899. {
  1900. int dy = ptb->iButHeight;
  1901. if (uButton >= (UINT)ptb->iNumButtons
  1902. || (ptb->Buttons[uButton].fsState & TBSTATE_HIDDEN))
  1903. {
  1904. return FALSE;
  1905. }
  1906. if (!ptb->fItemRectsValid) {
  1907. TB_CalcItemRects(ptb);
  1908. ptb->fItemRectsValid = TRUE;
  1909. }
  1910. lpRect->left = ptb->Buttons[uButton].pt.x;
  1911. lpRect->right = lpRect->left + TBWidthOfButton(ptb, &ptb->Buttons[uButton], NULL);
  1912. lpRect->top = ptb->Buttons[uButton].pt.y;
  1913. lpRect->bottom = lpRect->top + TBHeightOfButton(ptb, &ptb->Buttons[uButton]);
  1914. return TRUE;
  1915. }
  1916. void InvalidateButton(PTBSTATE ptb, LPTBBUTTONDATA pButtonToPaint, BOOL fErase)
  1917. {
  1918. RECT rc;
  1919. if (TB_GetItemRect(ptb, (UINT) (pButtonToPaint - ptb->Buttons), &rc))
  1920. {
  1921. InvalidateRect(ptb->ci.hwnd, &rc, fErase);
  1922. }
  1923. }
  1924. /*----------------------------------------------------------
  1925. Purpose: Toggles the button as a dropdown
  1926. Returns: TRUE if handled
  1927. */
  1928. BOOL TBToggleDropDown(PTBSTATE ptb, int iPos, BOOL fEatMsg)
  1929. {
  1930. BOOL bRet = FALSE;
  1931. LPTBBUTTONDATA ptbButton = &ptb->Buttons[iPos];
  1932. ASSERT(TB_IsDropDown(ptbButton));
  1933. if (ptbButton->fsState & TBSTATE_ENABLED)
  1934. {
  1935. UINT nVal;
  1936. ptb->iPressedDD = iPos;
  1937. if (TB_HasUnsplitDDArrow(ptb, ptbButton))
  1938. ptbButton->fsState |= TBSTATE_PRESSED;
  1939. InvalidateButton(ptb, ptbButton, TRUE);
  1940. UpdateWindow(ptb->ci.hwnd);
  1941. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT, iPos+1);
  1942. nVal = (UINT) SendItemNotify(ptb, ptbButton->idCommand, TBN_DROPDOWN);
  1943. if (TBDDRET_DEFAULT == nVal || TBDDRET_TREATPRESSED == nVal)
  1944. {
  1945. if (fEatMsg)
  1946. {
  1947. MSG msg;
  1948. PeekMessage(&msg, ptb->ci.hwnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
  1949. }
  1950. ptb->iPressedDD = -1;
  1951. if (TB_HasUnsplitDDArrow(ptb, ptbButton))
  1952. ptbButton->fsState &= ~TBSTATE_PRESSED;
  1953. InvalidateButton(ptb, ptbButton, TRUE);
  1954. UpdateWindow(ptb->ci.hwnd);
  1955. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT, iPos+1);
  1956. }
  1957. bRet = (TBDDRET_DEFAULT == nVal);
  1958. }
  1959. return bRet;
  1960. }
  1961. void TBInvalidateButton(PTBSTATE ptb, int i, BOOL fErase)
  1962. {
  1963. if (i != -1) {
  1964. InvalidateButton(ptb, &ptb->Buttons[i], fErase);
  1965. }
  1966. }
  1967. void TBSetHotItem(PTBSTATE ptb, int iPos, DWORD dwReason)
  1968. {
  1969. HWND hwnd;
  1970. if ((ptb->ci.style & TBSTYLE_FLAT) ) {
  1971. // Either one of these values can be -1, but refrain
  1972. // from processing if both are negative b/c it is wasteful
  1973. // and very common
  1974. if ((ptb->iHot != iPos || (dwReason & HICF_RESELECT)) &&
  1975. (0 <= ptb->iHot || 0 <= iPos) &&
  1976. iPos < ptb->iNumButtons)
  1977. {
  1978. NMTBHOTITEM nmhot = {0};
  1979. // Has the mouse moved away from the toolbar but
  1980. // do we still anchor the highlight?
  1981. if (0 > iPos && ptb->fAnchorHighlight && (dwReason & HICF_MOUSE))
  1982. return ; // Yes; deny the hot item change
  1983. // Send a notification about the hot item change
  1984. if (0 > ptb->iHot)
  1985. {
  1986. if (iPos >= 0)
  1987. nmhot.idNew = ptb->Buttons[iPos].idCommand;
  1988. nmhot.dwFlags = HICF_ENTERING;
  1989. }
  1990. else if (0 > iPos)
  1991. {
  1992. if (ptb->iHot >= 0 && ptb->iHot < ptb->iNumButtons)
  1993. nmhot.idOld = ptb->Buttons[ptb->iHot].idCommand;
  1994. nmhot.dwFlags = HICF_LEAVING;
  1995. }
  1996. else
  1997. {
  1998. if (ptb->iHot < ptb->iNumButtons)
  1999. nmhot.idOld = ptb->Buttons[ptb->iHot].idCommand;
  2000. nmhot.idNew = ptb->Buttons[iPos].idCommand;
  2001. }
  2002. nmhot.dwFlags |= dwReason;
  2003. // must save this for revalidation
  2004. hwnd = ptb->ci.hwnd;
  2005. if (CCSendNotify(&ptb->ci, TBN_HOTITEMCHANGE, &nmhot.hdr))
  2006. return; // deny the hot item change
  2007. // Revalidate the window
  2008. if (!IsWindow(hwnd)) return;
  2009. TBInvalidateButton(ptb, ptb->iHot, TRUE);
  2010. if ((iPos < 0) || !(ptb->Buttons[iPos].fsState & TBSTATE_ENABLED))
  2011. iPos = -1;
  2012. ptb->iHot = iPos;
  2013. if (GetFocus() == ptb->ci.hwnd)
  2014. MyNotifyWinEvent(EVENT_OBJECT_FOCUS, ptb->ci.hwnd, OBJID_CLIENT, iPos + 1);
  2015. TBInvalidateButton(ptb, ptb->iHot, TRUE);
  2016. if ((iPos >= 0 && iPos < ptb->iNumButtons) &&
  2017. (TB_IsDropDown(&ptb->Buttons[iPos])) &&
  2018. (dwReason & HICF_TOGGLEDROPDOWN))
  2019. {
  2020. TBToggleDropDown(ptb, iPos, FALSE);
  2021. }
  2022. }
  2023. }
  2024. }
  2025. BOOL GetInsertMarkRect(PTBSTATE ptb, LPRECT prc, BOOL fHorizMode)
  2026. {
  2027. BOOL fRet = TB_GetItemRect(ptb, ptb->iInsert, prc);
  2028. if (fRet)
  2029. {
  2030. // if we are in horizontal mode, we need a vertical insertion marker
  2031. if ( fHorizMode )
  2032. {
  2033. if (ptb->fInsertAfter)
  2034. prc->left = prc->right;
  2035. else
  2036. prc->right = prc->left;
  2037. prc->left -= INSERTMARKSIZE/2;
  2038. prc->right += INSERTMARKSIZE/2 + 1;
  2039. }
  2040. else
  2041. {
  2042. if (ptb->fInsertAfter)
  2043. prc->top = prc->bottom;
  2044. else
  2045. prc->bottom = prc->top;
  2046. prc->top -= INSERTMARKSIZE/2;
  2047. prc->bottom += INSERTMARKSIZE/2 + 1;
  2048. }
  2049. }
  2050. return fRet;
  2051. }
  2052. void TBInvalidateMark(PTBSTATE ptb)
  2053. {
  2054. RECT rc;
  2055. if (GetInsertMarkRect(ptb, &rc, !(ptb->ci.style & CCS_VERT)))
  2056. {
  2057. InvalidateRect(ptb->ci.hwnd, &rc, TRUE);
  2058. }
  2059. }
  2060. void TBSetInsertMark(PTBSTATE ptb, LPTBINSERTMARK ptbim)
  2061. {
  2062. if (ptbim->iButton != ptb->iInsert ||
  2063. BOOLIFY(ptb->fInsertAfter) != BOOLIFY(ptbim->dwFlags & TBIMHT_AFTER))
  2064. {
  2065. if (ptb->iInsert != -1)
  2066. TBInvalidateMark(ptb);
  2067. ptb->iInsert = ptbim->iButton;
  2068. ptb->fInsertAfter = BOOLIFY(ptbim->dwFlags & TBIMHT_AFTER);
  2069. if (ptb->iInsert != -1)
  2070. TBInvalidateMark(ptb);
  2071. }
  2072. }
  2073. void TBCycleHotItem(PTBSTATE ptb, int iStart, int iDirection, UINT nReason)
  2074. {
  2075. int i;
  2076. int iPrev;
  2077. NMTBWRAPHOTITEM nmwh;
  2078. nmwh.iDir = iDirection;
  2079. nmwh.nReason = nReason;
  2080. //When cycling around the menu, without this check, the second to last menu
  2081. //item would be selected.
  2082. if (iStart == -1 && iDirection == -1)
  2083. iStart = 0;
  2084. for (i = 0; i < ptb->iNumButtons; i++)
  2085. {
  2086. iPrev = iStart;
  2087. iStart += iDirection + ptb->iNumButtons;
  2088. iStart %= ptb->iNumButtons;
  2089. if ( ( iPrev + iDirection >= ptb->iNumButtons) || (iPrev + iDirection < 0) )
  2090. {
  2091. nmwh.iStart = iStart;
  2092. if (CCSendNotify(&ptb->ci, TBN_WRAPHOTITEM, &nmwh.hdr))
  2093. return;
  2094. }
  2095. if (ptb->Buttons[iStart].fsState & TBSTATE_ENABLED &&
  2096. !(ptb->Buttons[iStart].fsState & TBSTATE_HIDDEN) &&
  2097. !(ptb->Buttons[iStart].fsStyle & BTNS_SEP))
  2098. {
  2099. // if the old hot item was dropped down, undrop it.
  2100. if (ptb->iHot != -1 && ptb->iHot == ptb->iPressedDD)
  2101. TBToggleDropDown(ptb, ptb->iHot, FALSE);
  2102. TBSetHotItem(ptb, iStart, nReason);
  2103. break;
  2104. }
  2105. }
  2106. }
  2107. // Do hit testing by sliding the origin of the supplied point
  2108. //
  2109. // returns:
  2110. // >= 0 index of non separator item hit
  2111. // < 0 index of separator or nearest non separator item (area
  2112. // just below and to the left)
  2113. //
  2114. // +--------------------------------------
  2115. // | -1 -1 -1 -1
  2116. // | btn sep btn
  2117. // | +-----+ +-----+
  2118. // | | | | |
  2119. // | -1 | 0 | -1 | 2 | -3
  2120. // | | | | |
  2121. // | +-----+ +-----+
  2122. // |
  2123. // | -1 -1 -1 -2 -3
  2124. //
  2125. int TBHitTest(PTBSTATE ptb, int xPos, int yPos)
  2126. {
  2127. int prev = 0;
  2128. int last = 0;
  2129. int i;
  2130. RECT rc;
  2131. if (ptb->iNumButtons == 0)
  2132. return(-1);
  2133. for (i=0; i<ptb->iNumButtons; i++)
  2134. {
  2135. if (TB_GetItemRect(ptb, i, &rc))
  2136. {
  2137. // ignore this button if hidden because of HideClippedButtons style
  2138. if (!(ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS) || !(TBIsRectClipped(ptb, &rc)))
  2139. {
  2140. // From PtInRect docs:
  2141. // A point is within a rectangle if it lies on the left or top
  2142. // side or is within all four sides. A point on the right or
  2143. // bottom side is considered outside the rectangle.
  2144. if (yPos >= rc.top && yPos < rc.bottom)
  2145. {
  2146. if (xPos >= rc.left && xPos < rc.right)
  2147. {
  2148. if (ptb->Buttons[i].fsStyle & BTNS_SEP)
  2149. return - i - 1;
  2150. else
  2151. return i;
  2152. }
  2153. else
  2154. {
  2155. prev = i + 1;
  2156. }
  2157. }
  2158. else
  2159. {
  2160. last = i;
  2161. }
  2162. }
  2163. }
  2164. }
  2165. if (prev)
  2166. return -1 - prev;
  2167. else if (yPos > rc.bottom)
  2168. // this means that we are off the bottom of the toolbar
  2169. return(- i - 1);
  2170. return last + 1;
  2171. }
  2172. // Same as above except:
  2173. // - returns TRUE if the cursor is on the button edge.
  2174. // - returns FALSE is the cursor is b/t buttons or on the button itself
  2175. BOOL TBInsertMarkHitTest(PTBSTATE ptb, int xPos, int yPos, LPTBINSERTMARK ptbim)
  2176. {
  2177. TBINSERTMARK prev = {-1, TBIMHT_AFTER|TBIMHT_BACKGROUND}; // best guess if we hit a row
  2178. TBINSERTMARK last = {-1, TBIMHT_AFTER|TBIMHT_BACKGROUND}; // best guess if we don't
  2179. int i;
  2180. // restrict hit testing depending upon whether we are vertical or horizontal
  2181. BOOL fHorizMode = !(ptb->ci.style & CCS_VERT);
  2182. for (i=0; i<ptb->iNumButtons; i++)
  2183. {
  2184. RECT rc;
  2185. if (TB_GetItemRect(ptb, i, &rc))
  2186. {
  2187. if (yPos >= rc.top && yPos < rc.bottom)
  2188. {
  2189. if (xPos >= rc.left && xPos < rc.right)
  2190. {
  2191. ptbim->iButton = i;
  2192. if ( fHorizMode )
  2193. {
  2194. if (xPos < rc.left + g_cxEdge*4)
  2195. {
  2196. ptbim->dwFlags = 0;
  2197. return TRUE;
  2198. }
  2199. else if (xPos > rc.right - g_cxEdge*4)
  2200. {
  2201. ptbim->dwFlags = TBIMHT_AFTER;
  2202. return TRUE;
  2203. }
  2204. }
  2205. else
  2206. {
  2207. // vertical....
  2208. if (yPos < rc.top + g_cyEdge*4)
  2209. {
  2210. ptbim->dwFlags = 0;
  2211. return TRUE;
  2212. }
  2213. else if (yPos > rc.bottom - g_cyEdge*4)
  2214. {
  2215. ptbim->dwFlags = TBIMHT_AFTER;
  2216. return TRUE;
  2217. }
  2218. }
  2219. // else we are just on a button...
  2220. ptbim->dwFlags = 0;
  2221. return FALSE;
  2222. }
  2223. else
  2224. {
  2225. if (xPos < rc.left)
  2226. {
  2227. // since buttons are laid out left to right
  2228. // and rows are laid out top to bottom,
  2229. // if we ever hit this case, we can't hit anything else
  2230. ptbim->iButton = i;
  2231. ptbim->dwFlags = TBIMHT_BACKGROUND;
  2232. return FALSE;
  2233. }
  2234. else // (xPos > rc.right)
  2235. {
  2236. // remember the last one we've seen on this row
  2237. prev.iButton = i;
  2238. }
  2239. }
  2240. }
  2241. else
  2242. {
  2243. if (yPos < rc.top)
  2244. {
  2245. if (prev.iButton != -1)
  2246. {
  2247. *ptbim = prev;
  2248. }
  2249. else
  2250. {
  2251. ptbim->iButton = i;
  2252. ptbim->dwFlags = TBIMHT_BACKGROUND;
  2253. }
  2254. }
  2255. else
  2256. {
  2257. // remember the last one we've seen
  2258. last.iButton = i;
  2259. }
  2260. }
  2261. }
  2262. }
  2263. if (prev.iButton != -1)
  2264. *ptbim = prev;
  2265. else
  2266. *ptbim = last;
  2267. return FALSE;
  2268. }
  2269. int CountRows(PTBSTATE ptb)
  2270. {
  2271. LPTBBUTTONDATA pButton, pBtnLast;
  2272. int rows = 1;
  2273. // BUGBUG (scotth): this doesn't look vertical-friendly
  2274. // chrisny: semantically no, technically it will work like a charm :-)
  2275. pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  2276. for (pButton = ptb->Buttons; pButton<pBtnLast; pButton++) {
  2277. if (pButton->fsState & TBSTATE_WRAP) {
  2278. rows++;
  2279. if (pButton->fsStyle & BTNS_SEP)
  2280. rows++;
  2281. }
  2282. }
  2283. return rows;
  2284. }
  2285. #define CountCols(ptb) CountRows(ptb)
  2286. void WrapToolbarCol(PTBSTATE ptb, int dy, LPRECT lpRect, int *pCols)
  2287. {
  2288. LPTBBUTTONDATA pButton, pBtnLast, pBtnPrev;
  2289. LPTBBUTTONDATA pbtnLastVisible = NULL;
  2290. LPTBBUTTONDATA pbtnPrev = NULL;
  2291. int xPos, yPos;
  2292. int dyButton;
  2293. int yPosWrap = 0;
  2294. int cCols = 1;
  2295. DEBUG_CODE( int cItemsPerCol = 0; )
  2296. ASSERT(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL);
  2297. TraceMsg(TF_TOOLBAR, "Toolbar: calculating WrapToolbar");
  2298. // dy must be at least the button height, otherwise the final
  2299. // rect is mis-calculated and will be too big.
  2300. if (dy < ptb->iButHeight)
  2301. dy = ptb->iButHeight;
  2302. dyButton = ptb->iButHeight;
  2303. xPos = ptb->xFirstButton;
  2304. yPos = ptb->iYPos;
  2305. pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  2306. ptb->szCached.cx = -1;
  2307. ptb->szCached.cy = -1;
  2308. if (pCols)
  2309. (*pCols) = 1;
  2310. pBtnPrev = ptb->Buttons;
  2311. for (pButton = ptb->Buttons; pButton < pBtnLast; pButton++)
  2312. {
  2313. DEBUG_CODE( cItemsPerCol++; )
  2314. // we nuke the wrap state at the start of the loop.
  2315. // so we don't know if/when we are adding on a wrap bit that wasn't there
  2316. // before. we overstep the button, then back up when we've gone too far,
  2317. pButton->fsState &= ~TBSTATE_WRAP;
  2318. if (!(pButton->fsState & TBSTATE_HIDDEN))
  2319. {
  2320. if (pButton->fsStyle & BTNS_SEP)
  2321. yPos += (TBGetSepHeight(ptb, pButton));
  2322. else
  2323. yPos += dyButton;
  2324. // Is this button out of bounds?
  2325. if (yPos > dy)
  2326. {
  2327. // Yes; wrap it.
  2328. if ((pButton->fsStyle & BTNS_SEP) &&
  2329. yPos - TBGetSepHeight(ptb, pButton) > yPosWrap)
  2330. {
  2331. yPosWrap = yPos - TBGetSepHeight(ptb, pButton); // wrap at first in next col.
  2332. }
  2333. else if (yPos - dyButton > yPosWrap)
  2334. yPosWrap = yPos - dyButton; // wrap at first in next col.
  2335. if (xPos + ptb->iButWidth <= ptb->sizeBound.cx)
  2336. xPos += ptb->iButWidth;
  2337. yPos = dyButton;
  2338. cCols++;
  2339. pBtnPrev->fsState |= TBSTATE_WRAP;
  2340. DEBUG_CODE( cItemsPerCol = 0; )
  2341. }
  2342. // button in bounds gets handled above.
  2343. pBtnPrev = pButton; // save previous for wrap point
  2344. }
  2345. }
  2346. yPos = yPosWrap ? yPosWrap : yPos;
  2347. if (pCols)
  2348. *pCols = cCols;
  2349. ptb->rc.left = 0;
  2350. ptb->rc.right = xPos + ptb->iButWidth;
  2351. ptb->rc.top = 0;
  2352. ptb->rc.bottom = yPos;
  2353. if (lpRect)
  2354. CopyRect(lpRect, &ptb->rc);
  2355. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  2356. }
  2357. /**** WrapToolbar: * The buttons in the toolbar is layed out from left to right,
  2358. * top to bottom. If adding another button to the current row,
  2359. * while computing the layout, would cause that button to extend
  2360. * beyond the right edge or the client area, then locate a break-
  2361. * point (marked with the TBSTATE_WRAP flag). A break-point is:
  2362. *
  2363. * a) The right-most separator on the current row.
  2364. *
  2365. * b) The right-most button if there is no separator on the current row.
  2366. *
  2367. * A new row is also started at the end of any button group (sequence
  2368. * of buttons that are delimited by separators) that are taller than
  2369. * or equal to two rows.
  2370. */
  2371. void WrapToolbar(PTBSTATE ptb, int dx, LPRECT lpRect, int *pRows)
  2372. {
  2373. BOOL fInvalidate = FALSE;
  2374. LPTBBUTTONDATA pButton, pBtnT, pBtnLast;
  2375. LPTBBUTTONDATA pbtnLastVisible = NULL;
  2376. LPTBBUTTONDATA pbtnPrev = NULL;
  2377. BOOL fLastVisibleWrapped = FALSE;
  2378. int xPos, yPos, xMax;
  2379. int dyButton;
  2380. BOOL bWrapAtNextSeparator = FALSE;
  2381. ASSERT(!(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL));
  2382. TraceMsg(TF_TOOLBAR, "Toolbar: calculating WrapToolbar");
  2383. if (ptb->iNumButtons == 0) {
  2384. // no buttons, so we're not going to go through the loop below; initialize
  2385. // dyButton to 0 so that we fill in lpRect with 0 height. this fixes ideal
  2386. // size calculation for empty toolbars (NT5 #180430)
  2387. dyButton = 0;
  2388. } else {
  2389. if (dx < ptb->iButWidth) {
  2390. // dx must be at least the button width, otherwise the final
  2391. // rect is mis-calculated and will be too big.
  2392. dx = ptb->iButWidth;
  2393. }
  2394. dyButton = ptb->iButHeight;
  2395. }
  2396. xMax = 0;
  2397. xPos = ptb->xFirstButton;
  2398. yPos = ptb->iYPos;
  2399. pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  2400. ptb->szCached.cx = -1;
  2401. ptb->szCached.cy = -1;
  2402. if (pRows)
  2403. (*pRows)=1;
  2404. for (pButton = ptb->Buttons; pButton < pBtnLast; pButton++)
  2405. {
  2406. // we nuke the wrap state at the start of the loop.
  2407. // so we don't know if/when we are adding on a wrap bit that wasn't there
  2408. // before. we overstep the button, then back up when we've gone too far,
  2409. // so we can't simply keep the at the start of the loop
  2410. // we need to keep it over to the next iteration
  2411. BOOL fNextLastVisibleWrapped = (pButton->fsState & TBSTATE_WRAP);
  2412. LPTBBUTTONDATA pbtnSav = pButton;
  2413. pButton->fsState &= ~TBSTATE_WRAP;
  2414. if (!(pButton->fsState & TBSTATE_HIDDEN))
  2415. {
  2416. LPTBBUTTONDATA pbtnNextLastVisible = pButton;
  2417. xPos += TBWidthOfButton(ptb, pButton, NULL) - s_dxOverlap;
  2418. // Is this a normal button and is the button out of bounds?
  2419. if (!(pButton->fsStyle & BTNS_SEP) && (xPos > dx)) {
  2420. // Yes; wrap it. Go back to the first non-hidden separator
  2421. // as a break-point candidate.
  2422. for (pBtnT=pButton;
  2423. pBtnT>ptb->Buttons && !(pBtnT->fsState & TBSTATE_WRAP);
  2424. pBtnT--)
  2425. {
  2426. if ((pBtnT->fsStyle & BTNS_SEP) &&
  2427. !(pBtnT->fsState & TBSTATE_HIDDEN))
  2428. {
  2429. yPos += (TBGetSepHeight(ptb, pBtnT)) + dyButton;
  2430. bWrapAtNextSeparator = FALSE;
  2431. if (pRows)
  2432. (*pRows)++;
  2433. goto SetWrapHere;
  2434. }
  2435. }
  2436. pBtnT = pButton;
  2437. // Are we at the first button?
  2438. if (pButton != ptb->Buttons) {
  2439. // No; back up to first non-hidden button
  2440. do {
  2441. pBtnT--;
  2442. } while ((pBtnT>ptb->Buttons) &&
  2443. (pBtnT->fsState & TBSTATE_HIDDEN));
  2444. // Is it already wrapped?
  2445. if (pBtnT->fsState & TBSTATE_WRAP)
  2446. {
  2447. // Yes; wrap the button we were looking at originally
  2448. pBtnT = pButton;
  2449. }
  2450. }
  2451. // Wrap at the next separator because we've now wrapped in the middle
  2452. // of a group of buttons.
  2453. bWrapAtNextSeparator = TRUE;
  2454. yPos += dyButton;
  2455. SetWrapHere:
  2456. pBtnT->fsState |= TBSTATE_WRAP;
  2457. // find out if this wrap bit is new...
  2458. // it isn't if this button was the last visible button
  2459. // and that last visible button started off wrapped
  2460. if (pBtnT != pbtnLastVisible || !fLastVisibleWrapped)
  2461. fInvalidate = TRUE;
  2462. xPos = ptb->xFirstButton;
  2463. pButton = pBtnT;
  2464. // Count another row.
  2465. if (pRows)
  2466. (*pRows)++;
  2467. }
  2468. else
  2469. {
  2470. // No; this is a separator (in or out of bounds) or a button that is in-bounds.
  2471. if (pButton->fsStyle & BTNS_SEP)
  2472. {
  2473. if (ptb->ci.style & CCS_VERT)
  2474. {
  2475. if (pbtnPrev && !(pbtnPrev->fsState & TBSTATE_WRAP))
  2476. {
  2477. pbtnPrev->fsState |= TBSTATE_WRAP;
  2478. yPos += dyButton;
  2479. }
  2480. xPos = ptb->xFirstButton;
  2481. yPos += TBGetSepHeight(ptb, pButton);
  2482. pButton->fsState |= TBSTATE_WRAP;
  2483. if (pRows)
  2484. (*pRows)++;
  2485. }
  2486. else if (bWrapAtNextSeparator)
  2487. {
  2488. bWrapAtNextSeparator = FALSE;
  2489. pButton->fsState |= TBSTATE_WRAP;
  2490. xPos = ptb->xFirstButton;
  2491. yPos += dyButton + (TBGetSepHeight(ptb, pButton));
  2492. if (pRows)
  2493. (*pRows)+=2;
  2494. }
  2495. }
  2496. // This button is visible and it's one we cached at the top of the loop
  2497. // set it for the next loop
  2498. if (pButton == pbtnNextLastVisible) {
  2499. ASSERT(!(pButton->fsState & TBSTATE_HIDDEN));
  2500. if (!(pButton->fsState & TBSTATE_HIDDEN)) {
  2501. // we don't know that we're not going to re-wrap an item that was initially wrapped
  2502. // until this point
  2503. if (pbtnLastVisible && fLastVisibleWrapped && !(pbtnLastVisible->fsState & TBSTATE_WRAP))
  2504. fInvalidate = TRUE;
  2505. pbtnLastVisible = pButton;
  2506. fLastVisibleWrapped = fNextLastVisibleWrapped;
  2507. }
  2508. }
  2509. }
  2510. if (!(pButton->fsStyle&BTNS_SEP))
  2511. xMax = max(xPos, xMax);
  2512. pbtnPrev = pbtnSav;
  2513. }
  2514. }
  2515. if (lpRect)
  2516. {
  2517. lpRect->left = 0;
  2518. lpRect->right = xMax;
  2519. lpRect->top = 0;
  2520. lpRect->bottom = yPos + ptb->iYPos + dyButton;
  2521. }
  2522. if (fInvalidate)
  2523. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  2524. }
  2525. // only called from TB_SETROWS so no worry's about TBSTYLE_EX_MULTICOLUMN
  2526. BOOL BoxIt(PTBSTATE ptb, int height, BOOL fLarger, LPRECT lpRect)
  2527. {
  2528. int dx, bwidth;
  2529. int rows, prevRows, prevWidth;
  2530. RECT rcCur;
  2531. if (height<1)
  2532. height = 1;
  2533. rows = CountRows(ptb);
  2534. if (height==rows || ptb->iNumButtons==0)
  2535. {
  2536. GetClientRect(ptb->ci.hwnd, lpRect);
  2537. return FALSE;
  2538. }
  2539. bwidth = ptb->iButWidth-s_dxOverlap;
  2540. prevRows = ptb->iNumButtons+1;
  2541. prevWidth = bwidth;
  2542. for (rows=height+1, dx = bwidth; rows>height;dx+=bwidth/4)
  2543. {
  2544. WrapToolbar(ptb, dx, &rcCur, &rows);
  2545. if (rows<prevRows && rows>height)
  2546. {
  2547. prevWidth = dx;
  2548. prevRows = rows;
  2549. }
  2550. }
  2551. if (rows<height && fLarger)
  2552. {
  2553. WrapToolbar(ptb, prevWidth, &rcCur, NULL);
  2554. }
  2555. if (lpRect)
  2556. *lpRect = rcCur;
  2557. return TRUE;
  2558. }
  2559. int PositionFromID(PTBSTATE ptb, LONG_PTR id)
  2560. {
  2561. int i;
  2562. // Handle case where this is sent at the wrong time..
  2563. if (ptb == NULL || id == -1)
  2564. return -1;
  2565. // note, we don't skip separators, so you better not have conflicting
  2566. // cmd ids and separator ids.
  2567. for (i = 0; i < ptb->iNumButtons; i++)
  2568. if (ptb->Buttons[i].idCommand == id)
  2569. return i; // position found
  2570. return -1; // ID not found!
  2571. }
  2572. // check a radio button by button index.
  2573. // the button matching idCommand was just pressed down. this forces
  2574. // up all other buttons in the group.
  2575. // this does not work with buttons that are forced up with
  2576. void MakeGroupConsistant(PTBSTATE ptb, int idCommand)
  2577. {
  2578. int i, iFirst, iLast, iButton;
  2579. int cButtons = ptb->iNumButtons;
  2580. LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  2581. iButton = PositionFromID(ptb, idCommand);
  2582. if (iButton < 0)
  2583. return;
  2584. // assertion
  2585. // if (!(pAllButtons[iButton].fsStyle & BTNS_CHECK))
  2586. // return;
  2587. // did the pressed button just go down?
  2588. if (!(pAllButtons[iButton].fsState & TBSTATE_CHECKED))
  2589. return; // no, can't do anything
  2590. // find the limits of this radio group
  2591. // there was a bug here since win95 days -- ; there was no ; at the end of for loop
  2592. // and if was part of it -- some apps may rely on that (reljai 6/16/98)
  2593. for (iFirst = iButton; (iFirst > 0) && (pAllButtons[iFirst].fsStyle & BTNS_GROUP); iFirst--);
  2594. if (!(pAllButtons[iFirst].fsStyle & BTNS_GROUP))
  2595. iFirst++;
  2596. cButtons--;
  2597. for (iLast = iButton; (iLast < cButtons) && (pAllButtons[iLast].fsStyle & BTNS_GROUP); iLast++);
  2598. if (!(pAllButtons[iLast].fsStyle & BTNS_GROUP))
  2599. iLast--;
  2600. // search for the currently down button and pop it up
  2601. for (i = iFirst; i <= iLast; i++) {
  2602. if (i != iButton) {
  2603. // is this button down?
  2604. if (pAllButtons[i].fsState & TBSTATE_CHECKED) {
  2605. pAllButtons[i].fsState &= ~TBSTATE_CHECKED; // pop it up
  2606. TBInvalidateButton(ptb, i, TRUE);
  2607. break; // only one button is down right?
  2608. }
  2609. }
  2610. }
  2611. }
  2612. void DestroyStrings(PTBSTATE ptb)
  2613. {
  2614. PTSTR *p;
  2615. PTSTR end = 0, start = 0;
  2616. int i;
  2617. p = ptb->pStrings;
  2618. for (i = 0; i < ptb->nStrings; i++) {
  2619. if (!((*p < end) && (*p > start))) {
  2620. start = (*p);
  2621. end = start + (LocalSize((HANDLE)*p) / sizeof(TCHAR));
  2622. LocalFree((HANDLE)*p);
  2623. }
  2624. p++;
  2625. }
  2626. LocalFree((HANDLE)ptb->pStrings);
  2627. }
  2628. // gets the iString from pStrings and copies it to pszText.
  2629. // returns the lstrlen.
  2630. // pszText can be null to just fetch the length.
  2631. int TBGetString(PTBSTATE ptb, int iString, int cchText, LPTSTR pszText)
  2632. {
  2633. int iRet = -1;
  2634. if (iString < ptb->nStrings)
  2635. {
  2636. iRet = lstrlen(ptb->pStrings[iString]);
  2637. if (pszText)
  2638. {
  2639. StringCchCopy(pszText, cchText, ptb->pStrings[iString]);
  2640. }
  2641. }
  2642. return iRet;
  2643. }
  2644. // gets the iString from pStrings and copies it to pszText.
  2645. // returns the lstrlen.
  2646. // pszText can be null to just fetch the length.
  2647. int TBGetStringA(PTBSTATE ptb, int iString, int cchText, LPSTR pszText)
  2648. {
  2649. int iRet = -1;
  2650. if (iString < ptb->nStrings)
  2651. {
  2652. iRet = lstrlenW(ptb->pStrings[iString]);
  2653. if (pszText)
  2654. {
  2655. WideCharToMultiByte (CP_ACP, 0, ptb->pStrings[iString],
  2656. -1, pszText, cchText, NULL, NULL);
  2657. }
  2658. }
  2659. return iRet;
  2660. }
  2661. #define MAXSTRINGSIZE 1024
  2662. int TBAddStrings(PTBSTATE ptb, WPARAM wParam, LPARAM lParam)
  2663. {
  2664. int i = 0,j = 0, cxMax = 0;
  2665. LPTSTR lpsz;
  2666. PTSTR pString, pStringAlloc, psz;
  2667. int numstr;
  2668. PTSTR *pFoo;
  2669. PTSTR *pOffset;
  2670. TCHAR cSeparator;
  2671. int len;
  2672. // read the string as a resource
  2673. if (wParam != 0) {
  2674. pString = (PTSTR)LocalAlloc(LPTR, (MAXSTRINGSIZE * sizeof (TCHAR)));
  2675. if (!pString)
  2676. return -1;
  2677. i = LoadString((HINSTANCE)wParam, LOWORD(lParam), (LPTSTR)pString, MAXSTRINGSIZE);
  2678. if (!i) {
  2679. LocalFree(pString);
  2680. return -1;
  2681. }
  2682. // realloc string buffer to actual needed size
  2683. psz = LocalReAlloc(pString, (i+1) * sizeof (TCHAR), LMEM_MOVEABLE);
  2684. if (psz)
  2685. pString = psz;
  2686. // convert separators to '\0' and count number of strings
  2687. cSeparator = *pString;
  2688. for (numstr = 0, psz = pString + 1, i--; i; i--, psz++) {
  2689. if (*psz == cSeparator) {
  2690. if (i != 1) // We don't want to count the second terminator as another string
  2691. numstr++;
  2692. *psz = 0; // terminate with 0
  2693. }
  2694. // shift string to the left to overwrite separator identifier
  2695. *(psz - 1) = *psz;
  2696. }
  2697. }
  2698. // read explicit string. copy it into local memory, too.
  2699. else {
  2700. // Common mistake is to forget to check the return value of
  2701. // LoadLibrary and accidentally pass wParam=NULL.
  2702. if (IS_INTRESOURCE(lParam))
  2703. return -1;
  2704. // find total length and number of strings
  2705. for (i = 0, numstr = 0, lpsz = (LPTSTR)lParam;;) {
  2706. i++;
  2707. if (*lpsz == 0) {
  2708. numstr++;
  2709. if (*(lpsz + 1) == 0)
  2710. break;
  2711. }
  2712. lpsz++;
  2713. }
  2714. pString = (PTSTR)LocalAlloc(LPTR, (i * sizeof (TCHAR)));
  2715. if (!pString)
  2716. return -1;
  2717. hmemcpy(pString, (void *)lParam, i * sizeof(TCHAR));
  2718. }
  2719. pStringAlloc = pString; // in case something bad happens
  2720. // make room for increased string pointer table
  2721. pFoo = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
  2722. (ptb->nStrings + numstr) * sizeof(PTSTR));
  2723. if (!pFoo) {
  2724. goto Failure;
  2725. }
  2726. ptb->pStrings = pFoo;
  2727. // pointer to next open slot in string index table.
  2728. pOffset = ptb->pStrings + ptb->nStrings;
  2729. for (i = 0; i < numstr; i++, pOffset++)
  2730. {
  2731. *pOffset = pString;
  2732. len = lstrlen(pString);
  2733. pString += len + 1;
  2734. }
  2735. // is the world big enough to handle the larger buttons?
  2736. i = ptb->nStrings;
  2737. ptb->nStrings += numstr;
  2738. if (!TBRecalc(ptb))
  2739. {
  2740. ptb->nStrings -= numstr;
  2741. // back out changes.
  2742. pFoo = (PTSTR *)CCLocalReAlloc(ptb->pStrings,
  2743. ptb->nStrings * sizeof(PTSTR));
  2744. if (pFoo)
  2745. ptb->pStrings = pFoo;
  2746. // don't get mad if pFoo == NULL; it means the shrink failed, no big deal
  2747. Failure:
  2748. LocalFree(pStringAlloc);
  2749. return -1;
  2750. }
  2751. return i; // index of first added string
  2752. }
  2753. void MapToStandardBitmaps(HINSTANCE *phinst, UINT_PTR *pidBM, int *pnButtons)
  2754. {
  2755. if (*phinst == HINST_COMMCTRL) {
  2756. *phinst = g_hinst;
  2757. // low 2 bits are coded M(mono == ~color) L(large == ~small)
  2758. // 0 0 -> color small
  2759. // 0 1 -> color large
  2760. // ...
  2761. // 1 1 -> mono large
  2762. switch (*pidBM)
  2763. {
  2764. case IDB_STD_SMALL_COLOR:
  2765. case IDB_STD_LARGE_COLOR:
  2766. case IDB_STD_SMALL_MONO:
  2767. case IDB_STD_LARGE_MONO:
  2768. *pidBM = IDB_STDTB_SMALL_COLOR + (*pidBM & 1);
  2769. *pnButtons = STD_PRINT + 1;
  2770. break;
  2771. case IDB_HIST_SMALL_COLOR:
  2772. case IDB_HIST_LARGE_COLOR:
  2773. //case IDB_HIST_SMALL_MONO:
  2774. //case IDB_HIST_LARGE_MONO:
  2775. *pidBM = IDB_HISTTB_SMALL_COLOR + (*pidBM & 1);
  2776. *pnButtons = HIST_LAST + 1;
  2777. break;
  2778. case IDB_VIEW_SMALL_COLOR:
  2779. case IDB_VIEW_LARGE_COLOR:
  2780. case IDB_VIEW_SMALL_MONO:
  2781. case IDB_VIEW_LARGE_MONO:
  2782. *pidBM = IDB_VIEWTB_SMALL_COLOR + (*pidBM & 1);
  2783. *pnButtons = VIEW_NEWFOLDER + 1;
  2784. break;
  2785. }
  2786. }
  2787. }
  2788. //
  2789. // the PBITMAP points to the BITMAP structure that was GetObject'd from
  2790. // the hbm, except that pbm->bmWidth and pbm->bmHeight have been adjusted
  2791. // to represent the *desired* height and width, not the actual height
  2792. // and width.
  2793. //
  2794. HBITMAP _CopyBitmap(PTBSTATE ptb, HBITMAP hbm, PBITMAP pbm)
  2795. {
  2796. HBITMAP hbmCopy = NULL;
  2797. HDC hdcWin;
  2798. HDC hdcSrc, hdcDest;
  2799. // Old code called CreateColorBitmap, which is bad on multimon systems
  2800. // because it will create a bitmap that ImageList_AddMasked can't handle,
  2801. // resulting in disabled toolbar buttons looking bad.
  2802. // so we have to create the bitmap copy in the same format as the source
  2803. hdcWin = GetDC(ptb->ci.hwnd);
  2804. hdcSrc = CreateCompatibleDC(hdcWin);
  2805. hdcDest = CreateCompatibleDC(hdcWin);
  2806. if (hdcWin && hdcSrc && hdcDest) {
  2807. SelectObject(hdcSrc, hbm);
  2808. if (pbm->bmBits) {
  2809. // Source was a DIB section. Create a DIB section in the same
  2810. // color format with the same palette.
  2811. //
  2812. // Man, creating a DIB section is so annoying.
  2813. struct
  2814. {
  2815. // Our private version of BITMAPINFO
  2816. BITMAPINFOHEADER bmiHeader;
  2817. RGBQUAD bmiColors[256];
  2818. } bmi;
  2819. UINT cBitsPixel;
  2820. LPVOID pvDummy;
  2821. ZeroMemory(&bmi.bmiHeader, SIZEOF(bmi.bmiHeader));
  2822. bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
  2823. bmi.bmiHeader.biWidth = pbm->bmWidth;
  2824. bmi.bmiHeader.biHeight = pbm->bmHeight;
  2825. bmi.bmiHeader.biPlanes = 1;
  2826. // DIB color depths must be exactly 1, 4, 8 or 24.
  2827. cBitsPixel = pbm->bmPlanes * pbm->bmBitsPixel;
  2828. if (cBitsPixel <= 1)
  2829. bmi.bmiHeader.biBitCount = 1;
  2830. else if (cBitsPixel <= 4)
  2831. bmi.bmiHeader.biBitCount = 4;
  2832. else if (cBitsPixel <= 8)
  2833. bmi.bmiHeader.biBitCount = 8;
  2834. else
  2835. goto CreateDDB; // ImageList_AddMasked doesn't like DIBs deeper than 8bpp
  2836. // And get the color table too
  2837. ASSERT(bmi.bmiHeader.biBitCount <= 8);
  2838. bmi.bmiHeader.biClrUsed = GetDIBColorTable(hdcSrc, 0, 1 << bmi.bmiHeader.biBitCount, bmi.bmiColors);
  2839. ASSERT(bmi.bmiHeader.biCompression == BI_RGB);
  2840. ASSERT(bmi.bmiHeader.biSizeImage == 0);
  2841. hbmCopy = CreateDIBSection(hdcWin, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, &pvDummy, NULL, 0);
  2842. } else {
  2843. // Source was a DDB. Create a duplicate DDB.
  2844. CreateDDB:
  2845. // Since the caller may have dorked the bmWidth,
  2846. // we have to recompute the bmWidthBytes, because GDI
  2847. // gets mad if it's not exactly right, even in the bmBits == NULL
  2848. // case.
  2849. pbm->bmBits = NULL;
  2850. pbm->bmWidthBytes = ((pbm->bmBitsPixel * pbm->bmWidth + 15) >> 4) << 1;
  2851. hbmCopy = CreateBitmapIndirect(pbm);
  2852. }
  2853. SelectObject(hdcDest, hbmCopy);
  2854. // fill the background
  2855. PatB(hdcDest, 0, 0, pbm->bmWidth, pbm->bmHeight, g_clrBtnFace);
  2856. BitBlt(hdcDest, 0, 0, pbm->bmWidth, pbm->bmHeight,
  2857. hdcSrc, 0, 0, SRCCOPY);
  2858. }
  2859. if (hdcWin)
  2860. ReleaseDC(ptb->ci.hwnd, hdcWin);
  2861. if (hdcSrc)
  2862. DeleteDC(hdcSrc);
  2863. if (hdcDest)
  2864. DeleteDC(hdcDest);
  2865. return hbmCopy;
  2866. }
  2867. BOOL TBAddBitmapToImageList(PTBSTATE ptb, PTBBMINFO pTemp)
  2868. {
  2869. HBITMAP hbm = NULL, hbmTemp = NULL;
  2870. HIMAGELIST himl = TBGetImageList(ptb, HIML_NORMAL, 0);
  2871. if (!himl) {
  2872. himl = ImageList_Create(ptb->iDxBitmap, ptb->iDyBitmap, ILC_MASK | ILC_COLORDDB, 4, 4);
  2873. if (!himl)
  2874. return(FALSE);
  2875. TBSetImageList(ptb, HIML_NORMAL, 0, himl);
  2876. ImageList_SetBkColor(himl, (ptb->ci.style & TBSTYLE_TRANSPARENT) ? CLR_NONE : g_clrBtnFace);
  2877. }
  2878. if (pTemp->hInst) {
  2879. // can't use LoadImage(..., LR_MAP3DCOLORS) - more than 3 colors
  2880. hbm = hbmTemp = CreateMappedBitmap(pTemp->hInst, pTemp->wID, CMB_DIBSECTION, NULL, 0);
  2881. } else if (pTemp->wID) {
  2882. hbm = (HBITMAP)pTemp->wID;
  2883. }
  2884. if (hbm) {
  2885. //
  2886. // Fix up bitmaps that aren't iDxBitmap x iDyBitmap
  2887. //
  2888. BITMAP bm;
  2889. GetObject( hbm, sizeof(bm), &bm);
  2890. if (bm.bmWidth < ptb->iDxBitmap) {
  2891. bm.bmWidth = ptb->iDxBitmap;
  2892. }
  2893. if (bm.bmHeight < ptb->iDyBitmap) {
  2894. bm.bmHeight = ptb->iDyBitmap;
  2895. }
  2896. // The error cases we are catching are:
  2897. // If the pTemp->nButtons is 0 then we assume there is one button
  2898. // If width of the bitmap is less than what it is supposed to be, we fix it.
  2899. if (!pTemp->nButtons)
  2900. bm.bmWidth = ptb->iDxBitmap;
  2901. else if (pTemp->nButtons > (bm.bmWidth / ptb->iDxBitmap))
  2902. bm.bmWidth = ptb->iDxBitmap * pTemp->nButtons;
  2903. // Must preserve color depth to keep ImageList_AddMasked happy
  2904. // And if we started with a DIB section, then create a DIB section.
  2905. // (Curiously, CopyImage does not preserve DIB-ness.)
  2906. hbm = (HBITMAP)_CopyBitmap(ptb, hbm, &bm);
  2907. }
  2908. // AddMasked parties on the bitmap, so we want to use a local copy
  2909. if (hbm) {
  2910. ImageList_AddMasked(himl, hbm, g_clrBtnFace);
  2911. DeleteObject(hbm);
  2912. }
  2913. if (hbmTemp) {
  2914. DeleteObject(hbmTemp);
  2915. }
  2916. return(TRUE);
  2917. }
  2918. void TBBuildImageList(PTBSTATE ptb)
  2919. {
  2920. int i;
  2921. PTBBMINFO pTemp;
  2922. HIMAGELIST himl;
  2923. ptb->fHimlValid = TRUE;
  2924. // is the parent dealing natively with imagelists? if so,
  2925. // don't do this back compat building
  2926. if (ptb->fHimlNative)
  2927. return;
  2928. himl = TBSetImageList(ptb, HIML_NORMAL, 0, NULL);
  2929. ImageList_Destroy(himl);
  2930. for (i = 0, pTemp = ptb->pBitmaps; i < ptb->nBitmaps; i++, pTemp++) {
  2931. TBAddBitmapToImageList(ptb, pTemp);
  2932. }
  2933. }
  2934. /* Adds a new bitmap to the list of BMs available for this toolbar.
  2935. * Returns the index of the first button in the bitmap or -1 if there
  2936. * was an error.
  2937. */
  2938. int AddBitmap(PTBSTATE ptb, int nButtons, HINSTANCE hBMInst, UINT_PTR idBM)
  2939. {
  2940. PTBBMINFO pTemp;
  2941. int nBM, nIndex;
  2942. // map things to the standard toolbar images
  2943. if (hBMInst == HINST_COMMCTRL) // -1
  2944. {
  2945. // set the proper dimensions...
  2946. if (idBM & 1)
  2947. SetBitmapSize(ptb, LARGE_DXYBITMAP, LARGE_DXYBITMAP);
  2948. else
  2949. SetBitmapSize(ptb, SMALL_DXYBITMAP, SMALL_DXYBITMAP);
  2950. MapToStandardBitmaps(&hBMInst, &idBM, &nButtons);
  2951. }
  2952. if (ptb->pBitmaps)
  2953. {
  2954. /* Check if the bitmap has already been added
  2955. */
  2956. for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps, nIndex=0;
  2957. nBM>0; --nBM, ++pTemp)
  2958. {
  2959. if (pTemp->hInst==hBMInst && pTemp->wID==idBM)
  2960. {
  2961. /* We already have this bitmap, but have we "registered" all
  2962. * the buttons in it?
  2963. */
  2964. if (pTemp->nButtons >= nButtons)
  2965. return(nIndex);
  2966. if (nBM == 1)
  2967. {
  2968. /* If this is the last bitmap, we can easily increase the
  2969. * number of buttons without messing anything up.
  2970. */
  2971. pTemp->nButtons = nButtons;
  2972. return(nIndex);
  2973. }
  2974. }
  2975. nIndex += pTemp->nButtons;
  2976. }
  2977. }
  2978. pTemp = (PTBBMINFO)CCLocalReAlloc(ptb->pBitmaps,
  2979. (ptb->nBitmaps + 1)*sizeof(TBBMINFO));
  2980. if (!pTemp)
  2981. return(-1);
  2982. ptb->pBitmaps = pTemp;
  2983. pTemp = ptb->pBitmaps + ptb->nBitmaps;
  2984. pTemp->hInst = hBMInst;
  2985. pTemp->wID = idBM;
  2986. pTemp->nButtons = nButtons;
  2987. if (!TBAddBitmapToImageList(ptb, pTemp))
  2988. return(-1);
  2989. ++ptb->nBitmaps;
  2990. for (nButtons=0, --pTemp; pTemp>=ptb->pBitmaps; --pTemp)
  2991. nButtons += pTemp->nButtons;
  2992. return(nButtons);
  2993. }
  2994. /* Adds a bitmap to the list of BMs available for this
  2995. * toolbar. Returns the index of the first button in the bitmap or -1 if there
  2996. * was an error.
  2997. */
  2998. int PASCAL TBLoadImages(PTBSTATE ptb, UINT_PTR id, HINSTANCE hinst)
  2999. {
  3000. int iTemp;
  3001. TBBMINFO bmi;
  3002. HIMAGELIST himl;
  3003. MapToStandardBitmaps(&hinst, &id, &iTemp);
  3004. bmi.hInst = hinst;
  3005. bmi.wID = id;
  3006. bmi.nButtons = iTemp;
  3007. himl = TBGetImageList(ptb, HIML_NORMAL, 0);
  3008. if (himl)
  3009. iTemp = ImageList_GetImageCount(himl);
  3010. else
  3011. iTemp = 0;
  3012. if (!TBAddBitmapToImageList(ptb, &bmi))
  3013. return(-1);
  3014. ptb->fHimlNative = TRUE;
  3015. return iTemp;
  3016. }
  3017. BOOL ReplaceBitmap(PTBSTATE ptb, LPTBREPLACEBITMAP lprb)
  3018. {
  3019. int nBM;
  3020. PTBBMINFO pTemp;
  3021. int iTemp;
  3022. MapToStandardBitmaps(&lprb->hInstOld, &lprb->nIDOld, &iTemp);
  3023. MapToStandardBitmaps(&lprb->hInstNew, &lprb->nIDNew, &lprb->nButtons);
  3024. for (nBM=ptb->nBitmaps, pTemp=ptb->pBitmaps;
  3025. nBM>0; --nBM, ++pTemp)
  3026. {
  3027. if (pTemp->hInst==lprb->hInstOld && pTemp->wID==lprb->nIDOld)
  3028. {
  3029. // number of buttons must match
  3030. pTemp->hInst = lprb->hInstNew;
  3031. pTemp->wID = lprb->nIDNew;
  3032. pTemp->nButtons = lprb->nButtons;
  3033. TBInvalidateImageList(ptb);
  3034. return TRUE;
  3035. }
  3036. }
  3037. return FALSE;
  3038. }
  3039. void TBInvalidateItemRects(PTBSTATE ptb)
  3040. {
  3041. // Invalidate item rect cache
  3042. ptb->fItemRectsValid = FALSE;
  3043. // Invalidate the tooltips
  3044. ptb->fTTNeedsFlush = TRUE;
  3045. // Invalidate the ideal size cache
  3046. ptb->szCached.cx = -1;
  3047. ptb->szCached.cy = -1;
  3048. }
  3049. void FlushToolTipsMgrNow(PTBSTATE ptb) {
  3050. // change all the rects for the tool tips mgr. this is
  3051. // cheap, and we don't do it often, so go ahead
  3052. // and do them all.
  3053. if(ptb->hwndToolTips) {
  3054. UINT i;
  3055. TOOLINFO ti;
  3056. LPTBBUTTONDATA pButton;
  3057. ti.cbSize = SIZEOF(ti);
  3058. ti.hwnd = ptb->ci.hwnd;
  3059. ti.lpszText = LPSTR_TEXTCALLBACK;
  3060. for ( i = 0, pButton = ptb->Buttons;
  3061. i < (UINT)ptb->iNumButtons;
  3062. i++, pButton++) {
  3063. if (!(pButton->fsStyle & BTNS_SEP)) {
  3064. ti.uId = pButton->idCommand;
  3065. if (!TB_GetItemRect(ptb, i, &ti.rect) ||
  3066. ((ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS) && TBIsRectClipped(ptb, &ti.rect))) {
  3067. ti.rect.left = ti.rect.right = ti.rect.top = ti.rect.bottom = 0;
  3068. }
  3069. SendMessage(ptb->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  3070. }
  3071. }
  3072. ptb->fTTNeedsFlush = FALSE;
  3073. }
  3074. }
  3075. BOOL TBReallocButtons(PTBSTATE ptb, UINT uButtons)
  3076. {
  3077. LPTBBUTTONDATA ptbbNew;
  3078. LPTBBUTTONDATA pOldCaptureButton;
  3079. if (!ptb || !ptb->uStructSize)
  3080. return FALSE;
  3081. // When we realloc the Button array, make sure all interior pointers
  3082. // move with it. (This should probably be an index.)
  3083. pOldCaptureButton = ptb->pCaptureButton;
  3084. // realloc the button table
  3085. ptbbNew = (LPTBBUTTONDATA)CCLocalReAlloc(ptb->Buttons,
  3086. uButtons * sizeof(TBBUTTONDATA));
  3087. if (!ptbbNew) return FALSE;
  3088. if (pOldCaptureButton)
  3089. ptb->pCaptureButton = (LPTBBUTTONDATA)(
  3090. (LPBYTE)ptbbNew +
  3091. ((LPBYTE)pOldCaptureButton - (LPBYTE)ptb->Buttons));
  3092. ptb->Buttons = ptbbNew;
  3093. return TRUE;
  3094. }
  3095. BOOL TBInsertButtons(PTBSTATE ptb, UINT uWhere, UINT uButtons, LPTBBUTTON lpButtons, BOOL fNative)
  3096. {
  3097. LPTBBUTTONDATA pOut;
  3098. LPTBBUTTONDATA ptbbIn;
  3099. UINT uAdded;
  3100. UINT uStart;
  3101. BOOL fRecalc;
  3102. int idHot = -1;
  3103. if (!TBReallocButtons(ptb, ptb->iNumButtons + uButtons))
  3104. return FALSE;
  3105. // comments by chee (not the original author) so they not be
  3106. // exactly right... be warned.
  3107. // if where points beyond the end, set it at the end
  3108. if (uWhere > (UINT)ptb->iNumButtons)
  3109. uWhere = ptb->iNumButtons;
  3110. // Need to save these since the values gues toasted.
  3111. uAdded = uButtons;
  3112. uStart = uWhere;
  3113. // Correct the hot item when we add something something. Since the hot item is index based, the index
  3114. // has probrably changed
  3115. if (ptb->iHot >= 0 && ptb->iHot < ptb->iNumButtons)
  3116. idHot = ptb->Buttons[ptb->iHot].idCommand;
  3117. // move buttons above uWhere up uButton spaces
  3118. // the uWhere gets inverted and counts to zero..
  3119. //
  3120. // REVIEW: couldn't this be done with MoveMemory?
  3121. // MoveMemory(&ptb->Buttons[uWhere], &ptb->Buttons[uWhere+uButtons], sizeof(ptb->Buttons[0])*(ptb->iNumButtons - uWhere));
  3122. //
  3123. for (ptbbIn = &ptb->Buttons[ptb->iNumButtons-1], pOut = ptbbIn+uButtons,
  3124. uWhere=(UINT)ptb->iNumButtons-uWhere; uWhere>0;
  3125. --ptbbIn, --pOut, --uWhere)
  3126. *pOut = *ptbbIn;
  3127. // only need to recalc if there are strings & room enough to actually show them
  3128. fRecalc = (TBHasStrings(ptb) && ((ptb->ci.style & TBSTYLE_LIST) || ((ptb->iDyBitmap + ptb->yPad + g_cyEdge) < ptb->iButHeight)));
  3129. // now do the copy.
  3130. for (lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons+ptb->uStructSize*(uButtons-1)),
  3131. ptb->iNumButtons+=(int)uButtons; // init
  3132. uButtons>0; //test
  3133. --pOut, lpButtons=(LPTBBUTTON)((LPBYTE)lpButtons-ptb->uStructSize), --uButtons)
  3134. {
  3135. TBInputStruct(ptb, pOut, lpButtons);
  3136. // If this button is a seperator, then should not use the string
  3137. // buffer passed in, because it could be bogus data.
  3138. if (pOut->fsStyle & BTNS_SEP)
  3139. pOut->iString = -1;
  3140. if (TBISSTRINGPTR(pOut->iString)) {
  3141. LPTSTR psz = (LPTSTR)pOut->iString;
  3142. if (!fNative) {
  3143. psz = ProduceWFromA(ptb->ci.uiCodePage, (LPSTR)psz);
  3144. }
  3145. pOut->iString = 0;
  3146. Str_Set((LPTSTR*)&pOut->iString, psz);
  3147. if (!fNative)
  3148. FreeProducedString(psz);
  3149. if (!ptb->fNoStringPool)
  3150. fRecalc = TRUE;
  3151. ptb->fNoStringPool = TRUE;
  3152. }
  3153. if(ptb->hwndToolTips && !(lpButtons->fsStyle & BTNS_SEP)) {
  3154. TOOLINFO ti;
  3155. // don't bother setting the rect because we'll do it below
  3156. // in TBInvalidateItemRects;
  3157. ti.cbSize = sizeof(ti);
  3158. ti.uFlags = 0;
  3159. ti.hwnd = ptb->ci.hwnd;
  3160. ti.uId = lpButtons->idCommand;
  3161. ti.lpszText = LPSTR_TEXTCALLBACK;
  3162. SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  3163. (LPARAM)(LPTOOLINFO)&ti);
  3164. }
  3165. if (pOut->fsStyle & BTNS_SEP && pOut->DUMMYUNION_MEMBER(cxySep) <= 0)
  3166. {
  3167. // Compat: Corel (Font navigator) expects the separators to be
  3168. // 8 pixels wide.
  3169. // as do many old apps.
  3170. //
  3171. // so if it's not flat or not vertical, put it to defautl to win95 size
  3172. pOut->DUMMYUNION_MEMBER(cxySep) = g_dxButtonSep;
  3173. }
  3174. }
  3175. // Re-compute layout if toolbar is wrappable.
  3176. if ((ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN) ||
  3177. (ptb->ci.style & TBSTYLE_WRAPABLE))
  3178. {
  3179. // NOTE: we used to do send ourself a message instead of call directly...
  3180. //SendMessage(ptb->ci.hwnd, TB_AUTOSIZE, 0, 0);
  3181. TBAutoSize(ptb);
  3182. }
  3183. TBInvalidateItemRects(ptb);
  3184. // adding and removing buttons during toolbar customization shouldn't
  3185. // result in recalcing the sizes of buttons.
  3186. if (fRecalc && !ptb->hdlgCust)
  3187. TBRecalc(ptb);
  3188. //
  3189. // Reorder notification so apps can go requery what's on the toolbar if
  3190. // more than 1 button was added; otherwise, just say create.
  3191. //
  3192. if (uAdded == 1)
  3193. MyNotifyWinEvent(EVENT_OBJECT_CREATE, ptb->ci.hwnd, OBJID_CLIENT,
  3194. uWhere+1);
  3195. else
  3196. MyNotifyWinEvent(EVENT_OBJECT_REORDER, ptb->ci.hwnd, OBJID_CLIENT, 0);
  3197. // was there a hot item before the delete?
  3198. if (idHot != -1)
  3199. {
  3200. // Yes; Then update it to the current index
  3201. ptb->iHot = PositionFromID(ptb, idHot);
  3202. }
  3203. TBInvalidateItemRects(ptb);
  3204. // We need to completely redraw the toolbar at this point.
  3205. // this MUST be done last!
  3206. // tbrecalc and others will nuke out invalid area and we won't paint if this isn't last
  3207. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3208. return(TRUE);
  3209. }
  3210. /* Notice that the state structure is not realloc'ed smaller at this
  3211. * point. This is a time optimization, and the fact that the structure
  3212. * will not move is used in other places.
  3213. */
  3214. BOOL DeleteButton(PTBSTATE ptb, UINT uIndex)
  3215. {
  3216. TBNOTIFY tbn = { 0 };
  3217. LPTBBUTTONDATA pIn, pOut;
  3218. BOOL fRecalc;
  3219. int idHot = -1;
  3220. if (uIndex >= (UINT)ptb->iNumButtons)
  3221. return FALSE;
  3222. if (&ptb->Buttons[uIndex] == ptb->pCaptureButton) {
  3223. if (ptb->uStructSize == 0x14)
  3224. ptb->fRequeryCapture = TRUE;
  3225. if (!CCReleaseCapture(&ptb->ci)) return FALSE;
  3226. ptb->pCaptureButton = NULL;
  3227. }
  3228. // Correct the hot item when we remove something. Since the hot item is index based, the index
  3229. // has probrably changed
  3230. if (ptb->iHot >= 0 && ptb->iHot < ptb->iNumButtons)
  3231. idHot = ptb->Buttons[ptb->iHot].idCommand;
  3232. // Notify Active Accessibility of the delete
  3233. MyNotifyWinEvent(EVENT_OBJECT_DESTROY, ptb->ci.hwnd, OBJID_CLIENT, uIndex+1);
  3234. // Notify client of the delete
  3235. tbn.iItem = ptb->Buttons[uIndex].idCommand;
  3236. TBOutputStruct(ptb, &ptb->Buttons[uIndex], &tbn.tbButton);
  3237. CCSendNotify(&ptb->ci, TBN_DELETINGBUTTON, &tbn.hdr);
  3238. if (TBISSTRINGPTR(ptb->Buttons[uIndex].iString))
  3239. Str_Set((LPTSTR*)&ptb->Buttons[uIndex].iString, NULL);
  3240. if (ptb->hwndToolTips) {
  3241. TOOLINFO ti;
  3242. ti.cbSize = sizeof(ti);
  3243. ti.hwnd = ptb->ci.hwnd;
  3244. ti.uId = ptb->Buttons[uIndex].idCommand;
  3245. SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  3246. }
  3247. --ptb->iNumButtons;
  3248. pOut = ptb->Buttons + uIndex;
  3249. fRecalc = (pOut->fsState & TBSTATE_WRAP);
  3250. for (pIn = pOut + 1; uIndex<(UINT)ptb->iNumButtons; ++uIndex, ++pIn, ++pOut)
  3251. {
  3252. fRecalc |= (pIn->fsState & TBSTATE_WRAP);
  3253. *pOut = *pIn;
  3254. }
  3255. // We need to completely recalc or redraw the toolbar at this point.
  3256. if (((ptb->ci.style & TBSTYLE_WRAPABLE)
  3257. || (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN)) && fRecalc)
  3258. {
  3259. RECT rc;
  3260. HWND hwnd = ptb->ci.hwnd;
  3261. if (!(ptb->ci.style & CCS_NORESIZE) && !(ptb->ci.style & CCS_NOPARENTALIGN))
  3262. hwnd = GetParent(hwnd);
  3263. GetWindowRect(hwnd, &rc);
  3264. if (ptb->ci.style & TBSTYLE_WRAPABLE)
  3265. WrapToolbar(ptb, rc.right - rc.left, &rc, NULL);
  3266. else
  3267. WrapToolbarCol(ptb, ptb->sizeBound.cy, &rc, NULL);
  3268. }
  3269. // was there a hot item before the delete?
  3270. if (idHot != -1)
  3271. {
  3272. // Yes; Then update it to the current index
  3273. ptb->iHot = PositionFromID(ptb, idHot);
  3274. }
  3275. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3276. TBInvalidateItemRects(ptb);
  3277. return TRUE;
  3278. }
  3279. // move button at location iOld to location iNew, sliding everything
  3280. // after iNew UP.
  3281. BOOL PASCAL TBMoveButton(PTBSTATE ptb, UINT iOld, UINT iNew)
  3282. {
  3283. TBBUTTONDATA tbd, *ptbdOld, *ptbdNew;
  3284. if (iOld >= (UINT)ptb->iNumButtons)
  3285. return FALSE;
  3286. if (iNew > (UINT)ptb->iNumButtons-1)
  3287. iNew = (UINT)ptb->iNumButtons-1;
  3288. if (iOld == iNew)
  3289. return FALSE;
  3290. TBInvalidateItemRects(ptb);
  3291. ptbdOld = &(ptb->Buttons[iOld]);
  3292. ptbdNew = &(ptb->Buttons[iNew]);
  3293. tbd = *ptbdOld;
  3294. {
  3295. TBBUTTONDATA *ptbdSrc;
  3296. TBBUTTONDATA *ptbdDst;
  3297. int iCount, iInc;
  3298. if (iOld < iNew)
  3299. {
  3300. // move [iOld+1..iNew] to [iOld..iNew-1]
  3301. iCount = iNew - iOld;
  3302. iInc = 1;
  3303. ptbdSrc = ptbdOld + 1;
  3304. ptbdDst = ptbdOld;
  3305. if (ptb->pCaptureButton > ptbdOld && ptb->pCaptureButton <= ptbdNew)
  3306. ptb->pCaptureButton--;
  3307. }
  3308. else
  3309. {
  3310. ASSERT(iNew < iOld);
  3311. // move [iNew..iOld-1] to [iNew+1..iOld]
  3312. iCount = iOld - iNew;
  3313. iInc = -1;
  3314. ptbdSrc = ptbdNew + iCount - 1;
  3315. ptbdDst = ptbdNew + iCount;
  3316. if (ptb->pCaptureButton >= ptbdNew && ptb->pCaptureButton < ptbdOld)
  3317. ptb->pCaptureButton++;
  3318. }
  3319. do {
  3320. *ptbdDst = *ptbdSrc;
  3321. ptbdDst += iInc;
  3322. ptbdSrc += iInc;
  3323. iCount--;
  3324. } while (iCount);
  3325. }
  3326. *ptbdNew = tbd;
  3327. if (ptb->pCaptureButton == ptbdOld)
  3328. ptb->pCaptureButton = ptbdNew;
  3329. TBAutoSize(ptb);
  3330. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3331. return TRUE;
  3332. }
  3333. // deal with old TBBUTON structs for compatibility
  3334. void TBInputStruct(PTBSTATE ptb, LPTBBUTTONDATA pButtonInt, LPTBBUTTON pButtonExt)
  3335. {
  3336. pButtonInt->DUMMYUNION_MEMBER(iBitmap) = pButtonExt->iBitmap;
  3337. pButtonInt->idCommand = pButtonExt->idCommand;
  3338. pButtonInt->fsState = pButtonExt->fsState;
  3339. pButtonInt->fsStyle = pButtonExt->fsStyle;
  3340. pButtonInt->cx = 0;
  3341. if (ptb->uStructSize >= sizeof(TBBUTTON))
  3342. {
  3343. pButtonInt->dwData = pButtonExt->dwData;
  3344. pButtonInt->iString = pButtonExt->iString;
  3345. }
  3346. else
  3347. {
  3348. /* It is assumed the only other possibility is the OLDBUTTON struct */
  3349. /* We don't care about dwData */
  3350. pButtonInt->dwData = 0;
  3351. pButtonInt->iString = -1;
  3352. }
  3353. }
  3354. void TBOutputStruct(PTBSTATE ptb, LPTBBUTTONDATA pButtonInt, LPTBBUTTON pButtonExt)
  3355. {
  3356. ZeroMemory(pButtonExt, ptb->uStructSize);
  3357. pButtonExt->iBitmap = pButtonInt->DUMMYUNION_MEMBER(iBitmap);
  3358. pButtonExt->idCommand = pButtonInt->idCommand;
  3359. pButtonExt->fsState = pButtonInt->fsState;
  3360. pButtonExt->fsStyle = pButtonInt->fsStyle;
  3361. // We're returning cx in the bReserved field
  3362. COMPILETIME_ASSERT(FIELD_OFFSET(TBBUTTONDATA, cx) == FIELD_OFFSET(TBBUTTON, bReserved));
  3363. COMPILETIME_ASSERT(sizeof(pButtonInt->cx) <= sizeof(pButtonExt->bReserved));
  3364. ((LPTBBUTTONDATA)pButtonExt)->cx = pButtonInt->cx;
  3365. if (ptb->uStructSize >= sizeof(TBBUTTON))
  3366. {
  3367. pButtonExt->dwData = pButtonInt->dwData;
  3368. pButtonExt->iString = pButtonInt->iString;
  3369. }
  3370. }
  3371. void TBOnButtonStructSize(PTBSTATE ptb, UINT uStructSize)
  3372. {
  3373. /* You are not allowed to change this after adding buttons.
  3374. */
  3375. if (ptb && !ptb->iNumButtons)
  3376. {
  3377. ptb->uStructSize = uStructSize;
  3378. }
  3379. }
  3380. void TBAutoSize(PTBSTATE ptb)
  3381. {
  3382. HWND hwndParent;
  3383. RECT rc;
  3384. int nTBThickness = 0;
  3385. if (ptb->fRedrawOff) {
  3386. // redraw is off; defer autosize until redraw is turned back on
  3387. ptb->fRecalc = TRUE;
  3388. return;
  3389. }
  3390. if (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN)
  3391. {
  3392. ASSERT(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL);
  3393. nTBThickness = ptb->iButWidth * CountCols(ptb) + g_cyEdge * 2;
  3394. }
  3395. else
  3396. nTBThickness = ptb->iButHeight * CountRows(ptb) + g_cxEdge * 2;
  3397. hwndParent = GetParent(ptb->ci.hwnd);
  3398. if (!hwndParent)
  3399. return;
  3400. if ((ptb->ci.style & TBSTYLE_WRAPABLE)
  3401. || (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN))
  3402. {
  3403. RECT rcNew;
  3404. if ((ptb->ci.style & CCS_NORESIZE) || (ptb->ci.style & CCS_NOPARENTALIGN))
  3405. GetWindowRect(ptb->ci.hwnd, &rc);
  3406. else
  3407. GetWindowRect(hwndParent, &rc);
  3408. if (ptb->ci.style & TBSTYLE_WRAPABLE)
  3409. WrapToolbar(ptb, rc.right - rc.left, &rcNew, NULL);
  3410. else
  3411. WrapToolbarCol(ptb, ptb->sizeBound.cy, &rcNew, NULL);
  3412. // Some sample app found a bug in our autosize code which this line
  3413. // fixes. Unfortunately Carbon Copy 32 (IE4 bug 31943) relies on the
  3414. // broken behavior and fixing this clips the buttons.
  3415. //
  3416. //nTBThickness = rcNew.bottom - rcNew.top + g_cxEdge;
  3417. }
  3418. if ((ptb->ci.style & TBSTYLE_WRAPABLE) ||
  3419. (ptb->dwStyleEx & (TBSTYLE_EX_MULTICOLUMN | TBSTYLE_EX_HIDECLIPPEDBUTTONS)))
  3420. {
  3421. TBInvalidateItemRects(ptb);
  3422. }
  3423. GetWindowRect(ptb->ci.hwnd, &rc);
  3424. MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rc, 2);
  3425. NewSize(ptb->ci.hwnd, nTBThickness, ptb->ci.style,
  3426. rc.left, rc.top, rc.right, rc.bottom);
  3427. }
  3428. void TBSetStyle(PTBSTATE ptb, DWORD dwStyle)
  3429. {
  3430. BOOL fSizeChanged = FALSE;
  3431. if ((BOOL)(ptb->ci.style & TBSTYLE_WRAPABLE) != (BOOL)(dwStyle & TBSTYLE_WRAPABLE))
  3432. {
  3433. int i;
  3434. fSizeChanged = TRUE;
  3435. for (i=0; i<ptb->iNumButtons; i++)
  3436. ptb->Buttons[i].fsState &= ~TBSTATE_WRAP;
  3437. }
  3438. ptb->ci.style = dwStyle;
  3439. if (fSizeChanged)
  3440. TBRecalc(ptb);
  3441. TBAutoSize(ptb);
  3442. TraceMsg(TF_TOOLBAR, "toolbar window style changed %x", ptb->ci.style);
  3443. }
  3444. void TBSetStyleEx(PTBSTATE ptb, DWORD dwStyleEx, DWORD dwStyleMaskEx)
  3445. {
  3446. BOOL fSizeChanged = FALSE;
  3447. if (dwStyleMaskEx)
  3448. dwStyleEx = (ptb->dwStyleEx & ~dwStyleMaskEx) | (dwStyleEx & dwStyleMaskEx);
  3449. // Second, we can validate a few of the bits:
  3450. // Multicolumn should never be set w/o the vertical style...
  3451. ASSERT((ptb->dwStyleEx & TBSTYLE_EX_VERTICAL) || !(ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN));
  3452. // also can't be set with hide clipped buttons style (for now)
  3453. ASSERT(!(ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS) || !(ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN));
  3454. // ...but just in case someone gets it wrong, we'll set the vertical
  3455. // style and rip off the hide clipped buttons style
  3456. if (dwStyleEx & TBSTYLE_EX_MULTICOLUMN)
  3457. {
  3458. dwStyleEx |= TBSTYLE_EX_VERTICAL;
  3459. dwStyleEx &= ~TBSTYLE_EX_HIDECLIPPEDBUTTONS;
  3460. }
  3461. // Then, some things need to be tweaked when they change
  3462. if ((ptb->dwStyleEx ^ dwStyleEx) & TBSTYLE_EX_MULTICOLUMN)
  3463. {
  3464. int i;
  3465. // Clear all the wrap states if we're changing multicolumn styles
  3466. for (i = 0; i < ptb->iNumButtons; i++)
  3467. ptb->Buttons[i].fsState &= ~TBSTATE_WRAP;
  3468. fSizeChanged = TRUE;
  3469. }
  3470. if ((ptb->dwStyleEx ^ dwStyleEx) & TBSTYLE_EX_MIXEDBUTTONS)
  3471. {
  3472. int i;
  3473. for (i = 0; i < ptb->iNumButtons; i++)
  3474. (ptb->Buttons[i]).cx = 0;
  3475. fSizeChanged = TRUE;
  3476. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3477. }
  3478. if ((ptb->dwStyleEx ^ dwStyleEx) & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
  3479. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3480. ptb->dwStyleEx = dwStyleEx;
  3481. if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  3482. TBSetStyle(ptb, CCS_VERT); // vertical sep and insert mark orientation
  3483. if (fSizeChanged)
  3484. {
  3485. TBRecalc(ptb);
  3486. TBAutoSize(ptb);
  3487. }
  3488. TraceMsg(TF_TOOLBAR, "toolbar window extended style changed %x", ptb->dwStyleEx);
  3489. }
  3490. LRESULT TB_OnSetImage(PTBSTATE ptb, LPTBBUTTONDATA ptbButton, int iImage)
  3491. {
  3492. if (!ptb->fHimlNative) {
  3493. if (ptb->fHimlValid) {
  3494. if (!TBGetImageList(ptb, HIML_NORMAL, 0) ||
  3495. iImage >= ImageList_GetImageCount(TBGetImageList(ptb, HIML_NORMAL, 0)))
  3496. return FALSE;
  3497. } else {
  3498. PTBBMINFO pTemp;
  3499. int nBitmap;
  3500. UINT nTot;
  3501. // we're not natively himl and we've got some invalid
  3502. // image state, so we need to count the bitmaps ourselvesa
  3503. pTemp = ptb->pBitmaps;
  3504. nTot = 0;
  3505. for (nBitmap=0; nBitmap < ptb->nBitmaps; nBitmap++) {
  3506. nTot += pTemp->nButtons;
  3507. pTemp++;
  3508. }
  3509. if (iImage >= (int)nTot)
  3510. return FALSE;
  3511. }
  3512. }
  3513. ptbButton->DUMMYUNION_MEMBER(iBitmap) = iImage;
  3514. InvalidateButton(ptb, ptbButton, FALSE);
  3515. UpdateWindow(ptb->ci.hwnd);
  3516. return TRUE;
  3517. }
  3518. void TB_OnDestroy(PTBSTATE ptb)
  3519. {
  3520. HWND hwnd = ptb->ci.hwnd;
  3521. int i;
  3522. for (i = 0; i < ptb->iNumButtons; i++) {
  3523. if (TBISSTRINGPTR(ptb->Buttons[i].iString))
  3524. Str_Set((LPTSTR*)&ptb->Buttons[i].iString, NULL);
  3525. }
  3526. //
  3527. // If the toolbar created tooltips, then destroy them.
  3528. //
  3529. if ((ptb->ci.style & TBSTYLE_TOOLTIPS) && IsWindow(ptb->hwndToolTips)) {
  3530. DestroyWindow (ptb->hwndToolTips);
  3531. ptb->hwndToolTips = NULL;
  3532. }
  3533. if (ptb->hDragProxy)
  3534. DestroyDragProxy(ptb->hDragProxy);
  3535. if (ptb->hbmMono)
  3536. DeleteObject(ptb->hbmMono);
  3537. ReleaseMonoDC(ptb);
  3538. if (ptb->nStrings > 0)
  3539. DestroyStrings(ptb);
  3540. if (ptb->hfontIcon && ptb->fFontCreated)
  3541. DeleteObject(ptb->hfontIcon);
  3542. // only do this destroy if pBitmaps exists..
  3543. // this is our signal that it was from an old style toolba
  3544. // and we created it ourselves.
  3545. if (ptb->pBitmaps)
  3546. ImageList_Destroy(TBGetImageList(ptb, HIML_NORMAL, 0));
  3547. if (ptb->pBitmaps)
  3548. LocalFree(ptb->pBitmaps);
  3549. // couldn't have created tb if pimgs creation failed
  3550. CCLocalReAlloc(ptb->pimgs, 0);
  3551. Str_Set(&ptb->pszTip, NULL);
  3552. if (ptb->Buttons) LocalFree(ptb->Buttons);
  3553. LocalFree((HLOCAL)ptb);
  3554. SetWindowInt(hwnd, 0, 0);
  3555. TerminateDitherBrush();
  3556. }
  3557. void TB_OnSetState(PTBSTATE ptb, LPTBBUTTONDATA ptbButton, BYTE bState, int iPos)
  3558. {
  3559. BYTE fsState;
  3560. fsState = bState ^ ptbButton->fsState;
  3561. ptbButton->fsState = bState;
  3562. if (fsState)
  3563. {
  3564. if (ptb->fRedrawOff)
  3565. {
  3566. ptb->fInvalidate = ptb->fRecalc = TRUE;
  3567. }
  3568. else
  3569. {
  3570. if (fsState & TBSTATE_HIDDEN)
  3571. {
  3572. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3573. TBRecalc(ptb);
  3574. }
  3575. else
  3576. InvalidateButton(ptb, ptbButton, TRUE);
  3577. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, ptb->ci.hwnd, OBJID_CLIENT,
  3578. iPos+1);
  3579. }
  3580. }
  3581. }
  3582. void TB_OnSetCmdID(PTBSTATE ptb, LPTBBUTTONDATA ptbButton, UINT idCommand)
  3583. {
  3584. UINT uiOldID;
  3585. uiOldID = ptbButton->idCommand;
  3586. ptbButton->idCommand = idCommand;
  3587. //
  3588. // If the app was using tooltips, then
  3589. // we need to update the command id there also.
  3590. //
  3591. if(ptb->hwndToolTips) {
  3592. TOOLINFO ti;
  3593. //
  3594. // Query the old information
  3595. //
  3596. ti.cbSize = sizeof(ti);
  3597. ti.hwnd = ptb->ci.hwnd;
  3598. ti.uId = uiOldID;
  3599. SendMessage(ptb->hwndToolTips, TTM_GETTOOLINFO, 0,
  3600. (LPARAM)(LPTOOLINFO)&ti);
  3601. //
  3602. // Delete the old tool since we can't just
  3603. // change the command id.
  3604. //
  3605. SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0,
  3606. (LPARAM)(LPTOOLINFO)&ti);
  3607. //
  3608. // Add the new tool with the new command id.
  3609. //
  3610. ti.uId = idCommand;
  3611. SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0,
  3612. (LPARAM)(LPTOOLINFO)&ti);
  3613. }
  3614. }
  3615. LRESULT TB_OnSetButtonInfo(PTBSTATE ptb, int idBtn, LPTBBUTTONINFO ptbbi)
  3616. {
  3617. int iPos;
  3618. BOOL fInvalidateAll = FALSE;
  3619. if (ptbbi->cbSize != SIZEOF(TBBUTTONINFO))
  3620. return 0;
  3621. if (ptbbi->dwMask & TBIF_BYINDEX)
  3622. iPos = idBtn;
  3623. else
  3624. iPos = PositionFromID(ptb, idBtn);
  3625. if (iPos != -1)
  3626. {
  3627. LPTBBUTTONDATA ptbButton;
  3628. BOOL fInvalidate = FALSE;
  3629. ptbButton = ptb->Buttons + iPos;
  3630. if (ptbbi->dwMask & TBIF_STYLE) {
  3631. if ((ptbButton->fsStyle ^ ptbbi->fsStyle) & (BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN))
  3632. {
  3633. // Width may have changed!
  3634. fInvalidateAll = TRUE;
  3635. }
  3636. if ((ptbButton->fsStyle ^ ptbbi->fsStyle) & BTNS_AUTOSIZE)
  3637. ptbButton->cx = 0;
  3638. ptbButton->fsStyle = ptbbi->fsStyle;
  3639. fInvalidate = TRUE;
  3640. }
  3641. if (ptbbi->dwMask & TBIF_STATE) {
  3642. TB_OnSetState(ptb, ptbButton, ptbbi->fsState, iPos);
  3643. }
  3644. if (ptbbi->dwMask & TBIF_IMAGE) {
  3645. TB_OnSetImage(ptb, ptbButton, ptbbi->iImage);
  3646. }
  3647. if (ptbbi->dwMask & TBIF_SIZE) {
  3648. ptbButton->cx = ptbbi->cx;
  3649. fInvalidate = TRUE;
  3650. fInvalidateAll = TRUE;
  3651. }
  3652. if (ptbbi->dwMask & TBIF_TEXT) {
  3653. // changing the text on an autosize button means recalc
  3654. if (BTN_IS_AUTOSIZE(ptb, ptbButton)) {
  3655. fInvalidateAll = TRUE;
  3656. ptbButton->cx = (WORD)0;
  3657. }
  3658. ptb->fNoStringPool = TRUE;
  3659. if (!TBISSTRINGPTR(ptbButton->iString)) {
  3660. ptbButton->iString = 0;
  3661. }
  3662. Str_Set((LPTSTR*)&ptbButton->iString, ptbbi->pszText);
  3663. fInvalidate = TRUE;
  3664. }
  3665. if (ptbbi->dwMask & TBIF_LPARAM) {
  3666. ptbButton->dwData = ptbbi->lParam;
  3667. }
  3668. if (ptbbi->dwMask & TBIF_COMMAND) {
  3669. TB_OnSetCmdID(ptb, ptbButton, ptbbi->idCommand);
  3670. }
  3671. if (fInvalidateAll || fInvalidate) {
  3672. TBInvalidateItemRects(ptb);
  3673. if (fInvalidateAll)
  3674. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  3675. else
  3676. InvalidateButton(ptb, ptbButton, TRUE);
  3677. }
  3678. return TRUE;
  3679. }
  3680. return FALSE;
  3681. }
  3682. LRESULT TB_OnGetButtonInfo(PTBSTATE ptb, int idBtn, LPTBBUTTONINFO ptbbi)
  3683. {
  3684. int iPos;
  3685. if (ptbbi->cbSize != SIZEOF(TBBUTTONINFO))
  3686. return -1;
  3687. if (ptbbi->dwMask & TBIF_BYINDEX)
  3688. iPos = idBtn;
  3689. else
  3690. iPos = PositionFromID(ptb, idBtn);
  3691. if (iPos >= 0 && iPos < ptb->iNumButtons)
  3692. {
  3693. LPTBBUTTONDATA ptbButton;
  3694. ptbButton = ptb->Buttons + iPos;
  3695. if (ptbbi->dwMask & TBIF_STYLE) {
  3696. ptbbi->fsStyle = ptbButton->fsStyle;
  3697. }
  3698. if (ptbbi->dwMask & TBIF_STATE) {
  3699. ptbbi->fsState = ptbButton->fsState;
  3700. }
  3701. if (ptbbi->dwMask & TBIF_IMAGE) {
  3702. ptbbi->iImage = ptbButton->DUMMYUNION_MEMBER(iBitmap);
  3703. }
  3704. if (ptbbi->dwMask & TBIF_SIZE) {
  3705. ptbbi->cx = (WORD) ptbButton->cx;
  3706. }
  3707. if (ptbbi->dwMask & TBIF_TEXT)
  3708. {
  3709. if (TBISSTRINGPTR(ptbButton->iString))
  3710. {
  3711. StringCchCopy(ptbbi->pszText, ptbbi->cchText, (LPCTSTR)ptbButton->iString);
  3712. }
  3713. }
  3714. if (ptbbi->dwMask & TBIF_LPARAM) {
  3715. ptbbi->lParam = ptbButton->dwData;
  3716. }
  3717. if (ptbbi->dwMask & TBIF_COMMAND) {
  3718. ptbbi->idCommand = ptbButton->idCommand;
  3719. }
  3720. } else
  3721. iPos = -1;
  3722. return iPos;
  3723. }
  3724. UINT GetAccelerator(LPTSTR psz)
  3725. {
  3726. UINT ch = (UINT)-1;
  3727. LPTSTR pszAccel = psz;
  3728. // then prefixes are allowed.... see if it has one
  3729. do
  3730. {
  3731. pszAccel = StrChr(pszAccel, CH_PREFIX);
  3732. if (pszAccel)
  3733. {
  3734. pszAccel = FastCharNext(pszAccel);
  3735. // handle having &&
  3736. if (*pszAccel != CH_PREFIX)
  3737. ch = *pszAccel;
  3738. else
  3739. pszAccel = FastCharNext(pszAccel);
  3740. }
  3741. }
  3742. while (pszAccel && (ch == (UINT)-1));
  3743. return ch;
  3744. }
  3745. UINT TBButtonAccelerator(PTBSTATE ptb, LPTBBUTTONDATA ptbn)
  3746. {
  3747. UINT ch = (UINT)-1;
  3748. LPTSTR psz = TB_StrForButton(ptb, ptbn);
  3749. if (psz && *psz) {
  3750. if (!(ptb->uDrawTextMask & ptb->uDrawText & DT_NOPREFIX)) {
  3751. ch = GetAccelerator(psz);
  3752. }
  3753. if (ch == (UINT)-1) {
  3754. // no prefix found. use the first char
  3755. ch = (UINT)*psz;
  3756. }
  3757. }
  3758. return (UINT)ch;
  3759. }
  3760. /*----------------------------------------------------------
  3761. Purpose: Returns the number of buttons that have the passed
  3762. in char as their accelerator
  3763. */
  3764. int TBHasAccelerator(PTBSTATE ptb, UINT ch)
  3765. {
  3766. int i;
  3767. int c = 0;
  3768. for (i = 0; i < ptb->iNumButtons; i++)
  3769. {
  3770. if (!ChrCmpI((WORD)TBButtonAccelerator(ptb, &ptb->Buttons[i]), (WORD)ch))
  3771. c++;
  3772. }
  3773. if (c == 0)
  3774. {
  3775. NMCHAR nm = {0};
  3776. nm.ch = ch;
  3777. nm.dwItemPrev = 0;
  3778. nm.dwItemNext = -1;
  3779. // The duplicate accelerator is used to expand or execute a menu item,
  3780. // if we determine that there are no items, we still want to ask the
  3781. // owner if there are any...
  3782. if (CCSendNotify(&ptb->ci, TBN_MAPACCELERATOR, &nm.hdr) &&
  3783. nm.dwItemNext != -1)
  3784. {
  3785. c++;
  3786. }
  3787. }
  3788. return c;
  3789. }
  3790. /*----------------------------------------------------------
  3791. Purpose: Returns TRUE if the character maps to more than one
  3792. button.
  3793. */
  3794. BOOL TBHasDupChar(PTBSTATE ptb, UINT ch)
  3795. {
  3796. BOOL bRet = FALSE;
  3797. NMTBDUPACCELERATOR nmda;
  3798. int c = 0;
  3799. nmda.ch = ch;
  3800. // BUGBUG (lamadio): this is going away
  3801. if (CCSendNotify(&ptb->ci, TBN_DUPACCELERATOR, &nmda.hdr))
  3802. {
  3803. bRet = nmda.fDup;
  3804. }
  3805. else
  3806. {
  3807. if (TBHasAccelerator(ptb, ch) > 1)
  3808. bRet = TRUE;
  3809. }
  3810. return bRet;
  3811. }
  3812. /*----------------------------------------------------------
  3813. Purpose: Returns the index of the item whose accelerator matches
  3814. the given character. Starts at the current hot item.
  3815. Returns: -1 if nothing found
  3816. */
  3817. int TBItemFromAccelerator(PTBSTATE ptb, UINT ch, BOOL * pbDup)
  3818. {
  3819. int iRet = -1;
  3820. int i;
  3821. int iStart = ptb->iHot;
  3822. NMTBWRAPACCELERATOR nmwa;
  3823. NMCHAR nm = {0};
  3824. nm.ch = ch;
  3825. nm.dwItemPrev = iStart;
  3826. nm.dwItemNext = -1;
  3827. // Ask the client if they want to handle this keyboard press
  3828. if (CCSendNotify(&ptb->ci, TBN_MAPACCELERATOR, &nm.hdr) &&
  3829. (int)nm.dwItemNext > iStart && (int)nm.dwItemNext < ptb->iNumButtons)
  3830. {
  3831. // They handled it, so we're just going to return the position
  3832. // that they said.
  3833. iRet = nm.dwItemNext;
  3834. }
  3835. else for (i = 0; i < ptb->iNumButtons; i++)
  3836. {
  3837. if ( iStart + 1 >= ptb->iNumButtons )
  3838. {
  3839. nmwa.ch = ch;
  3840. if (CCSendNotify(&ptb->ci, TBN_WRAPACCELERATOR, &nmwa.hdr))
  3841. return nmwa.iButton;
  3842. }
  3843. iStart += 1 + ptb->iNumButtons;
  3844. iStart %= ptb->iNumButtons;
  3845. if ((ptb->Buttons[iStart].fsState & TBSTATE_ENABLED) &&
  3846. !ChrCmpI((WORD)TBButtonAccelerator(ptb, &ptb->Buttons[iStart]), (WORD)ch))
  3847. {
  3848. iRet = iStart;
  3849. break;
  3850. }
  3851. }
  3852. *pbDup = TBHasDupChar(ptb, ch);
  3853. return iRet;
  3854. }
  3855. BOOL TBOnChar(PTBSTATE ptb, UINT ch)
  3856. {
  3857. NMCHAR nm = {0};
  3858. BOOL bDupChar;
  3859. int iPos = TBItemFromAccelerator(ptb, ch, &bDupChar);
  3860. BOOL fHandled = FALSE;
  3861. // Send the notification. Parent may want to change the next button.
  3862. nm.ch = ch;
  3863. nm.dwItemPrev = (0 <= ptb->iHot) ? ptb->Buttons[ptb->iHot].idCommand : -1;
  3864. nm.dwItemNext = (0 <= iPos) ? ptb->Buttons[iPos].idCommand : -1;
  3865. if (CCSendNotify(&ptb->ci, NM_CHAR, (LPNMHDR)&nm))
  3866. return TRUE;
  3867. iPos = PositionFromID(ptb, nm.dwItemNext);
  3868. if (-1 != iPos)
  3869. {
  3870. DWORD dwFlags = HICF_ACCELERATOR;
  3871. if (ptb->iHot == iPos)
  3872. dwFlags |= HICF_RESELECT;
  3873. if (bDupChar)
  3874. dwFlags |= HICF_DUPACCEL;
  3875. TBSetHotItem(ptb, iPos, dwFlags);
  3876. if (bDupChar)
  3877. iPos = -1;
  3878. fHandled = TRUE;
  3879. } else {
  3880. // handle this here instead of VK_KEYDOWN
  3881. // because a typical thing to do is to pop down a menu
  3882. // which will beep when it gets the WM_CHAR resulting from
  3883. // the VK_KEYDOWN
  3884. switch (ch) {
  3885. case ' ':
  3886. case 13:
  3887. if (ptb->iHot != -1)
  3888. {
  3889. LPTBBUTTONDATA ptbButton = &ptb->Buttons[ptb->iHot];
  3890. if (TB_IsDropDown(ptbButton) &&
  3891. !TB_HasSplitDDArrow(ptb, ptbButton))
  3892. {
  3893. iPos = ptb->iHot;
  3894. fHandled = TRUE;
  3895. }
  3896. break;
  3897. }
  3898. }
  3899. }
  3900. if (-1 != iPos) {
  3901. LPTBBUTTONDATA ptbButton = &ptb->Buttons[iPos];
  3902. if (TB_IsDropDown(ptbButton))
  3903. TBToggleDropDown(ptb, iPos, FALSE);
  3904. }
  3905. //notify of navigation key usage
  3906. CCNotifyNavigationKeyUsage(&(ptb->ci), UISF_HIDEFOCUS | UISF_HIDEACCEL);
  3907. return fHandled;
  3908. }
  3909. BOOL TBOnMapAccelerator(PTBSTATE ptb, UINT ch, UINT * pidCmd)
  3910. {
  3911. int iPos;
  3912. BOOL bDupChar;
  3913. ASSERT(IS_VALID_WRITE_PTR(pidCmd, UINT));
  3914. iPos = TBItemFromAccelerator(ptb, ch, &bDupChar);
  3915. if (-1 != iPos)
  3916. {
  3917. *pidCmd = ptb->Buttons[iPos].idCommand;
  3918. return TRUE;
  3919. }
  3920. return FALSE;
  3921. }
  3922. BOOL TBOnKey(PTBSTATE ptb, int nVirtKey, UINT uFlags)
  3923. {
  3924. NMKEY nm;
  3925. // Send the notification
  3926. nm.nVKey = nVirtKey;
  3927. nm.uFlags = uFlags;
  3928. if (CCSendNotify(&ptb->ci, NM_KEYDOWN, &nm.hdr))
  3929. return TRUE;
  3930. // Swap the left and right arrow key if the control is mirrored.
  3931. nVirtKey = RTLSwapLeftRightArrows(&ptb->ci, nVirtKey);
  3932. if (ptb->iHot != -1 && TB_IsDropDown(&ptb->Buttons[ptb->iHot])) {
  3933. // if we're on a dropdown button and you hit the up/down arrow (left/rigth in vert mode)
  3934. // then drop the button down.
  3935. // escape undrops it if it's dropped
  3936. switch (nVirtKey) {
  3937. case VK_RIGHT:
  3938. case VK_LEFT:
  3939. if (!(ptb->ci.style & CCS_VERT))
  3940. break;
  3941. goto DropDown;
  3942. case VK_DOWN:
  3943. case VK_UP:
  3944. if ((ptb->ci.style & CCS_VERT) || (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL))
  3945. break;
  3946. goto DropDown;
  3947. case VK_ESCAPE:
  3948. if (ptb->iHot != ptb->iPressedDD)
  3949. break;
  3950. DropDown:
  3951. TBToggleDropDown(ptb, ptb->iHot, FALSE);
  3952. //notify of navigation key usage
  3953. CCNotifyNavigationKeyUsage(&(ptb->ci), UISF_HIDEFOCUS | UISF_HIDEACCEL);
  3954. return TRUE;
  3955. }
  3956. }
  3957. switch (nVirtKey) {
  3958. case VK_RIGHT:
  3959. case VK_DOWN:
  3960. TBCycleHotItem(ptb, ptb->iHot, 1, HICF_ARROWKEYS);
  3961. break;
  3962. case VK_LEFT:
  3963. case VK_UP:
  3964. TBCycleHotItem(ptb, ptb->iHot, -1, HICF_ARROWKEYS);
  3965. break;
  3966. case VK_SPACE:
  3967. case VK_RETURN:
  3968. if (ptb->iHot != -1)
  3969. {
  3970. FORWARD_WM_COMMAND(ptb->ci.hwndParent, ptb->Buttons[ptb->iHot].idCommand, ptb->ci.hwnd, BN_CLICKED, SendMessage);
  3971. }
  3972. break;
  3973. default:
  3974. return FALSE;
  3975. }
  3976. //notify of navigation key usage
  3977. CCNotifyNavigationKeyUsage(&(ptb->ci), UISF_HIDEFOCUS | UISF_HIDEACCEL);
  3978. return TRUE;
  3979. }
  3980. LRESULT TB_OnSetButtonInfoA(PTBSTATE ptb, int idBtn, LPTBBUTTONINFOA ptbbiA)
  3981. {
  3982. TBBUTTONINFO tbbi = *(LPTBBUTTONINFO)ptbbiA;
  3983. WCHAR szText[256];
  3984. if ((ptbbiA->dwMask & TBIF_TEXT) && ptbbiA->pszText)
  3985. {
  3986. tbbi.pszText = szText;
  3987. tbbi.cchText = ARRAYSIZE(szText);
  3988. MultiByteToWideChar(CP_ACP, 0, (LPCSTR) ptbbiA->pszText, -1,
  3989. szText, ARRAYSIZE(szText));
  3990. }
  3991. return TB_OnSetButtonInfo(ptb, idBtn, (LPTBBUTTONINFO)&tbbi);
  3992. }
  3993. LRESULT TB_OnGetButtonInfoA(PTBSTATE ptb, int idBtn, LPTBBUTTONINFOA ptbbiA)
  3994. {
  3995. LPTBBUTTONDATA ptbButton;
  3996. int iPos;
  3997. DWORD dwMask = ptbbiA->dwMask;
  3998. ptbbiA->dwMask &= ~TBIF_TEXT;
  3999. iPos = (int) TB_OnGetButtonInfo(ptb, idBtn, (LPTBBUTTONINFO)ptbbiA);
  4000. if (iPos != -1)
  4001. {
  4002. ptbButton = ptb->Buttons + iPos;
  4003. ptbbiA->dwMask = dwMask;
  4004. if (ptbbiA->dwMask & TBIF_TEXT)
  4005. {
  4006. if (TBISSTRINGPTR(ptbButton->iString))
  4007. {
  4008. WideCharToMultiByte (CP_ACP, 0, (LPCTSTR)ptbButton->iString,
  4009. -1, ptbbiA->pszText , ptbbiA->cchText, NULL, NULL);
  4010. }
  4011. else
  4012. {
  4013. ptbbiA->pszText[0] = 0;
  4014. }
  4015. }
  4016. }
  4017. return iPos;
  4018. }
  4019. void TBOnMouseMove(PTBSTATE ptb, HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  4020. {
  4021. if (ptb->fActive)
  4022. {
  4023. BOOL fSameButton;
  4024. BOOL fDragOut = FALSE;
  4025. int iPos;
  4026. // do drag notifies/drawing first
  4027. if (ptb->pCaptureButton != NULL)
  4028. {
  4029. if (hwnd != GetCapture())
  4030. {
  4031. //DebugMsg(DM_TRACE, TEXT("capture isn't us"));
  4032. SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_ENDDRAG);
  4033. // Revalidate after calling out
  4034. if (!IsWindow(hwnd)) return;
  4035. // if the button is still pressed, unpress it.
  4036. if (EVAL(ptb->pCaptureButton) &&
  4037. (ptb->pCaptureButton->fsState & TBSTATE_PRESSED))
  4038. SendMessage(hwnd, TB_PRESSBUTTON, ptb->pCaptureButton->idCommand, 0L);
  4039. ptb->pCaptureButton = NULL;
  4040. ptb->fRightDrag = FALSE; // just in case we were right dragging
  4041. }
  4042. else
  4043. {
  4044. //DebugMsg(DM_TRACE, TEXT("capture IS us, and state is enabled"));
  4045. iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4046. fSameButton = (iPos >= 0 && ptb->pCaptureButton == ptb->Buttons + iPos);
  4047. // notify on first drag out
  4048. if (!fSameButton && !ptb->fDragOutNotify)
  4049. {
  4050. ptb->fDragOutNotify = TRUE;
  4051. fDragOut = (BOOL)SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_DRAGOUT);
  4052. // Revalidate after calling out
  4053. if (!IsWindow(hwnd)) return;
  4054. }
  4055. // Check for ptb->pCaptureButton in case it was somehow nuked
  4056. // in TBN_DRAGOUT.
  4057. // This happens in the case when dragging an item out of start menu. When the
  4058. // notify TBN_DRAGOUT is received, they go into a modal drag drop loop. Before
  4059. // This loop finishes, the file is moved, causing a shell change notify to nuke
  4060. // the button, which invalidates pCatpure button. So I'm getting rid of the
  4061. // eval (lamadio) 4.14.98
  4062. if (ptb->pCaptureButton &&
  4063. (ptb->pCaptureButton->fsState & TBSTATE_ENABLED) &&
  4064. (fSameButton == !(ptb->pCaptureButton->fsState & TBSTATE_PRESSED)) &&
  4065. !ptb->fRightDrag)
  4066. {
  4067. //DebugMsg(DM_TRACE, TEXT("capture IS us, and Button is different"));
  4068. ptb->pCaptureButton->fsState ^= TBSTATE_PRESSED;
  4069. InvalidateButton(ptb, ptb->pCaptureButton, TRUE);
  4070. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
  4071. OBJID_CLIENT, (ptb->pCaptureButton - ptb->Buttons) + 1);
  4072. }
  4073. }
  4074. }
  4075. if (!fDragOut)
  4076. {
  4077. TBRelayToToolTips(ptb, wMsg, wParam, lParam);
  4078. // Support hot tracking?
  4079. if ((ptb->ci.style & TBSTYLE_FLAT) )
  4080. {
  4081. // Yes; set the hot item
  4082. iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4083. TBSetHotItem(ptb, iPos, HICF_MOUSE);
  4084. // Track mouse events now?
  4085. if (!ptb->fMouseTrack && !ptb->fAnchorHighlight)
  4086. {
  4087. // Yes
  4088. TRACKMOUSEEVENT tme;
  4089. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  4090. tme.dwFlags = TME_LEAVE;
  4091. tme.hwndTrack = hwnd;
  4092. ptb->fMouseTrack = TRUE;
  4093. TrackMouseEvent(&tme);
  4094. }
  4095. }
  4096. }
  4097. }
  4098. }
  4099. void TBHandleLButtonDown(PTBSTATE ptb, LPARAM lParam, int iPos)
  4100. {
  4101. LPTBBUTTONDATA ptbButton;
  4102. HWND hwnd = ptb->ci.hwnd;
  4103. if (iPos >= 0 && iPos < ptb->iNumButtons)
  4104. {
  4105. POINT pt;
  4106. RECT rcDropDown;
  4107. LPARAM_TO_POINT(lParam, pt);
  4108. // should this check for the size of the button struct?
  4109. ptbButton = ptb->Buttons + iPos;
  4110. if (TB_IsDropDown(ptbButton))
  4111. TB_GetItemDropDownRect(ptb, iPos, &rcDropDown);
  4112. if (TB_IsDropDown(ptbButton) &&
  4113. (!TB_HasSplitDDArrow(ptb, ptbButton) || PtInRect(&rcDropDown, pt))) {
  4114. // Was the dropdown handled?
  4115. if (!TBToggleDropDown(ptb, iPos, TRUE))
  4116. {
  4117. // No; consider it a drag-out
  4118. ptb->pCaptureButton = ptbButton;
  4119. SetCapture(hwnd);
  4120. ptb->fDragOutNotify = FALSE;
  4121. SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_BEGINDRAG);
  4122. GetMessagePosClient(ptb->ci.hwnd, &ptb->ptCapture);
  4123. }
  4124. } else {
  4125. ptb->pCaptureButton = ptbButton;
  4126. SetCapture(hwnd);
  4127. if (ptbButton->fsState & TBSTATE_ENABLED)
  4128. {
  4129. ptbButton->fsState |= TBSTATE_PRESSED;
  4130. InvalidateButton(ptb, ptbButton, TRUE);
  4131. UpdateWindow(hwnd); // immediate feedback
  4132. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd,
  4133. OBJID_CLIENT, iPos+1);
  4134. }
  4135. ptb->fDragOutNotify = FALSE;
  4136. // pCaptureButton may have changed
  4137. if (ptb->pCaptureButton)
  4138. SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_BEGINDRAG);
  4139. GetMessagePosClient(ptb->ci.hwnd, &ptb->ptCapture);
  4140. }
  4141. }
  4142. }
  4143. void TBOnLButtonDown(PTBSTATE ptb, HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  4144. {
  4145. int iPos;
  4146. NMCLICK nm = {0};
  4147. ptb->fRequeryCapture = FALSE;
  4148. TBRelayToToolTips(ptb, wMsg, wParam, lParam);
  4149. iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4150. if ((ptb->ci.style & CCS_ADJUSTABLE) &&
  4151. (((wParam & MK_SHIFT) && !(ptb->ci.style & TBSTYLE_ALTDRAG)) ||
  4152. ((GetKeyState(VK_MENU) & ~1) && (ptb->ci.style & TBSTYLE_ALTDRAG))))
  4153. {
  4154. MoveButton(ptb, iPos);
  4155. }
  4156. else {
  4157. TBHandleLButtonDown(ptb, lParam, iPos);
  4158. }
  4159. if ((iPos >= 0) && (iPos < ptb->iNumButtons))
  4160. {
  4161. nm.dwItemSpec = ptb->Buttons[iPos].idCommand;
  4162. nm.dwItemData = ptb->Buttons[iPos].dwData;
  4163. }
  4164. else
  4165. nm.dwItemSpec = (UINT_PTR) -1;
  4166. LPARAM_TO_POINT(lParam, nm.pt);
  4167. CCSendNotify(&ptb->ci, NM_LDOWN, (LPNMHDR )&nm);
  4168. }
  4169. void TBOnLButtonUp(PTBSTATE ptb, HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
  4170. {
  4171. int iPos = -1;
  4172. NMCLICK nm = { 0 };
  4173. TBRelayToToolTips(ptb, wMsg, wParam, lParam);
  4174. if (lParam != (LPARAM)-1)
  4175. iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4176. if (ptb->fRequeryCapture && iPos >= 0) {
  4177. // hack for broderbund (and potentially mand mfc apps.
  4178. // on button down, they delete the pressed button and insert another one right underneat that
  4179. // has pretty much the same characteristics.
  4180. // on win95, we allowed pCaptureButton to temporarily point to garbage.
  4181. // now we validate against it.
  4182. // we detect this case on delete now and if the creation size (uStructSize == old 0x14 size)
  4183. // we reget the capture button here
  4184. ptb->pCaptureButton = &ptb->Buttons[iPos];
  4185. }
  4186. if (ptb->pCaptureButton != NULL) {
  4187. int idCommand = ptb->pCaptureButton->idCommand;
  4188. if (!CCReleaseCapture(&ptb->ci)) return;
  4189. SendItemNotify(ptb, idCommand, TBN_ENDDRAG);
  4190. if (!IsWindow(hwnd)) return;
  4191. if (ptb->pCaptureButton && (ptb->pCaptureButton->fsState & TBSTATE_ENABLED) && iPos >=0
  4192. && (ptb->pCaptureButton == ptb->Buttons+iPos)) {
  4193. ptb->pCaptureButton->fsState &= ~TBSTATE_PRESSED;
  4194. if (ptb->pCaptureButton->fsStyle & BTNS_CHECK) {
  4195. if (ptb->pCaptureButton->fsStyle & BTNS_GROUP) {
  4196. // group buttons already checked can't be force
  4197. // up by the user.
  4198. if (ptb->pCaptureButton->fsState & TBSTATE_CHECKED) {
  4199. ptb->pCaptureButton = NULL;
  4200. return; // bail!
  4201. }
  4202. ptb->pCaptureButton->fsState |= TBSTATE_CHECKED;
  4203. MakeGroupConsistant(ptb, idCommand);
  4204. } else {
  4205. ptb->pCaptureButton->fsState ^= TBSTATE_CHECKED; // toggle
  4206. }
  4207. }
  4208. InvalidateButton(ptb, ptb->pCaptureButton, TRUE);
  4209. ptb->pCaptureButton = NULL;
  4210. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT,
  4211. iPos+1);
  4212. FORWARD_WM_COMMAND(ptb->ci.hwndParent, idCommand, hwnd, BN_CLICKED, SendMessage);
  4213. // do not dereference ptb... it might have been destroyed on the WM_COMMAND.
  4214. // if the window has been destroyed, bail out.
  4215. if (!IsWindow(hwnd))
  4216. return;
  4217. goto SendUpClick;
  4218. }
  4219. else {
  4220. ptb->pCaptureButton = NULL;
  4221. }
  4222. }
  4223. else
  4224. {
  4225. SendUpClick:
  4226. if ((iPos >= 0) && (iPos < ptb->iNumButtons)) {
  4227. nm.dwItemSpec = ptb->Buttons[iPos].idCommand;
  4228. nm.dwItemData = ptb->Buttons[iPos].dwData;
  4229. } else
  4230. nm.dwItemSpec = (UINT_PTR) -1;
  4231. LPARAM_TO_POINT(lParam, nm.pt);
  4232. CCSendNotify(&ptb->ci, NM_CLICK, (LPNMHDR )&nm);
  4233. }
  4234. }
  4235. BOOL CALLBACK GetUpdateRectEnumProc(HWND hwnd, LPARAM lParam)
  4236. {
  4237. PTBSTATE ptb = (PTBSTATE)lParam;
  4238. if (IsWindowVisible(hwnd))
  4239. {
  4240. RECT rcInvalid;
  4241. if (GetUpdateRect(hwnd, &rcInvalid, FALSE))
  4242. {
  4243. RECT rcNew;
  4244. MapWindowPoints(hwnd, ptb->ci.hwnd, (LPPOINT)&rcInvalid, 2);
  4245. UnionRect(&rcNew, &rcInvalid, &ptb->rcInvalid);
  4246. ptb->rcInvalid = rcNew;
  4247. }
  4248. }
  4249. return TRUE;
  4250. }
  4251. void TB_OnSize(PTBSTATE ptb, int nWidth, int nHeight)
  4252. {
  4253. if (ptb->dwStyleEx & TBSTYLE_EX_HIDECLIPPEDBUTTONS)
  4254. {
  4255. // figure out which buttons intersect the resized region
  4256. // and invalidate the rects for those buttons
  4257. //
  4258. // +---------------+------+
  4259. // | | <--- rcResizeH
  4260. // | | |
  4261. // +---------------+------+
  4262. // | ^ | |
  4263. // +---|-----------+------+
  4264. // rcResizeV
  4265. int i;
  4266. RECT rcResizeH, rcResizeV;
  4267. SetRect(&rcResizeH, min(ptb->rc.right, nWidth),
  4268. ptb->rc.top,
  4269. max(ptb->rc.right, nWidth),
  4270. min(ptb->rc.bottom, nHeight));
  4271. SetRect(&rcResizeV, ptb->rc.left,
  4272. min(ptb->rc.bottom, nHeight),
  4273. min(ptb->rc.right, nWidth),
  4274. max(ptb->rc.bottom, nHeight));
  4275. for (i = 0; i < ptb->iNumButtons; i++)
  4276. {
  4277. RECT rc, rcBtn;
  4278. TB_GetItemRect(ptb, i, &rcBtn);
  4279. if (IntersectRect(&rc, &rcBtn, &rcResizeH) ||
  4280. IntersectRect(&rc, &rcBtn, &rcResizeV))
  4281. {
  4282. InvalidateRect(ptb->ci.hwnd, &rcBtn, TRUE);
  4283. }
  4284. }
  4285. SetRect(&ptb->rc, 0, 0, nWidth, nHeight);
  4286. }
  4287. }
  4288. BOOL TB_TranslateAccelerator(HWND hwnd, LPMSG lpmsg)
  4289. {
  4290. if (!lpmsg)
  4291. return FALSE;
  4292. if (GetFocus() != hwnd)
  4293. return FALSE;
  4294. switch (lpmsg->message) {
  4295. case WM_KEYUP:
  4296. case WM_KEYDOWN:
  4297. switch (lpmsg->wParam) {
  4298. case VK_RIGHT:
  4299. case VK_LEFT:
  4300. case VK_UP:
  4301. case VK_DOWN:
  4302. case VK_ESCAPE:
  4303. case VK_SPACE:
  4304. case VK_RETURN:
  4305. TranslateMessage(lpmsg);
  4306. DispatchMessage(lpmsg);
  4307. return TRUE;
  4308. }
  4309. break;
  4310. case WM_CHAR:
  4311. switch (lpmsg->wParam) {
  4312. case VK_ESCAPE:
  4313. case VK_SPACE:
  4314. case VK_RETURN:
  4315. TranslateMessage(lpmsg);
  4316. DispatchMessage(lpmsg);
  4317. return TRUE;
  4318. }
  4319. break;
  4320. }
  4321. return FALSE;
  4322. }
  4323. void TBInitMetrics(PTBSTATE ptb)
  4324. {
  4325. // init our g_clr's
  4326. InitGlobalColors();
  4327. // get the size of a drop down arrow
  4328. ptb->dxDDArrowChar = GetSystemMetrics(SM_CYMENUCHECK);
  4329. }
  4330. LRESULT TBGenerateDragImage(PTBSTATE ptb, SHDRAGIMAGE* pshdi)
  4331. {
  4332. HBITMAP hbmpOld = NULL;
  4333. NMTBCUSTOMDRAW tbcd = { 0 };
  4334. HDC hdcDragImage;
  4335. // Do we have a hot item?
  4336. if (ptb->iHot == -1)
  4337. return 0; // No? Return...
  4338. hdcDragImage = CreateCompatibleDC(NULL);
  4339. if (!hdcDragImage)
  4340. return 0;
  4341. //
  4342. // Mirror the the DC, if the toolbar is mirrored.
  4343. //
  4344. if (ptb->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  4345. {
  4346. SET_DC_RTL_MIRRORED(hdcDragImage);
  4347. }
  4348. tbcd.nmcd.hdc = hdcDragImage;
  4349. ptb->ci.dwCustom = CICustomDrawNotify(&ptb->ci, CDDS_PREPAINT, (NMCUSTOMDRAW *)&tbcd);
  4350. pshdi->sizeDragImage.cx = TBWidthOfButton(ptb, &ptb->Buttons[ptb->iHot], hdcDragImage);
  4351. pshdi->sizeDragImage.cy = ptb->iButHeight;
  4352. pshdi->hbmpDragImage = CreateBitmap( pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy,
  4353. GetDeviceCaps(hdcDragImage, PLANES), GetDeviceCaps(hdcDragImage, BITSPIXEL),
  4354. NULL);
  4355. if (pshdi->hbmpDragImage)
  4356. {
  4357. DWORD dwStyle;
  4358. RECT rc = {0, 0, pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy};
  4359. hbmpOld = SelectObject(hdcDragImage, pshdi->hbmpDragImage);
  4360. pshdi->crColorKey = RGB(0xFF, 0x00, 0x55);
  4361. FillRectClr(hdcDragImage, &rc, pshdi->crColorKey);
  4362. // We want the button to be drawn transparent. This is a hack, because I
  4363. // don't want to rewrite the draw code. Fake a transparent draw.
  4364. dwStyle = ptb->ci.style;
  4365. ptb->ci.style |= TBSTYLE_TRANSPARENT;
  4366. ptb->fAntiAlias = FALSE;
  4367. DrawButton(hdcDragImage, 0, 0, ptb, &ptb->Buttons[ptb->iHot], TRUE);
  4368. ptb->fAntiAlias = TRUE;
  4369. ptb->ci.style = dwStyle;
  4370. TB_GetItemRect(ptb, ptb->iHot, &rc);
  4371. if (PtInRect(&rc, ptb->ptCapture))
  4372. {
  4373. if (ptb->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  4374. pshdi->ptOffset.x = rc.right - ptb->ptCapture.x;
  4375. else
  4376. pshdi->ptOffset.x = ptb->ptCapture.x - rc.left;
  4377. pshdi->ptOffset.y = ptb->ptCapture.y - rc.top;
  4378. }
  4379. SelectObject(hdcDragImage, hbmpOld);
  4380. DeleteDC(hdcDragImage);
  4381. // We're passing back the created HBMP.
  4382. return 1;
  4383. }
  4384. return 0;
  4385. }
  4386. LRESULT CALLBACK ToolbarWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4387. {
  4388. LPTBBUTTONDATA ptbButton;
  4389. int iPos;
  4390. LRESULT dw;
  4391. PTBSTATE ptb = (PTBSTATE)GetWindowPtr0(hwnd); // GetWindowPtr(hwnd, 0)
  4392. if (uMsg == WM_NCCREATE)
  4393. {
  4394. LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
  4395. CCCreateWindow();
  4396. InitDitherBrush();
  4397. // create the state data for this toolbar
  4398. ptb = (PTBSTATE)LocalAlloc(LPTR, sizeof(TBSTATE));
  4399. if (!ptb)
  4400. return 0; // WM_NCCREATE failure is 0
  4401. // note, zero init memory from above
  4402. CIInitialize(&ptb->ci, hwnd, lpcs);
  4403. ptb->xFirstButton = s_xFirstButton;
  4404. ptb->iHot = -1;
  4405. ptb->iPressedDD = -1;
  4406. ptb->iInsert = -1;
  4407. ptb->clrim = CLR_DEFAULT;
  4408. ptb->fAntiAlias = TRUE; // Anti Alias fonts by default.
  4409. // initialize system metric-dependent stuff
  4410. TBInitMetrics(ptb);
  4411. // horizontal/vertical space taken up by button chisel, sides,
  4412. // and a 1 pixel margin. used in GrowToolbar.
  4413. ptb->xPad = 7;
  4414. ptb->yPad = 6;
  4415. ptb->fShowPrefix = TRUE;
  4416. ptb->iListGap = LIST_GAP;
  4417. ptb->iDropDownGap = DROPDOWN_GAP;
  4418. ptb->clrsc.clrBtnHighlight = ptb->clrsc.clrBtnShadow = CLR_DEFAULT;
  4419. ASSERT(ptb->uStructSize == 0);
  4420. ASSERT(ptb->hfontIcon == NULL); // initialize to null.
  4421. ASSERT(ptb->iButMinWidth == 0);
  4422. ASSERT(ptb->iButMaxWidth == 0);
  4423. ptb->nTextRows = 1;
  4424. ptb->fActive = TRUE;
  4425. // IE 3 passes in TBSTYLE_FLAT, but they really
  4426. // wanted TBSTYLE_TRANSPARENT also.
  4427. //
  4428. if (ptb->ci.style & TBSTYLE_FLAT) {
  4429. ptb->ci.style |= TBSTYLE_TRANSPARENT;
  4430. }
  4431. #ifdef DEBUG
  4432. if (IsFlagSet(g_dwPrototype, PTF_FLATLOOK))
  4433. {
  4434. TraceMsg(TF_TOOLBAR, "Using flat look for toolbars.");
  4435. ptb->ci.style |= TBSTYLE_FLAT;
  4436. }
  4437. #endif
  4438. // Now Initialize the hfont we will use.
  4439. TBChangeFont(ptb, 0, NULL);
  4440. // grow the button size to the appropriate girth
  4441. if (!SetBitmapSize(ptb, DEFAULTBITMAPX, DEFAULTBITMAPX))
  4442. {
  4443. goto Failure;
  4444. }
  4445. SetWindowPtr(hwnd, 0, ptb);
  4446. if (!(ptb->ci.style & (CCS_TOP | CCS_NOMOVEY | CCS_BOTTOM)))
  4447. {
  4448. ptb->ci.style |= CCS_TOP;
  4449. SetWindowLong(hwnd, GWL_STYLE, ptb->ci.style);
  4450. }
  4451. return TRUE;
  4452. Failure:
  4453. if (ptb) {
  4454. ASSERT(!ptb->Buttons); // App hasn't had a change to AddButtons yet
  4455. LocalFree(ptb);
  4456. }
  4457. return FALSE;
  4458. }
  4459. if (!ptb)
  4460. goto DoDefault;
  4461. switch (uMsg) {
  4462. case WM_CREATE:
  4463. if (ptb->ci.style & TBSTYLE_REGISTERDROP)
  4464. {
  4465. ptb->hDragProxy = CreateDragProxy(ptb->ci.hwnd, ToolbarDragCallback, TRUE);
  4466. }
  4467. goto DoDefault;
  4468. case WM_DESTROY:
  4469. CCDestroyWindow();
  4470. TB_OnDestroy(ptb);
  4471. break;
  4472. case WM_KEYDOWN:
  4473. if (TBOnKey(ptb, (int) wParam, HIWORD(lParam)))
  4474. break;
  4475. goto DoDefault;
  4476. case WM_UPDATEUISTATE:
  4477. {
  4478. if (CCOnUIState(&(ptb->ci), WM_UPDATEUISTATE, wParam, lParam))
  4479. {
  4480. BOOL fSmooth = FALSE;
  4481. #ifdef CLEARTYPE // Don't use SPI_CLEARTYPE because it's defined because of APIThk, but not in NT.
  4482. SystemParametersInfo(SPI_GETCLEARTYPE, 0, &fSmooth, 0);
  4483. #endif
  4484. // We erase background only if we are removing underscores or focus rect,
  4485. // or if Font smooting is enabled
  4486. InvalidateRect(hwnd, NULL,
  4487. fSmooth || ((UIS_SET == LOWORD(wParam)) ? TRUE : FALSE));
  4488. }
  4489. goto DoDefault;
  4490. }
  4491. case WM_GETDLGCODE:
  4492. return (LRESULT) (DLGC_WANTARROWS | DLGC_WANTCHARS);
  4493. case WM_SYSCHAR:
  4494. case WM_CHAR:
  4495. if (!TBOnChar(ptb, (UINT) wParam) &&
  4496. (ptb->ci.iVersion >= 5))
  4497. {
  4498. // didn't handle it & client is >= v5
  4499. // forward to default handler
  4500. goto DoDefault;
  4501. }
  4502. break;
  4503. case WM_SETFOCUS:
  4504. if (ptb->iHot == -1) {
  4505. // set hot the first enabled button
  4506. TBCycleHotItem(ptb, -1, 1, HICF_OTHER);
  4507. }
  4508. break;
  4509. case WM_KILLFOCUS:
  4510. TBSetHotItem(ptb, -1, HICF_OTHER);
  4511. break;
  4512. case WM_SETFONT:
  4513. TBSetFont(ptb, (HFONT)wParam, (BOOL)lParam);
  4514. return TRUE;
  4515. case WM_NCCALCSIZE:
  4516. // let defwindowproc handle the standard borders etc...
  4517. dw = DefWindowProc(hwnd, uMsg, wParam, lParam ) ;
  4518. // add the extra edge at the top of the toolbar to seperate from the menu bar
  4519. if (!(ptb->ci.style & CCS_NODIVIDER))
  4520. {
  4521. ((NCCALCSIZE_PARAMS *)lParam)->rgrc[0].top += g_cyEdge;
  4522. }
  4523. return dw;
  4524. case WM_NCHITTEST:
  4525. return HTCLIENT;
  4526. case WM_NCACTIVATE:
  4527. // only make sense to do this stuff if we're top level
  4528. if ((BOOLIFY(ptb->fActive) != (BOOL)wParam && !GetParent(hwnd))) {
  4529. int iButton;
  4530. ptb->fActive = (BOOL) wParam;
  4531. for (iButton = 0; iButton < ptb->iNumButtons; iButton++) {
  4532. ptbButton = &ptb->Buttons[iButton];
  4533. InvalidateButton(ptb, ptbButton, FALSE);
  4534. }
  4535. }
  4536. // fall through...
  4537. case WM_NCPAINT:
  4538. // old-style toolbars are forced to be without dividers above
  4539. if (!(ptb->ci.style & CCS_NODIVIDER))
  4540. {
  4541. RECT rc;
  4542. HDC hdc = GetWindowDC(hwnd);
  4543. GetWindowRect(hwnd, &rc);
  4544. MapWindowRect(NULL, hwnd, &rc); // screen -> client
  4545. rc.bottom = -rc.top; // bottom of NC area
  4546. rc.top = rc.bottom - g_cyEdge;
  4547. CCDrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_TOP | BF_BOTTOM, &(ptb->clrsc));
  4548. ReleaseDC(hwnd, hdc);
  4549. }
  4550. goto DoDefault;
  4551. case WM_ENABLE:
  4552. if (wParam) {
  4553. ptb->ci.style &= ~WS_DISABLED;
  4554. } else {
  4555. ptb->ci.style |= WS_DISABLED;
  4556. }
  4557. InvalidateRect(hwnd, NULL, ptb->ci.style & TBSTYLE_TRANSPARENT);
  4558. goto DoDefault;
  4559. case WM_PRINTCLIENT:
  4560. case WM_PAINT:
  4561. if (ptb->fTTNeedsFlush)
  4562. FlushToolTipsMgrNow(ptb);
  4563. if (ptb->fRedrawOff)
  4564. {
  4565. if (!wParam)
  4566. {
  4567. HDC hdcPaint;
  4568. PAINTSTRUCT ps;
  4569. hdcPaint = BeginPaint(hwnd, &ps);
  4570. EndPaint(hwnd, &ps);
  4571. }
  4572. // we got a paint region, so invalidate
  4573. // when we get redraw back on...
  4574. ptb->fInvalidate = TRUE;
  4575. }
  4576. else
  4577. {
  4578. TBPaint(ptb, (HDC)wParam);
  4579. }
  4580. break;
  4581. case WM_SETREDRAW:
  4582. {
  4583. // HACKHACK: only respect WM_SETREDRAW message if tbstyle is flat
  4584. // HACKHACK: fixes appcompat bug #60120
  4585. if (ptb->ci.style & TBSTYLE_FLAT ||
  4586. ptb->dwStyleEx & TBSTYLE_EX_VERTICAL ||
  4587. (ptb->ci.iVersion >= 5))
  4588. {
  4589. BOOL fRedrawOld = !ptb->fRedrawOff;
  4590. if ( wParam && ptb->fRedrawOff )
  4591. {
  4592. if ( ptb->fInvalidate )
  4593. {
  4594. // If font smoothing is enabled, then we need to erase the background too.
  4595. BOOL fSmooth = FALSE;
  4596. #ifdef CLEARTYPE // Don't use SPI_CLEARTYPE because it's defined because of APIThk, but not in NT.
  4597. SystemParametersInfo(SPI_GETCLEARTYPE, 0, &fSmooth, 0);
  4598. #endif
  4599. // invalidate before turning back on ...
  4600. RedrawWindow( hwnd, NULL, NULL, (fSmooth? RDW_ERASE: 0) | RDW_INVALIDATE );
  4601. ptb->fInvalidate = FALSE;
  4602. }
  4603. ptb->fRedrawOff = FALSE;
  4604. if ( ptb->fRecalc )
  4605. {
  4606. // recalc & autosize after turning back on
  4607. TBRecalc(ptb);
  4608. TBAutoSize(ptb);
  4609. ptb->fRecalc = FALSE;
  4610. }
  4611. }
  4612. else
  4613. {
  4614. ptb->fRedrawOff = !wParam;
  4615. }
  4616. if (ptb->ci.iVersion >= 5)
  4617. return fRedrawOld;
  4618. }
  4619. else
  4620. {
  4621. goto DoDefault;
  4622. }
  4623. }
  4624. break;
  4625. case WM_ERASEBKGND:
  4626. TB_OnEraseBkgnd(ptb, (HDC) wParam);
  4627. return(TRUE);
  4628. case WM_SYSCOLORCHANGE:
  4629. TB_OnSysColorChange(ptb);
  4630. if (ptb->hwndToolTips)
  4631. SendMessage(ptb->hwndToolTips, uMsg, wParam, lParam);
  4632. break;
  4633. case TB_GETROWS:
  4634. return CountRows(ptb);
  4635. break;
  4636. case TB_GETPADDING:
  4637. lParam = MAKELONG(-1, -1);
  4638. // fall through
  4639. case TB_SETPADDING:
  4640. {
  4641. LRESULT lres = MAKELONG(ptb->xPad, ptb->yPad);
  4642. int xPad = GET_X_LPARAM(lParam);
  4643. int yPad = GET_Y_LPARAM(lParam);
  4644. if (xPad != -1)
  4645. ptb->xPad = xPad;
  4646. if (yPad != -1)
  4647. ptb->yPad = yPad;
  4648. return lres;
  4649. }
  4650. case TB_SETROWS:
  4651. {
  4652. RECT rc;
  4653. if (BoxIt(ptb, LOWORD(wParam), HIWORD(wParam), &rc))
  4654. {
  4655. TBInvalidateItemRects(ptb);
  4656. SetWindowPos(hwnd, NULL, 0, 0, rc.right, rc.bottom,
  4657. SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
  4658. InvalidateRect(hwnd, NULL, TRUE);
  4659. }
  4660. if (lParam)
  4661. *((RECT *)lParam) = rc;
  4662. }
  4663. break;
  4664. case WM_MOVE:
  4665. // JJK TODO: This needs to be double buffered to get rid of the flicker
  4666. if (ptb->ci.style & TBSTYLE_TRANSPARENT)
  4667. InvalidateRect(hwnd, NULL, TRUE);
  4668. goto DoDefault;
  4669. case WM_SIZE:
  4670. TB_OnSize(ptb, LOWORD(lParam), HIWORD(lParam));
  4671. // fall through
  4672. case TB_AUTOSIZE:
  4673. TBAutoSize(ptb);
  4674. break;
  4675. case WM_COMMAND:
  4676. case WM_DRAWITEM:
  4677. case WM_MEASUREITEM:
  4678. case WM_VKEYTOITEM:
  4679. case WM_CHARTOITEM:
  4680. SendMessage(ptb->ci.hwndParent, uMsg, wParam, lParam);
  4681. break;
  4682. case WM_RBUTTONDBLCLK:
  4683. if (!CCSendNotify(&ptb->ci, NM_RDBLCLK, NULL))
  4684. goto DoDefault;
  4685. break;
  4686. case WM_RBUTTONUP:
  4687. {
  4688. NMCLICK nm = {0};
  4689. int iIndex;
  4690. if (ptb->pCaptureButton != NULL)
  4691. {
  4692. if (!CCReleaseCapture(&ptb->ci)) break;
  4693. ptb->pCaptureButton = NULL;
  4694. ptb->fRightDrag = FALSE;
  4695. }
  4696. iIndex = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4697. if ((iIndex >= 0) && (iIndex < ptb->iNumButtons)) {
  4698. nm.dwItemSpec = ptb->Buttons[iIndex].idCommand;
  4699. nm.dwItemData = ptb->Buttons[iIndex].dwData;
  4700. } else
  4701. nm.dwItemSpec = (UINT_PTR) -1;
  4702. LPARAM_TO_POINT(lParam, nm.pt);
  4703. if (!CCSendNotify(&ptb->ci, NM_RCLICK, (LPNMHDR )&nm))
  4704. goto DoDefault;
  4705. }
  4706. break;
  4707. case WM_LBUTTONDBLCLK:
  4708. iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4709. if (iPos < 0 && (ptb->ci.style & CCS_ADJUSTABLE))
  4710. {
  4711. iPos = -1 - iPos;
  4712. CustomizeTB(ptb, iPos);
  4713. } else {
  4714. TBHandleLButtonDown(ptb, lParam, iPos);
  4715. }
  4716. break;
  4717. case WM_LBUTTONDOWN:
  4718. TBOnLButtonDown(ptb, hwnd, uMsg, wParam, lParam);
  4719. break;
  4720. case WM_CAPTURECHANGED:
  4721. // do this only for newer apps because some apps
  4722. // do things like delete a button when you
  4723. // mouse down and add it back in immediately.
  4724. // also do it on a post because we call ReleaseCapture
  4725. // internally and only want to catch this on external release
  4726. if (ptb->ci.iVersion >= 5)
  4727. PostMessage(hwnd, TBP_ONRELEASECAPTURE, 0, 0);
  4728. //
  4729. // QFE fix for Autodesk. We used to hold capture
  4730. // even though the app wanted it back. Oops.
  4731. //
  4732. else if (ptb->fRightDrag && ptb->pCaptureButton) {
  4733. CCReleaseCapture(&ptb->ci);
  4734. ptb->fRightDrag = FALSE;
  4735. }
  4736. break;
  4737. case TBP_ONRELEASECAPTURE:
  4738. if (ptb->pCaptureButton) {
  4739. // abort current capture
  4740. // simulate a lost capture mouse move. this will restore state
  4741. TBOnMouseMove(ptb, hwnd, WM_MOUSEMOVE, 0, (LPARAM)-1);
  4742. ptb->pCaptureButton = NULL;
  4743. }
  4744. break;
  4745. case WM_RBUTTONDOWN:
  4746. if (ptb->pCaptureButton) {
  4747. // abort current capture
  4748. if (hwnd == GetCapture()) {
  4749. // we were left clicking. abort that now
  4750. if (!CCReleaseCapture(&ptb->ci)) break;
  4751. // simulate a lost capture mouse move. this will restore state
  4752. TBOnMouseMove(ptb, hwnd, WM_MOUSEMOVE, 0, (LPARAM)-1);
  4753. }
  4754. }
  4755. iPos = TBHitTest(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4756. // we need to check VK_RBUTTON because some apps subclass us to pick off rbuttondown to do their menu
  4757. // (instead of up, or the notify, or wm_contextmenu)
  4758. // then after it's done and the button is up, they then send us a button down
  4759. if ((iPos >= 0) && (iPos < ptb->iNumButtons) && (GetAsyncKeyState(VK_RBUTTON) < 0))
  4760. {
  4761. ptb->pCaptureButton = ptb->Buttons + iPos;
  4762. ptb->fRightDrag = TRUE;
  4763. SetCapture(hwnd);
  4764. GetMessagePosClient(ptb->ci.hwnd, &ptb->ptCapture);
  4765. SendItemNotify(ptb, ptb->pCaptureButton->idCommand, TBN_BEGINDRAG);
  4766. if (!IsWindow(hwnd)) break;
  4767. ptb->fDragOutNotify = FALSE;
  4768. }
  4769. break;
  4770. case WM_MOUSELEAVE:
  4771. {
  4772. TRACKMOUSEEVENT tme;
  4773. // We only track mouse events on the flat style (for
  4774. // hot tracking)
  4775. ASSERT(ptb->ci.style & TBSTYLE_FLAT);
  4776. // Cancel the mouse event tracking
  4777. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  4778. tme.dwFlags = TME_CANCEL | TME_LEAVE;
  4779. tme.hwndTrack = hwnd;
  4780. TrackMouseEvent(&tme);
  4781. ptb->fMouseTrack = FALSE;
  4782. TBSetHotItem(ptb, -1, HICF_MOUSE);
  4783. }
  4784. break;
  4785. case WM_MOUSEMOVE:
  4786. TBOnMouseMove(ptb, hwnd, uMsg, wParam, lParam);
  4787. break;
  4788. case WM_LBUTTONUP:
  4789. TBOnLButtonUp(ptb, hwnd, uMsg, wParam, lParam);
  4790. break;
  4791. case WM_WININICHANGE:
  4792. InitGlobalMetrics(wParam);
  4793. if (ptb->fFontCreated)
  4794. TBChangeFont(ptb, wParam, NULL);
  4795. if (ptb->hwndToolTips)
  4796. SendMessage(ptb->hwndToolTips, uMsg, wParam, lParam);
  4797. // recalc & redraw
  4798. TBInitMetrics(ptb);
  4799. TBRecalc(ptb);
  4800. InvalidateRect(hwnd, NULL, TRUE);
  4801. break;
  4802. case WM_NOTIFYFORMAT:
  4803. return CIHandleNotifyFormat(&ptb->ci, lParam);
  4804. break;
  4805. case WM_NOTIFY:
  4806. #define lpNmhdr ((LPNMHDR)(lParam))
  4807. //The following statement traps all pager control notification messages.
  4808. if((lpNmhdr->code <= PGN_FIRST) && (lpNmhdr->code >= PGN_LAST)) {
  4809. return TB_OnPagerControlNotify(ptb, lpNmhdr);
  4810. }
  4811. {
  4812. LRESULT lres = 0;
  4813. if (lpNmhdr->code == TTN_NEEDTEXT) {
  4814. int i = PositionFromID(ptb, lpNmhdr->idFrom);
  4815. BOOL fEllipsied = FALSE;
  4816. LRESULT lres;
  4817. LPTOOLTIPTEXT lpnmTT = ((LPTOOLTIPTEXT) lParam);
  4818. if (i != -1) {
  4819. // if infotip not supported, try for TTN_NEEDTEXT in client.
  4820. if (!TBGetInfoTip(ptb, lpnmTT, &ptb->Buttons[i]))
  4821. lres = SendNotifyEx(ptb->ci.hwndParent, (HWND) -1,
  4822. lpNmhdr->code, lpNmhdr, ptb->ci.bUnicode);
  4823. #define IsTextPtr(lpszText) (((lpszText) != LPSTR_TEXTCALLBACK) && (!IS_INTRESOURCE(lpszText)))
  4824. fEllipsied = (BOOL)(ptb->Buttons[i].fsState & TBSTATE_ELLIPSES);
  4825. // if we don't get a string from TTN_NEEDTEXT try to use the title text.
  4826. if ((lpNmhdr->code == TTN_NEEDTEXT) &&
  4827. (BTN_NO_SHOW_TEXT(ptb, &ptb->Buttons[i]) || fEllipsied) &&
  4828. lpnmTT->lpszText && IsTextPtr(lpnmTT->lpszText) &&
  4829. !lpnmTT->lpszText[0])
  4830. {
  4831. LPCTSTR psz = TB_StrForButton(ptb, &ptb->Buttons[i]);
  4832. if (psz)
  4833. lpnmTT->lpszText = (LPTSTR)psz;
  4834. }
  4835. }
  4836. } else {
  4837. //
  4838. // We are just going to pass this on to the
  4839. // real parent. Note that -1 is used as
  4840. // the hwndFrom. This prevents SendNotifyEx
  4841. // from updating the NMHDR structure.
  4842. //
  4843. lres = SendNotifyEx(ptb->ci.hwndParent, (HWND) -1,
  4844. lpNmhdr->code, lpNmhdr, ptb->ci.bUnicode);
  4845. }
  4846. return(lres);
  4847. }
  4848. case WM_STYLECHANGING:
  4849. if (wParam == GWL_STYLE)
  4850. {
  4851. LPSTYLESTRUCT lpStyle = (LPSTYLESTRUCT) lParam;
  4852. // is MFC dorking with just our visibility bit?
  4853. if ((lpStyle->styleOld ^ lpStyle->styleNew) == WS_VISIBLE)
  4854. {
  4855. if (lpStyle->styleNew & WS_VISIBLE)
  4856. {
  4857. BOOL fSmooth = FALSE;
  4858. #ifdef CLEARTYPE // Don't use SPI_CLEARTYPE because it's defined because of APIThk, but not in NT.
  4859. SystemParametersInfo(SPI_GETCLEARTYPE, 0, &fSmooth, 0);
  4860. #endif
  4861. // MFC trying to make us visible,
  4862. // convert it to WM_SETREDRAW instead.
  4863. DefWindowProc(hwnd, WM_SETREDRAW, TRUE, 0);
  4864. // Reinvalidate everything we lost when we
  4865. // did the WM_SETREDRAW stuff.
  4866. RedrawWindow(hwnd, &ptb->rcInvalid, NULL, (fSmooth? RDW_ERASE: 0) | RDW_INVALIDATE | RDW_ALLCHILDREN);
  4867. ZeroMemory(&ptb->rcInvalid, SIZEOF(ptb->rcInvalid));
  4868. }
  4869. else
  4870. {
  4871. // Save the invalid rectangle in ptb->rcInvalid since
  4872. // WM_SETREDRAW will blow it away.
  4873. ZeroMemory(&ptb->rcInvalid, SIZEOF(ptb->rcInvalid));
  4874. GetUpdateRect(ptb->ci.hwnd, &ptb->rcInvalid, FALSE);
  4875. EnumChildWindows(ptb->ci.hwnd, GetUpdateRectEnumProc, (LPARAM)ptb);
  4876. // MFC trying to make us invisible,
  4877. // convert it to WM_SETREDRAW instead.
  4878. DefWindowProc(hwnd, WM_SETREDRAW, FALSE, 0);
  4879. }
  4880. }
  4881. }
  4882. break;
  4883. case WM_STYLECHANGED:
  4884. if (wParam == GWL_STYLE)
  4885. {
  4886. TBSetStyle(ptb, ((LPSTYLESTRUCT)lParam)->styleNew);
  4887. }
  4888. else if (wParam == GWL_EXSTYLE)
  4889. {
  4890. //
  4891. // If the RTL_MIRROR extended style bit had changed, let's
  4892. // repaint the control window
  4893. //
  4894. if ((ptb->ci.dwExStyle&RTL_MIRRORED_WINDOW) !=
  4895. (((LPSTYLESTRUCT)lParam)->styleNew&RTL_MIRRORED_WINDOW))
  4896. TBAutoSize(ptb);
  4897. //
  4898. // Save the new ex-style bits
  4899. //
  4900. ptb->ci.dwExStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
  4901. }
  4902. return 0;
  4903. case TB_GETIDEALSIZE:
  4904. {
  4905. NMPGCALCSIZE nm;
  4906. LPSIZE psize = (LPSIZE) lParam;
  4907. ASSERT(psize); // This should never be NULL
  4908. nm.dwFlag = wParam ? PGF_CALCHEIGHT : PGF_CALCWIDTH;
  4909. nm.iWidth = psize->cx;
  4910. nm.iHeight = psize->cy;
  4911. TB_OnCalcSize(ptb, (LPNMHDR)&nm);
  4912. // Since both values may have changed, reset the out-param.
  4913. psize->cy = nm.iHeight;
  4914. psize->cx = nm.iWidth;
  4915. return 1;
  4916. }
  4917. case TB_SETSTYLE:
  4918. TBSetStyle(ptb, (DWORD) lParam);
  4919. break;
  4920. case TB_GETSTYLE:
  4921. return (ptb->ci.style);
  4922. case TB_GETBUTTONSIZE:
  4923. return (MAKELONG(ptb->iButWidth,ptb->iButHeight));
  4924. case TB_SETBUTTONWIDTH:
  4925. if (ptb->iButMinWidth != LOWORD(lParam) ||
  4926. ptb->iButMaxWidth != HIWORD(lParam)) {
  4927. ptb->iButMinWidth = LOWORD(lParam);
  4928. ptb->iButMaxWidth = HIWORD(lParam);
  4929. ptb->iButWidth = 0;
  4930. TBRecalc(ptb);
  4931. InvalidateRect(hwnd, NULL, TRUE);
  4932. }
  4933. return TRUE;
  4934. case TB_TRANSLATEACCELERATOR:
  4935. return TB_TranslateAccelerator(hwnd, (LPMSG)lParam);
  4936. case TB_SETSTATE:
  4937. iPos = PositionFromID(ptb, wParam);
  4938. if (iPos < 0)
  4939. return FALSE;
  4940. ptbButton = ptb->Buttons + iPos;
  4941. TB_OnSetState(ptb, ptbButton, (BYTE)(LOWORD(lParam)), iPos);
  4942. TBInvalidateItemRects(ptb);
  4943. return TRUE;
  4944. // set the cmd ID of a button based on its position
  4945. case TB_SETCMDID:
  4946. if (wParam >= (UINT)ptb->iNumButtons)
  4947. return FALSE;
  4948. TB_OnSetCmdID(ptb, &ptb->Buttons[wParam], (UINT)lParam);
  4949. return TRUE;
  4950. case TB_GETSTATE:
  4951. iPos = PositionFromID(ptb, wParam);
  4952. if (iPos < 0)
  4953. return -1L;
  4954. return ptb->Buttons[iPos].fsState;
  4955. case TB_MAPACCELERATORA:
  4956. {
  4957. char szAcl[2];
  4958. WCHAR wszAcl[2];
  4959. szAcl[0] = (BYTE)wParam;
  4960. szAcl[1] = '\0';
  4961. MultiByteToWideChar(CP_ACP, 0, (LPCSTR)szAcl, ARRAYSIZE(szAcl), wszAcl, ARRAYSIZE(wszAcl));
  4962. // no need to check return we just take junk if MbtoWc has failed
  4963. wParam = (WPARAM)wszAcl[0];
  4964. }
  4965. // fall through...
  4966. case TB_MAPACCELERATOR:
  4967. return TBOnMapAccelerator(ptb, (UINT)wParam, (UINT *)lParam);
  4968. case TB_ENABLEBUTTON:
  4969. case TB_CHECKBUTTON:
  4970. case TB_PRESSBUTTON:
  4971. case TB_HIDEBUTTON:
  4972. case TB_INDETERMINATE:
  4973. case TB_MARKBUTTON:
  4974. {
  4975. BYTE fsState;
  4976. iPos = PositionFromID(ptb, wParam);
  4977. if (iPos < 0)
  4978. return FALSE;
  4979. ptbButton = &ptb->Buttons[iPos];
  4980. fsState = ptbButton->fsState;
  4981. if (LOWORD(lParam))
  4982. ptbButton->fsState |= wStateMasks[uMsg - TB_ENABLEBUTTON];
  4983. else
  4984. ptbButton->fsState &= ~wStateMasks[uMsg - TB_ENABLEBUTTON];
  4985. // did this actually change the state?
  4986. if (fsState != ptbButton->fsState) {
  4987. // is this button a member of a group?
  4988. if ((uMsg == TB_CHECKBUTTON) && (ptbButton->fsStyle & BTNS_GROUP))
  4989. MakeGroupConsistant(ptb, (int)wParam);
  4990. if (uMsg == TB_HIDEBUTTON) {
  4991. InvalidateRect(hwnd, NULL, TRUE);
  4992. TBInvalidateItemRects(ptb);
  4993. } else
  4994. InvalidateButton(ptb, ptbButton, TRUE);
  4995. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, iPos+1);
  4996. }
  4997. return(TRUE);
  4998. }
  4999. case TB_ISBUTTONENABLED:
  5000. case TB_ISBUTTONCHECKED:
  5001. case TB_ISBUTTONPRESSED:
  5002. case TB_ISBUTTONHIDDEN:
  5003. case TB_ISBUTTONINDETERMINATE:
  5004. case TB_ISBUTTONHIGHLIGHTED:
  5005. iPos = PositionFromID(ptb, wParam);
  5006. if (iPos < 0)
  5007. return(-1L);
  5008. return (LRESULT)ptb->Buttons[iPos].fsState & wStateMasks[uMsg - TB_ISBUTTONENABLED];
  5009. case TB_ADDBITMAP:
  5010. case TB_ADDBITMAP32: // only for compatibility with mail
  5011. {
  5012. LPTBADDBITMAP pab = (LPTBADDBITMAP)lParam;
  5013. return AddBitmap(ptb, (int) wParam, pab->hInst, pab->nID);
  5014. }
  5015. case TB_REPLACEBITMAP:
  5016. return ReplaceBitmap(ptb, (LPTBREPLACEBITMAP)lParam);
  5017. case TB_ADDSTRINGA:
  5018. {
  5019. LPWSTR lpStrings;
  5020. UINT uiCount;
  5021. LPSTR lpAnsiString = (LPSTR) lParam;
  5022. int iResult;
  5023. BOOL bAllocatedMem = FALSE;
  5024. if (!wParam && !IS_INTRESOURCE(lpAnsiString)) {
  5025. //
  5026. // We have to figure out how many characters
  5027. // are in this string.
  5028. //
  5029. uiCount = 0;
  5030. while (TRUE) {
  5031. uiCount++;
  5032. if ((*lpAnsiString == 0) && (*(lpAnsiString+1) == 0)) {
  5033. uiCount++; // needed for double null
  5034. break;
  5035. }
  5036. lpAnsiString++;
  5037. }
  5038. lpStrings = LocalAlloc(LPTR, uiCount * sizeof(TCHAR));
  5039. if (!lpStrings)
  5040. return -1;
  5041. bAllocatedMem = TRUE;
  5042. MultiByteToWideChar(CP_ACP, 0, (LPCSTR) lParam, uiCount, lpStrings, uiCount);
  5043. }
  5044. else
  5045. {
  5046. lpStrings = (LPWSTR)lParam;
  5047. }
  5048. iResult = TBAddStrings(ptb, wParam, (LPARAM)lpStrings);
  5049. if (bAllocatedMem)
  5050. LocalFree(lpStrings);
  5051. return iResult;
  5052. }
  5053. case TB_ADDSTRING:
  5054. return TBAddStrings(ptb, wParam, lParam);
  5055. case TB_GETSTRING:
  5056. return TBGetString(ptb, HIWORD(wParam), LOWORD(wParam), (LPTSTR)lParam);
  5057. case TB_GETSTRINGA:
  5058. return TBGetStringA(ptb, HIWORD(wParam), LOWORD(wParam), (LPSTR)lParam);
  5059. case TB_ADDBUTTONSA:
  5060. return TBInsertButtons(ptb, (UINT)-1, (UINT) wParam, (LPTBBUTTON)lParam, FALSE);
  5061. case TB_INSERTBUTTONA:
  5062. return TBInsertButtons(ptb, (UINT) wParam, 1, (LPTBBUTTON)lParam, FALSE);
  5063. case TB_ADDBUTTONS:
  5064. return TBInsertButtons(ptb, (UINT)-1, (UINT) wParam, (LPTBBUTTON)lParam, TRUE);
  5065. case TB_INSERTBUTTON:
  5066. return TBInsertButtons(ptb, (UINT) wParam, 1, (LPTBBUTTON)lParam, TRUE);
  5067. case TB_DELETEBUTTON:
  5068. return DeleteButton(ptb, (UINT) wParam);
  5069. case TB_GETBUTTON:
  5070. if (wParam >= (UINT)ptb->iNumButtons)
  5071. return(FALSE);
  5072. TBOutputStruct(ptb, ptb->Buttons + wParam, (LPTBBUTTON)lParam);
  5073. return TRUE;
  5074. case TB_SETANCHORHIGHLIGHT:
  5075. BLOCK
  5076. {
  5077. BOOL bAnchor = BOOLIFY(ptb->fAnchorHighlight);
  5078. ptb->fAnchorHighlight = BOOLFROMPTR(wParam);
  5079. return bAnchor;
  5080. }
  5081. break;
  5082. case TB_GETANCHORHIGHLIGHT:
  5083. return BOOLIFY(ptb->fAnchorHighlight);
  5084. case TB_HASACCELERATOR:
  5085. ASSERT(IS_VALID_WRITE_PTR(lParam, int*));
  5086. *((int*)lParam) = TBHasAccelerator(ptb, (UINT)wParam);
  5087. break;
  5088. case TB_SETHOTITEM:
  5089. lParam = HICF_OTHER;
  5090. // Fall through
  5091. case TB_SETHOTITEM2:
  5092. BLOCK
  5093. {
  5094. int iPos = ptb->iHot;
  5095. TBSetHotItem(ptb, (int)wParam, (DWORD)lParam);
  5096. return iPos;
  5097. }
  5098. break;
  5099. case TB_GETHOTITEM:
  5100. return ptb->iHot;
  5101. case TB_SETINSERTMARK:
  5102. TBSetInsertMark(ptb, (LPTBINSERTMARK)lParam);
  5103. break;
  5104. case TB_GETINSERTMARK:
  5105. {
  5106. LPTBINSERTMARK ptbim = (LPTBINSERTMARK)lParam;
  5107. ptbim->iButton = ptb->iInsert;
  5108. ptbim->dwFlags = ptb->fInsertAfter ? TBIMHT_AFTER : 0;
  5109. return TRUE;
  5110. }
  5111. case TB_SETINSERTMARKCOLOR:
  5112. {
  5113. LRESULT lres = (LRESULT)TB_GetInsertMarkColor(ptb);
  5114. ptb->clrim = (COLORREF) lParam;
  5115. return lres;
  5116. }
  5117. case TB_GETINSERTMARKCOLOR:
  5118. return TB_GetInsertMarkColor(ptb);
  5119. case TB_INSERTMARKHITTEST:
  5120. return (LRESULT)TBInsertMarkHitTest(ptb, ((LPPOINT)wParam)->x, ((LPPOINT)wParam)->y, (LPTBINSERTMARK)lParam);
  5121. case TB_MOVEBUTTON:
  5122. return (LRESULT)TBMoveButton(ptb, (UINT)wParam, (UINT)lParam);
  5123. case TB_GETMAXSIZE:
  5124. return (LRESULT)TBGetMaxSize(ptb, (LPSIZE) lParam );
  5125. case TB_BUTTONCOUNT:
  5126. return ptb->iNumButtons;
  5127. case TB_COMMANDTOINDEX:
  5128. return PositionFromID(ptb, wParam);
  5129. case TB_SAVERESTOREA:
  5130. {
  5131. LPWSTR lpSubKeyW, lpValueNameW;
  5132. TBSAVEPARAMSA * lpSaveA = (TBSAVEPARAMSA *) lParam;
  5133. BOOL bResult;
  5134. lpSubKeyW = ProduceWFromA (CP_ACP, lpSaveA->pszSubKey);
  5135. lpValueNameW = ProduceWFromA (CP_ACP, lpSaveA->pszValueName);
  5136. bResult = SaveRestoreFromReg(ptb, (BOOL) wParam, lpSaveA->hkr, lpSubKeyW, lpValueNameW);
  5137. FreeProducedString(lpSubKeyW);
  5138. FreeProducedString(lpValueNameW);
  5139. return bResult;
  5140. }
  5141. case TB_SAVERESTORE:
  5142. {
  5143. TBSAVEPARAMS* psr = (TBSAVEPARAMS *)lParam;
  5144. return SaveRestoreFromReg(ptb, (BOOL) wParam, psr->hkr, psr->pszSubKey, psr->pszValueName);
  5145. }
  5146. case TB_CUSTOMIZE:
  5147. CustomizeTB(ptb, ptb->iNumButtons);
  5148. break;
  5149. case TB_GETRECT:
  5150. // PositionFromID() accepts NULL ptbs!
  5151. wParam = PositionFromID(ptb, wParam);
  5152. // fall through
  5153. case TB_GETITEMRECT:
  5154. if (!lParam)
  5155. break;
  5156. return TB_GetItemRect(ptb, (UINT) wParam, (LPRECT)lParam);
  5157. case TB_BUTTONSTRUCTSIZE:
  5158. TBOnButtonStructSize(ptb, (UINT) wParam);
  5159. break;
  5160. case TB_SETBUTTONSIZE:
  5161. return GrowToolbar(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
  5162. case TB_SETBITMAPSIZE:
  5163. return SetBitmapSize(ptb, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  5164. case TB_SETIMAGELIST:
  5165. {
  5166. HIMAGELIST himl = (HIMAGELIST)lParam;
  5167. HIMAGELIST himlOld = TBSetImageList(ptb, HIML_NORMAL, (int) wParam, himl);
  5168. ptb->fHimlNative = TRUE;
  5169. if (!ptb->uStructSize) {
  5170. // let people get away without calling TB_BUTTONSTRUCTSIZE... no other control requires this
  5171. ptb->uStructSize = 20;
  5172. ASSERT(ptb->uStructSize == SIZEOF(TBBUTTON));
  5173. }
  5174. // The bitmap size is based on the primary image list
  5175. if (wParam == 0)
  5176. {
  5177. int cx = 0, cy = 0;
  5178. if (himl) {
  5179. // Update the bitmap size based on this image list
  5180. ImageList_GetIconSize(himl, &cx, &cy);
  5181. }
  5182. SetBitmapSize(ptb, cx, cy);
  5183. }
  5184. return (LRESULT)himlOld;
  5185. }
  5186. case TB_GETIMAGELIST:
  5187. return (LRESULT)TBGetImageList(ptb, HIML_NORMAL, (int) wParam);
  5188. case TB_GETIMAGELISTCOUNT:
  5189. return ptb->cPimgs;
  5190. case TB_SETHOTIMAGELIST:
  5191. return (LRESULT)TBSetImageList(ptb, HIML_HOT, (int) wParam, (HIMAGELIST)lParam);
  5192. case TB_GETHOTIMAGELIST:
  5193. return (LRESULT)TBGetImageList(ptb, HIML_HOT, (int) wParam);
  5194. case TB_GETDISABLEDIMAGELIST:
  5195. return (LRESULT)TBGetImageList(ptb, HIML_DISABLED, (int) wParam);
  5196. case TB_SETDISABLEDIMAGELIST:
  5197. return (LRESULT)TBSetImageList(ptb, HIML_DISABLED, (int) wParam, (HIMAGELIST)lParam);
  5198. case TB_GETOBJECT:
  5199. if (IsEqualIID((IID *)wParam, &IID_IDropTarget))
  5200. {
  5201. // if we have not already registered create an unregistered target now
  5202. if (ptb->hDragProxy == NULL)
  5203. ptb->hDragProxy = CreateDragProxy(ptb->ci.hwnd, ToolbarDragCallback, FALSE);
  5204. if (ptb->hDragProxy)
  5205. return (LRESULT)GetDragProxyTarget(ptb->hDragProxy, (IDropTarget **)lParam);
  5206. }
  5207. return E_FAIL;
  5208. case WM_GETFONT:
  5209. return (LRESULT)(ptb? ptb->hfontIcon : 0);
  5210. case TB_LOADIMAGES:
  5211. return TBLoadImages(ptb, (UINT_PTR) wParam, (HINSTANCE)lParam);
  5212. case TB_GETTOOLTIPS:
  5213. TB_ForceCreateTooltips(ptb);
  5214. return (LRESULT)ptb->hwndToolTips;
  5215. case TB_SETTOOLTIPS:
  5216. ptb->hwndToolTips = (HWND)wParam;
  5217. break;
  5218. case TB_SETPARENT:
  5219. {
  5220. HWND hwndOld = ptb->ci.hwndParent;
  5221. ptb->ci.hwndParent = (HWND)wParam;
  5222. return (LRESULT)hwndOld;
  5223. }
  5224. case TB_GETBUTTONINFOA:
  5225. return TB_OnGetButtonInfoA(ptb, (int)wParam, (LPTBBUTTONINFOA)lParam);
  5226. case TB_SETBUTTONINFOA:
  5227. return TB_OnSetButtonInfoA(ptb, (int)wParam, (LPTBBUTTONINFOA)lParam);
  5228. case TB_GETBUTTONINFO:
  5229. return TB_OnGetButtonInfo(ptb, (int)wParam, (LPTBBUTTONINFO)lParam);
  5230. case TB_SETBUTTONINFO:
  5231. return TB_OnSetButtonInfo(ptb, (int)wParam, (LPTBBUTTONINFO)lParam);
  5232. case TB_CHANGEBITMAP:
  5233. iPos = PositionFromID(ptb, wParam);
  5234. if (iPos < 0)
  5235. return(FALSE);
  5236. //
  5237. // Check to see if the new bitmap ID is
  5238. // valid.
  5239. //
  5240. ptbButton = &ptb->Buttons[iPos];
  5241. return TB_OnSetImage(ptb, ptbButton, LOWORD(lParam));
  5242. case TB_GETBITMAP:
  5243. iPos = PositionFromID(ptb, wParam);
  5244. if (iPos < 0)
  5245. return(FALSE);
  5246. ptbButton = &ptb->Buttons[iPos];
  5247. return ptbButton->DUMMYUNION_MEMBER(iBitmap);
  5248. case TB_GETBUTTONTEXTA:
  5249. iPos = PositionFromID(ptb, wParam);
  5250. if (iPos >= 0) {
  5251. LPTSTR psz;
  5252. ptbButton = &ptb->Buttons[iPos];
  5253. psz = TB_StrForButton(ptb, ptbButton);
  5254. if (psz)
  5255. {
  5256. // Passing a 0 for the length of the buffer when the
  5257. // buffer is NULL returns the number bytes required
  5258. // to convert the string.
  5259. int cbBuff = WideCharToMultiByte(CP_ACP, 0, psz, -1, NULL, 0, NULL, NULL);
  5260. // We used to pass an obscenly large number for the buffer length,
  5261. // but on checked builds, this causes badness. So no we double-dip
  5262. // into WideCharToMultiByte to calculate the real size required.
  5263. if (lParam)
  5264. {
  5265. WideCharToMultiByte(CP_ACP, 0, psz, -1, (LPSTR)lParam, cbBuff, NULL, NULL);
  5266. }
  5267. // WideChar include a trailing NULL but we don't want to.
  5268. return cbBuff - 1;
  5269. }
  5270. }
  5271. return -1;
  5272. case TB_GETBUTTONTEXT:
  5273. iPos = PositionFromID(ptb, wParam);
  5274. if (iPos >= 0)
  5275. {
  5276. LPCTSTR psz;
  5277. ptbButton = &ptb->Buttons[iPos];
  5278. psz = TB_StrForButton(ptb, ptbButton);
  5279. if (psz)
  5280. {
  5281. DWORD cch = lstrlen(psz);
  5282. if (lParam)
  5283. {
  5284. // REVIEW: message parameters do not indicate the size of the
  5285. // destination buffer.
  5286. StringCchCopy((LPTSTR)lParam, cch+1, psz);
  5287. }
  5288. return cch;
  5289. }
  5290. }
  5291. return -1;
  5292. case TB_GETBITMAPFLAGS:
  5293. {
  5294. DWORD fFlags = 0;
  5295. HDC hdc = GetDC(NULL);
  5296. if (GetDeviceCaps(hdc, LOGPIXELSY) >= 120)
  5297. fFlags |= TBBF_LARGE;
  5298. ReleaseDC(NULL, hdc);
  5299. return fFlags;
  5300. }
  5301. case TB_SETINDENT:
  5302. ptb->xFirstButton = (int) wParam;
  5303. InvalidateRect (hwnd, NULL, TRUE);
  5304. TBInvalidateItemRects(ptb);
  5305. return 1;
  5306. case TB_SETMAXTEXTROWS:
  5307. if (ptb->nTextRows != (int)wParam) {
  5308. ptb->nTextRows = (int) wParam;
  5309. TBRecalc(ptb);
  5310. InvalidateRect(hwnd, NULL, TRUE);
  5311. }
  5312. return 1;
  5313. case TB_SETLISTGAP:
  5314. ptb->iListGap = (int) wParam;
  5315. InvalidateRect(hwnd, NULL, TRUE);
  5316. break;
  5317. case TB_SETDROPDOWNGAP:
  5318. ptb->iDropDownGap = (int) wParam;
  5319. InvalidateRect(hwnd, NULL, TRUE);
  5320. break;
  5321. case TB_GETTEXTROWS:
  5322. return ptb->nTextRows;
  5323. case TB_HITTEST:
  5324. return TBHitTest(ptb, ((LPPOINT)lParam)->x, ((LPPOINT)lParam)->y);
  5325. case TB_SETDRAWTEXTFLAGS:
  5326. {
  5327. UINT uOld = ptb->uDrawText;
  5328. ptb->uDrawText = (UINT) (lParam & wParam);
  5329. ptb->uDrawTextMask = (UINT) wParam;
  5330. return uOld;
  5331. }
  5332. case TB_GETEXTENDEDSTYLE:
  5333. return (ptb->dwStyleEx);
  5334. case TB_SETEXTENDEDSTYLE:
  5335. {
  5336. DWORD dwRet = ptb->dwStyleEx;
  5337. TBSetStyleEx(ptb, (DWORD) lParam, (DWORD) wParam);
  5338. return dwRet;
  5339. }
  5340. case TB_SETBOUNDINGSIZE:
  5341. {
  5342. LPSIZE lpSize = (LPSIZE)lParam;
  5343. ptb->sizeBound = *lpSize;
  5344. break;
  5345. }
  5346. case TB_GETCOLORSCHEME:
  5347. {
  5348. LPCOLORSCHEME lpclrsc = (LPCOLORSCHEME) lParam;
  5349. if (lpclrsc) {
  5350. if (lpclrsc->dwSize == sizeof(COLORSCHEME))
  5351. *lpclrsc = ptb->clrsc;
  5352. }
  5353. return (LRESULT) lpclrsc;
  5354. }
  5355. case TB_SETCOLORSCHEME:
  5356. {
  5357. if (lParam) {
  5358. if (((LPCOLORSCHEME) lParam)->dwSize == sizeof(COLORSCHEME)) {
  5359. ptb->clrsc.clrBtnHighlight = ((LPCOLORSCHEME) lParam)->clrBtnHighlight;
  5360. ptb->clrsc.clrBtnShadow = ((LPCOLORSCHEME) lParam)->clrBtnShadow;
  5361. InvalidateRect(hwnd, NULL, FALSE);
  5362. if (ptb->ci.style & WS_BORDER)
  5363. CCInvalidateFrame(hwnd);
  5364. }
  5365. }
  5366. }
  5367. break;
  5368. case WM_GETOBJECT:
  5369. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  5370. return MSAA_CLASSNAMEIDX_TOOLBAR;
  5371. goto DoDefault;
  5372. case WM_NULL:
  5373. // Trap failed RegsiterWindowMessages;
  5374. break;
  5375. default:
  5376. {
  5377. LRESULT lres;
  5378. if (g_uDragImages == uMsg)
  5379. return TBGenerateDragImage(ptb, (SHDRAGIMAGE*)lParam);
  5380. if (CCWndProc(&ptb->ci, uMsg, wParam, lParam, &lres))
  5381. return lres;
  5382. }
  5383. DoDefault:
  5384. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  5385. }
  5386. return 0L;
  5387. }
  5388. int TB_CalcWidth(PTBSTATE ptb, int iHeight)
  5389. {
  5390. RECT rc;
  5391. int iWidth = 0;
  5392. int iMaxBtnWidth = 0; // ptb->iButWidth isn't always width of widest button
  5393. LPTBBUTTONDATA pButton, pBtnLast;
  5394. pBtnLast = &(ptb->Buttons[ptb->iNumButtons]);
  5395. for(pButton = ptb->Buttons; pButton < pBtnLast; pButton++)
  5396. {
  5397. if (!(pButton->fsState & TBSTATE_HIDDEN))
  5398. {
  5399. int iBtnWidth = TBWidthOfButton(ptb, pButton, NULL);
  5400. iWidth += iBtnWidth - s_dxOverlap;
  5401. iMaxBtnWidth = max(iMaxBtnWidth, iBtnWidth);
  5402. }
  5403. }
  5404. if (ptb->ci.style & TBSTYLE_WRAPABLE) {
  5405. //Make sure the height is a multiple of button height
  5406. iHeight -= (iHeight % ptb->iButHeight);
  5407. if (iHeight < ptb->iButHeight)
  5408. iHeight = ptb->iButHeight;
  5409. WrapToolbar(ptb, iWidth, &rc, NULL);
  5410. // if wrapping at full width gives us a height that's too big,
  5411. // then there's nothing we can do because widening it still keeps us at 1 row
  5412. if (iHeight > RECTHEIGHT(rc)) {
  5413. int iPrevWidth;
  5414. BOOL fDivide = TRUE; //first start by dividing for speed, then narrow it down by subtraction
  5415. TraceMsg(TF_TOOLBAR, "Toolbar: performing expensive width calculation!");
  5416. while (iMaxBtnWidth < iWidth) {
  5417. iPrevWidth = iWidth;
  5418. if (fDivide)
  5419. iWidth = (iWidth * 2) / 3;
  5420. else
  5421. iWidth -= ptb->iButWidth;
  5422. if (iWidth == iPrevWidth)
  5423. break;
  5424. WrapToolbar(ptb, iWidth, &rc, NULL);
  5425. if (iHeight < RECTHEIGHT(rc)) {
  5426. iWidth = iPrevWidth;
  5427. if (fDivide) {
  5428. // we've overstepped on dividing. go to the previous width
  5429. // that was ok, and now try subtracting one button at a time
  5430. fDivide = FALSE;
  5431. } else
  5432. break;
  5433. }
  5434. };
  5435. WrapToolbar(ptb, iWidth, &rc, NULL);
  5436. iWidth = max(RECTWIDTH(rc), iMaxBtnWidth);
  5437. }
  5438. // WrapToolbar above has the side effect of actually modifying
  5439. // the layout. we need to restore it after doing all this calculations
  5440. TBAutoSize(ptb);
  5441. }
  5442. return iWidth;
  5443. }
  5444. LRESULT TB_OnScroll(PTBSTATE ptb, LPNMHDR pnm)
  5445. {
  5446. POINT pt, ptTemp;
  5447. LPNMPGSCROLL pscroll = (LPNMPGSCROLL)pnm;
  5448. int iDir = pscroll->iDir;
  5449. RECT rcTemp, rc = pscroll->rcParent;
  5450. int parentsize = 0;
  5451. int scroll = pscroll->iScroll;
  5452. int iButton = 0;
  5453. int iButtonSize = ptb->iButHeight;
  5454. int y = 0;
  5455. int iCurrentButton = 0;
  5456. //This variable holds the number of buttons in a row
  5457. int iButInRow = 0;
  5458. pt.x = pscroll->iXpos;
  5459. pt.y = pscroll->iYpos;
  5460. ptTemp = pt;
  5461. //We need to add the offset of the toolbar to the scroll position to get the
  5462. //correct scroll positon in terms of toolbar window
  5463. pt.x += ptb->xFirstButton;
  5464. pt.y += ptb->iYPos;
  5465. ptTemp = pt;
  5466. if ((iDir == PGF_SCROLLUP) || (iDir == PGF_SCROLLDOWN))
  5467. {
  5468. //Vertical Mode
  5469. if (ptb->iButWidth == 0 )
  5470. {
  5471. iButInRow = 1;
  5472. }
  5473. else
  5474. {
  5475. iButInRow = RECTWIDTH(rc) / ptb->iButWidth;
  5476. }
  5477. }
  5478. else
  5479. {
  5480. //Horizontal Mode
  5481. iButInRow = 1;
  5482. }
  5483. // if the parent height/width is less than button height/width then set the number of
  5484. // buttons in a row to be 1
  5485. if (0 == iButInRow)
  5486. {
  5487. iButInRow = 1;
  5488. }
  5489. iCurrentButton = TBHitTest(ptb, pt.x + 1, pt.y + 1);
  5490. //if the button is negative then we have hit a seperator.
  5491. //Convert the index of the seperator into button index
  5492. if (iCurrentButton < 0)
  5493. iCurrentButton = -iCurrentButton - 1;
  5494. switch ( iDir )
  5495. {
  5496. case PGF_SCROLLUP:
  5497. case PGF_SCROLLLEFT:
  5498. if(iDir == PGF_SCROLLLEFT)
  5499. {
  5500. FlipRect(&rc);
  5501. FlipPoint(&pt);
  5502. FlipPoint(&ptTemp);
  5503. iButtonSize = ptb->iButWidth;
  5504. }
  5505. //Check if any button is partially visible at the left/top. if so then set the bottom
  5506. // of that button to be our current offset and then scroll. This avoids skipping over
  5507. // certain buttons when partial buttons are displayed at the left or top
  5508. y = pt.y;
  5509. TB_GetItemRect(ptb, iCurrentButton, &rcTemp);
  5510. if(iDir == PGF_SCROLLLEFT)
  5511. {
  5512. FlipRect(&rcTemp);
  5513. }
  5514. if (rcTemp.top < y-1)
  5515. {
  5516. iCurrentButton += iButInRow;
  5517. }
  5518. //Now do the actual calculation
  5519. parentsize = RECTHEIGHT(rc);
  5520. //if the control key is down and we have more than parentsize size of child window
  5521. // then scroll by that amount
  5522. if (pscroll->fwKeys & PGK_CONTROL)
  5523. {
  5524. if ((y - parentsize) > 0 )
  5525. {
  5526. scroll = parentsize;
  5527. }
  5528. else
  5529. {
  5530. scroll = y;
  5531. return 0L;
  5532. }
  5533. } else if ((y - iButtonSize) > 0 ){
  5534. // we dont have control key down so scroll by one buttonsize
  5535. scroll = iButtonSize;
  5536. } else {
  5537. scroll = pt.y;
  5538. return 0L;
  5539. }
  5540. ptTemp.y -= scroll;
  5541. if(iDir == PGF_SCROLLLEFT)
  5542. {
  5543. FlipPoint(&ptTemp);
  5544. }
  5545. iButton = TBHitTest(ptb, ptTemp.x, ptTemp.y);
  5546. //if the button is negative then we have hit a seperator.
  5547. //Convert the index of the seperator into button index
  5548. if (iButton < 0)
  5549. iButton = -iButton -1 ;
  5550. // if the hit test gives us the same button as our prevbutton then set the button
  5551. // to one button to the left of the prev button
  5552. if ((iButton == iCurrentButton) && (iButton >= iButInRow))
  5553. {
  5554. iButton -= iButInRow;
  5555. if ((ptb->Buttons[iButton].fsStyle & BTNS_SEP) && (iButton >= iButInRow))
  5556. {
  5557. iButton -= iButInRow;
  5558. }
  5559. }
  5560. //When scrolling left if we end up in the middle of some button then we align it to the
  5561. //right of that button this is to avoid scrolling more than the pager window width but if the
  5562. // button happens to be the left button of our current button then we end up in not scrolling
  5563. //if thats the case then move one more button to the left.
  5564. if (iButton == iCurrentButton-iButInRow)
  5565. {
  5566. iButton -= iButInRow;
  5567. }
  5568. TB_GetItemRect(ptb, iButton, &rcTemp);
  5569. if(iDir == PGF_SCROLLLEFT)
  5570. {
  5571. FlipRect(&rcTemp);
  5572. }
  5573. scroll = pt.y - rcTemp.bottom;
  5574. //Set the scroll value
  5575. pscroll->iScroll = scroll;
  5576. break;
  5577. case PGF_SCROLLDOWN:
  5578. case PGF_SCROLLRIGHT:
  5579. {
  5580. RECT rcChild;
  5581. int childsize;
  5582. GetWindowRect(ptb->ci.hwnd, &rcChild);
  5583. if( iDir == PGF_SCROLLRIGHT)
  5584. {
  5585. FlipRect(&rcChild);
  5586. FlipRect(&rc);
  5587. FlipPoint(&pt);
  5588. FlipPoint(&ptTemp);
  5589. iButtonSize = ptb->iButWidth;
  5590. }
  5591. childsize = RECTHEIGHT(rcChild);
  5592. parentsize = RECTHEIGHT(rc);
  5593. //if the control key is down and we have more than parentsize size of child window
  5594. // then scroll by that amount
  5595. if (pscroll->fwKeys & PGK_CONTROL)
  5596. {
  5597. if ((childsize - pt.y - parentsize) > parentsize)
  5598. {
  5599. scroll = parentsize;
  5600. }
  5601. else
  5602. {
  5603. scroll = childsize - pt.y - parentsize;
  5604. return 0L;
  5605. }
  5606. } else if (childsize - pt.y - parentsize > iButtonSize) {
  5607. // we dont have control key down so scroll by one buttonsize
  5608. scroll = iButtonSize;
  5609. } else {
  5610. pscroll->iScroll = childsize - pt.y - parentsize;
  5611. return 0L;
  5612. }
  5613. ptTemp.y += scroll;
  5614. if(iDir == PGF_SCROLLRIGHT)
  5615. {
  5616. FlipPoint(&ptTemp);
  5617. }
  5618. iButton = TBHitTest(ptb, ptTemp.x, ptTemp.y);
  5619. //if the button is negative then we have hit a seperator.
  5620. //Convert the index of the seperator into button index
  5621. if (iButton < 0)
  5622. iButton = -iButton - 1 ;
  5623. if ((iButton == iCurrentButton) && ((iButton + iButInRow) < ptb->iNumButtons))
  5624. {
  5625. iButton += iButInRow;
  5626. if ((ptb->Buttons[iButton].fsStyle & BTNS_SEP) && ((iButton + iButInRow) < ptb->iNumButtons))
  5627. {
  5628. iButton += iButInRow;
  5629. }
  5630. }
  5631. TB_GetItemRect(ptb, iButton, &rcTemp);
  5632. if(iDir == PGF_SCROLLRIGHT)
  5633. {
  5634. FlipRect(&rcTemp);
  5635. }
  5636. scroll = rcTemp.top - pt.y ;
  5637. //Set the scroll value
  5638. pscroll->iScroll = scroll;
  5639. break;
  5640. }
  5641. }
  5642. return 0L;
  5643. }
  5644. int TB_CalcHeight(PTBSTATE ptb)
  5645. {
  5646. int iHeight = 0;
  5647. int i;
  5648. ASSERT(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL);
  5649. ASSERT(!(ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN));
  5650. for (i = 0; i < ptb->iNumButtons; i++)
  5651. {
  5652. if (!(ptb->Buttons[i].fsState & TBSTATE_HIDDEN))
  5653. {
  5654. if (ptb->Buttons[i].fsStyle & BTNS_SEP)
  5655. iHeight += (TBGetSepHeight(ptb, &ptb->Buttons[i]));
  5656. else
  5657. iHeight += ptb->iButHeight;
  5658. }
  5659. }
  5660. return iHeight;
  5661. }
  5662. LRESULT TB_OnCalcSize(PTBSTATE ptb, LPNMHDR pnm)
  5663. {
  5664. LPNMPGCALCSIZE pcalcsize = (LPNMPGCALCSIZE)pnm;
  5665. RECT rc;
  5666. switch(pcalcsize->dwFlag)
  5667. {
  5668. case PGF_CALCHEIGHT:
  5669. if (ptb->szCached.cx == pcalcsize->iWidth)
  5670. pcalcsize->iHeight = ptb->szCached.cy;
  5671. else
  5672. {
  5673. if (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN)
  5674. {
  5675. WrapToolbarCol(ptb, ptb->sizeBound.cy, &rc, NULL);
  5676. pcalcsize->iWidth = RECTWIDTH(rc);
  5677. pcalcsize->iHeight = RECTHEIGHT(rc);
  5678. }
  5679. else if (ptb->dwStyleEx & TBSTYLE_EX_VERTICAL)
  5680. {
  5681. pcalcsize->iHeight = TB_CalcHeight(ptb);
  5682. }
  5683. else
  5684. {
  5685. // BUGBUG: this WrapToolbar call can modify toolbar layout ...
  5686. // seems busted. should perhaps call TBAutoSize after to restore.
  5687. WrapToolbar(ptb, pcalcsize->iWidth, &rc, NULL);
  5688. pcalcsize->iHeight = RECTHEIGHT(rc);
  5689. }
  5690. }
  5691. break;
  5692. case PGF_CALCWIDTH:
  5693. if (ptb->szCached.cy == pcalcsize->iHeight) {
  5694. pcalcsize->iWidth = ptb->szCached.cx;
  5695. } else {
  5696. pcalcsize->iWidth = TB_CalcWidth(ptb, pcalcsize->iHeight);
  5697. }
  5698. break;
  5699. }
  5700. ptb->szCached.cx = pcalcsize->iWidth;
  5701. ptb->szCached.cy = pcalcsize->iHeight;
  5702. return 0L;
  5703. }
  5704. LRESULT TB_OnPagerControlNotify(PTBSTATE ptb, LPNMHDR pnm)
  5705. {
  5706. switch(pnm->code) {
  5707. case PGN_SCROLL:
  5708. return TB_OnScroll(ptb, pnm);
  5709. break;
  5710. case PGN_CALCSIZE:
  5711. return TB_OnCalcSize(ptb, pnm);
  5712. break;
  5713. }
  5714. return 0L;
  5715. }
  5716. BOOL TBGetMaxSize( PTBSTATE ptb, LPSIZE lpsize )
  5717. {
  5718. // need to calc the number of buttons and then the number of separators...
  5719. int iButton;
  5720. LPTBBUTTONDATA pAllButtons = ptb->Buttons;
  5721. int iRealButtons = 0;
  5722. int iSeparators = 0;
  5723. if ( !lpsize )
  5724. return FALSE;
  5725. if (ptb->dwStyleEx & TBSTYLE_EX_MULTICOLUMN)
  5726. {
  5727. ASSERT(ptb->dwStyleEx & TBSTYLE_EX_VERTICAL);
  5728. lpsize->cx = RECTWIDTH(ptb->rc);
  5729. lpsize->cy = RECTHEIGHT(ptb->rc);
  5730. return TRUE;
  5731. }
  5732. for (iButton = 0; iButton < ptb->iNumButtons; iButton++)
  5733. {
  5734. LPTBBUTTONDATA pButton = &pAllButtons[iButton];
  5735. if (!( pButton->fsState & TBSTATE_HIDDEN ))
  5736. {
  5737. if ( pButton->fsStyle & BTNS_SEP )
  5738. iSeparators ++;
  5739. else
  5740. iRealButtons ++;
  5741. }
  5742. }
  5743. // BUGBUG: g_dxButtonSep is handy, but what if the separator style changes,
  5744. // BUGBUG: for example we don't distinguish between flat and non-flat separators..
  5745. if ( ptb->ci.style & CCS_VERT )
  5746. {
  5747. // we are vertical ...
  5748. lpsize->cx = ptb->iButWidth;
  5749. lpsize->cy = ptb->iButHeight * iRealButtons + g_dxButtonSep * iSeparators;
  5750. }
  5751. else
  5752. {
  5753. lpsize->cx = ptb->iButWidth * iRealButtons + g_dxButtonSep * iSeparators;
  5754. lpsize->cy = ptb->iButHeight;
  5755. }
  5756. return TRUE;
  5757. }
  5758. void TBGetItem(PTBSTATE ptb, LPTBBUTTONDATA ptButton, LPNMTBDISPINFO ptbdi)
  5759. {
  5760. ptbdi->idCommand = ptButton->idCommand;
  5761. ptbdi->iImage = -1;
  5762. ptbdi->lParam = ptButton->dwData;
  5763. CCSendNotify(&ptb->ci, TBN_GETDISPINFO, &(ptbdi->hdr));
  5764. if(ptbdi->dwMask & TBNF_DI_SETITEM) {
  5765. if(ptbdi->dwMask & TBNF_IMAGE)
  5766. ptButton->DUMMYUNION_MEMBER(iBitmap) = ptbdi->iImage;
  5767. }
  5768. }
  5769. BOOL TBGetInfoTip(PTBSTATE ptb, LPTOOLTIPTEXT lpttt, LPTBBUTTONDATA pTBButton)
  5770. {
  5771. NMTBGETINFOTIP git;
  5772. TCHAR szBuf[INFOTIPSIZE];
  5773. szBuf[0] = 0;
  5774. git.pszText = szBuf;
  5775. git.cchTextMax = ARRAYSIZE(szBuf);
  5776. git.iItem = pTBButton->idCommand;
  5777. git.lParam = pTBButton->dwData;
  5778. CCSendNotify(&ptb->ci, TBN_GETINFOTIP, &git.hdr);
  5779. if (git.pszText && git.pszText[0]) {
  5780. // if they didn't fill anything in, go to the default stuff
  5781. // without modifying the notify structure
  5782. Str_Set(&ptb->pszTip, git.pszText);
  5783. lpttt->lpszText = ptb->pszTip;
  5784. return lpttt->lpszText && lpttt->lpszText[0];
  5785. }
  5786. return FALSE;
  5787. }