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

7395 lines
216 KiB

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