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

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