Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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