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

1204 lines
35 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: tooltips.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Implements system tooltips.
  7. *
  8. * History:
  9. * 25-Aug-1996 vadimg created
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #define TT_XOFFSET 2
  14. #define TT_YOFFSET 1
  15. #define TTT_SHOW 1
  16. #define TTT_HIDE 2
  17. #define TTT_ANIMATE 3
  18. #define TT_ANIMATEDELAY 20
  19. #define TTF_POSITIVE 0x00000001
  20. #define bitsizeof(x) (sizeof(x) * 8)
  21. LONG GreGetBitmapBits(HBITMAP, ULONG, PBYTE, PLONG);
  22. DWORD CalcCaptionButton(PWND pwnd, int hit, LPWORD pcmd, LPRECT prcBtn, LPWORD pbm);
  23. int HitTestScrollBar(PWND pwnd, int ht, POINT pt);
  24. BOOL xxxHotTrackSB(PWND pwnd, int htEx, BOOL fDraw);
  25. __inline void ZeroTooltip(PTOOLTIPWND pttwnd)
  26. {
  27. RtlZeroMemory((PBYTE)pttwnd + (sizeof(TOOLTIPWND) - sizeof(TOOLTIP)),
  28. sizeof(TOOLTIP));
  29. }
  30. /***************************************************************************\
  31. * GetTooltipDC
  32. *
  33. * 2/3/1998 vadimg created
  34. \***************************************************************************/
  35. HDC GetTooltipDC(PTOOLTIPWND pttwnd)
  36. {
  37. HDC hdc = _GetDCEx((PWND)pttwnd, NULL, DCX_WINDOW | DCX_CACHE |
  38. DCX_USESTYLE);
  39. if (hdc == NULL)
  40. return NULL;
  41. GreSelectFont(hdc, ghStatusFont);
  42. return hdc;
  43. }
  44. /***************************************************************************\
  45. * InitTooltipAnimation
  46. *
  47. * Creates memory bitmap and DC for use by system tooltips. Gets the screen
  48. * DC used throughout.
  49. *
  50. * 12-Sep-96 vadimg created
  51. \***************************************************************************/
  52. void InitTooltipAnimation(PTOOLTIPWND pttwnd)
  53. {
  54. HDC hdc = GetTooltipDC(pttwnd);
  55. if ((pttwnd->hdcMem = GreCreateCompatibleDC(hdc)) == NULL) {
  56. return;
  57. }
  58. _ReleaseDC(hdc);
  59. GreSetDCOwner(pttwnd->hdcMem, OBJECT_OWNER_PUBLIC);
  60. }
  61. /***************************************************************************\
  62. * DestroyTooltipBitmap
  63. *
  64. \***************************************************************************/
  65. void DestroyTooltipBitmap(PTOOLTIPWND pttwnd)
  66. {
  67. if (pttwnd->hbmMem == NULL)
  68. return;
  69. GreSelectBitmap(pttwnd->hdcMem, GreGetStockObject(PRIV_STOCK_BITMAP));
  70. GreDeleteObject(pttwnd->hbmMem);
  71. pttwnd->hbmMem = NULL;
  72. }
  73. /***************************************************************************\
  74. * CreateTooltipBitmap
  75. *
  76. \***************************************************************************/
  77. BOOL CreateTooltipBitmap(PTOOLTIPWND pttwnd, UINT cx, UINT cy)
  78. {
  79. HDC hdc;
  80. if (pttwnd->hdcMem == NULL) {
  81. RIPMSG0(RIP_WARNING, "CreateTooltipBitmap: pttwnd->hdcMem is NULL");
  82. return FALSE;
  83. }
  84. DestroyTooltipBitmap(pttwnd);
  85. hdc = GetTooltipDC(pttwnd);
  86. pttwnd->hbmMem = GreCreateCompatibleBitmap(hdc, cx, cy);
  87. _ReleaseDC(hdc);
  88. if (pttwnd->hbmMem == NULL) {
  89. RIPMSG0(RIP_WARNING, "CreateTooltipBitmap: hbmMem is NULL");
  90. return FALSE;
  91. }
  92. GreSelectBitmap(pttwnd->hdcMem, pttwnd->hbmMem);
  93. return TRUE;
  94. }
  95. /***************************************************************************\
  96. * CleanupTooltipAnimation
  97. *
  98. * Deletes memory bitmap and DC for use by system tooltips. Release the
  99. * screen DC.
  100. *
  101. * 12-Sep-96 vadimg created
  102. \***************************************************************************/
  103. void CleanupTooltipAnimation(PTOOLTIPWND pttwnd)
  104. {
  105. DestroyTooltipBitmap(pttwnd);
  106. if (pttwnd->hdcMem != NULL) {
  107. GreSetDCOwner(pttwnd->hdcMem, OBJECT_OWNER_CURRENT);
  108. GreDeleteDC(pttwnd->hdcMem);
  109. }
  110. }
  111. /***************************************************************************\
  112. * TooltipAnimate
  113. *
  114. * Perform one frame of window animation. Just a simplified version of
  115. * the AnimateWindow API.
  116. *
  117. * 12-Sep-96 vadimg created
  118. \***************************************************************************/
  119. BOOL TooltipAnimate(PTOOLTIPWND pttwnd)
  120. {
  121. int y, yMem, yReal, ny, iy, cx, cy;
  122. DWORD dwElapsed;
  123. HDC hdc;
  124. BOOL fRet = FALSE;
  125. if (pttwnd->pstr == NULL)
  126. return TRUE;
  127. hdc = GetTooltipDC(pttwnd);
  128. cx = pttwnd->rcWindow.right - pttwnd->rcWindow.left;
  129. cy = pttwnd->rcWindow.bottom - pttwnd->rcWindow.top;
  130. dwElapsed = NtGetTickCount() - pttwnd->dwAnimStart;
  131. iy = MultDiv(cy, dwElapsed, CMS_TOOLTIP);
  132. if (dwElapsed > CMS_TOOLTIP || iy == cy) {
  133. GreBitBlt(hdc, 0, 0, cx, cy, pttwnd->hdcMem, 0, 0, SRCCOPY | NOMIRRORBITMAP, 0);
  134. fRet = TRUE;
  135. goto Cleanup;
  136. } else if (pttwnd->iyAnim == iy) {
  137. goto Cleanup;
  138. }
  139. if (pttwnd->dwFlags & TTF_POSITIVE) {
  140. y = 0;
  141. ny = 0;
  142. } else {
  143. y = cy;
  144. ny = -1;
  145. }
  146. yReal = y + ny * iy;
  147. yMem = (pttwnd->dwFlags & TTF_POSITIVE) ? cy - iy : 0;
  148. pttwnd->iyAnim = iy;
  149. GreBitBlt(hdc, 0, yReal, cx, iy, pttwnd->hdcMem, 0, yMem, SRCCOPY | NOMIRRORBITMAP, 0);
  150. Cleanup:
  151. _ReleaseDC(hdc);
  152. return fRet;
  153. }
  154. /***************************************************************************\
  155. * GetCursorHeight
  156. *
  157. * This is tricky. We need to get the actual cursor size from the hotspot
  158. * down to the end. There is no API in windows to do this, CYCURSOR is
  159. * just the metric for the size of the bitmap, the cursor starts at the top
  160. * of the bitmap and may be smaller than the bitmap itself.
  161. *
  162. * 12-Sep-96 vadimg ported from common controls
  163. \***************************************************************************/
  164. int GetCursorHeight(void)
  165. {
  166. int iAnd, iXor, dy = 16;
  167. WORD wMask[128];
  168. ICONINFO ii;
  169. BITMAP bm;
  170. PCURSOR pcur;
  171. long lOffset = 0;
  172. if ((pcur = PtiCurrent()->pq->spcurCurrent) == NULL) {
  173. return dy;
  174. }
  175. if (!_InternalGetIconInfo(pcur, &ii, NULL, NULL, NULL, FALSE)) {
  176. return dy;
  177. }
  178. if (!GreExtGetObjectW(ii.hbmMask, sizeof(bm), (LPSTR)&bm)) {
  179. goto Bail;
  180. }
  181. /*
  182. * Use the AND mask to get the cursor height if the XOR mask is there.
  183. */
  184. if (!GreGetBitmapBits(ii.hbmMask, sizeof(wMask), (BYTE*)wMask, &lOffset)) {
  185. goto Bail;
  186. }
  187. iAnd = (int)(bm.bmWidth * bm.bmHeight / bitsizeof(WORD));
  188. if (ii.hbmColor == NULL) {
  189. /*
  190. * if no color (XOR) bitmap, then the hbmMask is a double height bitmap
  191. * with the cursor and the mask stacked.
  192. */
  193. iXor = iAnd - 1;
  194. iAnd /= 2;
  195. } else {
  196. iXor = 0;
  197. }
  198. if (iAnd >= sizeof(wMask)) {
  199. iAnd = sizeof(wMask) - 1;
  200. }
  201. if (iXor >= sizeof(wMask)) {
  202. iXor = 0;
  203. }
  204. for (iAnd--; iAnd >= 0; iAnd--) {
  205. if ((iXor != 0 && wMask[iXor--] != 0) || wMask[iAnd] != 0xFFFF) {
  206. break;
  207. }
  208. }
  209. /*
  210. * Compute the distance between the pointer's lowest point and hotspot.
  211. */
  212. dy = (iAnd + 1) * bitsizeof(WORD) / (int)bm.bmWidth - (int)ii.yHotspot;
  213. Bail:
  214. if (ii.hbmColor) {
  215. GreDeleteObject(ii.hbmColor);
  216. }
  217. if (ii.hbmMask) {
  218. GreDeleteObject(ii.hbmMask);
  219. }
  220. return dy;
  221. }
  222. /***************************************************************************\
  223. * TooltipGetPosition
  224. *
  225. * Get the tooltip position on the screen taking into account the size of
  226. * the tooltip and the screen. The TTF_POSITIVE flag determines if positive
  227. * or negative animation is used.
  228. *
  229. * 12-Sep-96 vadimg created
  230. \***************************************************************************/
  231. BOOL TooltipGetPosition(PTOOLTIPWND pttwnd, SIZE *psize, POINT *ppt)
  232. {
  233. PMONITOR pMonitor;
  234. *ppt = gpsi->ptCursor;
  235. pMonitor = _MonitorFromPoint(*ppt, MONITOR_DEFAULTTONULL);
  236. if (pMonitor == NULL) {
  237. return FALSE;
  238. }
  239. if (ppt->y + psize->cy >= pMonitor->rcMonitor.bottom) {
  240. ppt->y = ppt->y - psize->cy;
  241. pttwnd->dwFlags &= ~TTF_POSITIVE;
  242. } else {
  243. ppt->y += GetCursorHeight();
  244. pttwnd->dwFlags |= TTF_POSITIVE;
  245. }
  246. if (ppt->x + psize->cx >= pMonitor->rcMonitor.right) {
  247. ppt->x = pMonitor->rcMonitor.right - psize->cx;
  248. }
  249. if (ppt->x < pMonitor->rcMonitor.left) {
  250. ppt->x = pMonitor->rcMonitor.left;
  251. }
  252. return TRUE;
  253. }
  254. /***************************************************************************\
  255. * xxxTooltipGetSize
  256. *
  257. * Estimate the size of the tooltip window based on the size of the text.
  258. *
  259. * 12-Sep-96 vadimg created
  260. \***************************************************************************/
  261. void xxxTooltipGetSize(PTOOLTIPWND pttwnd, SIZE *psize)
  262. {
  263. HDC hdc;
  264. CheckLock(pttwnd);
  265. hdc = GetTooltipDC(pttwnd);
  266. if (CALL_LPK(PtiCurrentShared())) {
  267. xxxClientGetTextExtentPointW(hdc, pttwnd->pstr, wcslen(pttwnd->pstr), psize);
  268. } else {
  269. GreGetTextExtentW(hdc, pttwnd->pstr, wcslen(pttwnd->pstr),
  270. psize, GGTE_WIN3_EXTENT);
  271. }
  272. _ReleaseDC(hdc);
  273. psize->cx += SYSMET(CXEDGE) + 2 * SYSMET(CXBORDER) * TT_XOFFSET;
  274. psize->cy += SYSMET(CYEDGE) + 2 * SYSMET(CYBORDER) * TT_YOFFSET;
  275. }
  276. /***************************************************************************\
  277. * xxxTooltipRender
  278. *
  279. * Render the tooltip window into the provided DC.
  280. *
  281. * 12-Sep-96 vadimg created
  282. \***************************************************************************/
  283. void xxxTooltipRender(PTOOLTIPWND pttwnd, HDC hdc)
  284. {
  285. COLORREF crBk;
  286. UINT uFlags;
  287. RECT rc;
  288. CheckLock(pttwnd);
  289. if (pttwnd->pstr == NULL)
  290. return;
  291. GreSelectFont(hdc, ghStatusFont);
  292. GreSetTextColor(hdc, gpsi->argbSystem[COLOR_INFOTEXT]);
  293. crBk = gpsi->argbSystem[COLOR_INFOBK];
  294. CopyOffsetRect(&rc, &pttwnd->rcClient, -pttwnd->rcClient.left,
  295. -pttwnd->rcClient.top);
  296. /*
  297. * We don't want dithered colors, so FillRect with the nearest color.
  298. */
  299. if (crBk == GreGetNearestColor(hdc, crBk)) {
  300. GreSetBkColor(hdc, crBk);
  301. uFlags = ETO_OPAQUE;
  302. } else {
  303. FillRect(hdc, &rc, SYSHBR(INFOBK));
  304. GreSetBkMode(hdc, TRANSPARENT);
  305. uFlags = ETO_CLIPPED;
  306. }
  307. if (CALL_LPK(PtiCurrentShared())) {
  308. xxxClientExtTextOutW(hdc, SYSMET(CXBORDER) * TT_XOFFSET,
  309. SYSMET(CYBORDER) * TT_YOFFSET, uFlags, &rc, pttwnd->pstr,
  310. wcslen(pttwnd->pstr), NULL);
  311. } else {
  312. GreExtTextOutW(hdc, SYSMET(CXBORDER) * TT_XOFFSET,
  313. SYSMET(CYBORDER) * TT_YOFFSET, uFlags, &rc, pttwnd->pstr,
  314. wcslen(pttwnd->pstr), NULL);
  315. }
  316. }
  317. /***************************************************************************\
  318. * FindNcHitEx
  319. *
  320. * 12-Sep-96 vadimg created
  321. \***************************************************************************/
  322. int FindNCHitEx(PWND pwnd, int ht, POINT pt)
  323. {
  324. /*
  325. * Bug 263057 joejo
  326. * It seems that pwnd->spmenu can be released and set to null,
  327. * without the WFMPRESENT flag being cleared. Make sure that
  328. * we have a good pwnd->spmenu before continuing.
  329. */
  330. if (ht == HTMENU && pwnd->spmenu && TestWF(pwnd, WFMPRESENT)) {
  331. PMENU spmenu = pwnd->spmenu;
  332. PITEM pitem;
  333. int nItem;
  334. nItem = MNItemHitTest(spmenu, pwnd, pt);
  335. if (nItem >= 0) {
  336. pitem = (PITEM)&spmenu->rgItems[nItem];
  337. switch ((ULONG_PTR)pitem->hbmp) {
  338. case (ULONG_PTR)HBMMENU_SYSTEM:
  339. ht = HTMDISYSMENU;
  340. break;
  341. case (ULONG_PTR)HBMMENU_MBAR_RESTORE:
  342. ht = HTMDIMAXBUTTON;
  343. break;
  344. case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE:
  345. case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE_D:
  346. ht = HTMDIMINBUTTON;
  347. break;
  348. case (ULONG_PTR)HBMMENU_MBAR_CLOSE:
  349. case (ULONG_PTR)HBMMENU_MBAR_CLOSE_D:
  350. ht = HTMDICLOSE;
  351. break;
  352. case (ULONG_PTR)HBMMENU_CALLBACK:
  353. ht = HTERROR;
  354. break;
  355. default:
  356. ht = HTMENUITEM;
  357. break;
  358. }
  359. }
  360. return MAKELONG(ht, nItem);
  361. } else if (ht == HTVSCROLL && TestWF(pwnd, WFVPRESENT)) {
  362. return MAKELONG(HitTestScrollBar(pwnd, TRUE, pt), 1);
  363. } else if (ht == HTHSCROLL && TestWF(pwnd, WFHPRESENT)) {
  364. return MAKELONG(HitTestScrollBar(pwnd, FALSE, pt), 0);
  365. }
  366. return ht;
  367. }
  368. /***************************************************************************\
  369. * KillTooltipTimer
  370. *
  371. * Kill the timer and zero out the timer id.
  372. \***************************************************************************/
  373. void KillTooltipTimer (PTOOLTIPWND pttwnd)
  374. {
  375. UINT uTID = pttwnd->uTID;
  376. if (uTID != 0) {
  377. pttwnd->uTID = 0;
  378. _KillTimer((PWND)pttwnd, uTID);
  379. }
  380. }
  381. /***************************************************************************\
  382. * SetTooltipTimer
  383. *
  384. \***************************************************************************/
  385. void SetTooltipTimer (PTOOLTIPWND pttwnd, UINT uTID, UINT uDelay)
  386. {
  387. KillTooltipTimer(pttwnd);
  388. pttwnd->uTID = uTID;
  389. InternalSetTimer((PWND)pttwnd, uTID, uDelay, NULL, 0);
  390. }
  391. /***************************************************************************\
  392. * xxxResetTooltip
  393. *
  394. * Hide the tooltip, kill the timer, and zero out most of the struct members.
  395. \***************************************************************************/
  396. void xxxResetTooltip(PTOOLTIPWND pttwnd)
  397. {
  398. KillTooltipTimer(pttwnd);
  399. CheckLock(pttwnd);
  400. if (TestWF(pttwnd, WFVISIBLE)) {
  401. PWND spwndMessage;
  402. TL tlpwnd;
  403. xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE |
  404. SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER);
  405. spwndMessage = PWNDMESSAGE(pttwnd);
  406. ThreadLockAlways(spwndMessage, &tlpwnd);
  407. xxxSetParent((PWND)pttwnd, spwndMessage);
  408. ThreadUnlock(&tlpwnd);
  409. }
  410. ZeroTooltip(pttwnd);
  411. pttwnd->head.rpdesk->dwDTFlags &= ~DF_TOOLTIP;
  412. }
  413. /***************************************************************************\
  414. * xxxShowTooltip
  415. *
  416. * Show the tooltip window.
  417. *
  418. * 12-Sep-96 vadimg created
  419. \***************************************************************************/
  420. BOOL xxxShowTooltip(PTOOLTIPWND pttwnd)
  421. {
  422. SIZE size;
  423. POINT pt;
  424. DWORD dwFlags;
  425. CheckLock(pttwnd);
  426. if (pttwnd->pstr == NULL)
  427. return FALSE;
  428. if (pttwnd->pstr == gszCAPTIONTOOLTIP) {
  429. PWND pwnd = PtiCurrent()->rpdesk->spwndTrack;
  430. /*
  431. * The window text might have changed in callbacks, retrieve it now
  432. */
  433. if (pwnd && TestWF(pwnd, WEFTRUNCATEDCAPTION) && pwnd->strName.Length) {
  434. wcsncpycch(gszCAPTIONTOOLTIP, pwnd->strName.Buffer, CAPTIONTOOLTIPLEN-1);
  435. gszCAPTIONTOOLTIP[CAPTIONTOOLTIPLEN-1] = 0;
  436. } else {
  437. return FALSE;
  438. }
  439. }
  440. xxxTooltipGetSize(pttwnd, &size);
  441. if (!TooltipGetPosition(pttwnd, &size, &pt)) {
  442. return FALSE;
  443. }
  444. dwFlags = SWP_CREATESPB | SWP_SHOWWINDOW | SWP_NOACTIVATE;
  445. if (TestEffectUP(TOOLTIPANIMATION)) {
  446. dwFlags |= SWP_NOREDRAW;
  447. }
  448. xxxSetWindowPos((PWND)pttwnd, PWND_TOP, pt.x, pt.y,
  449. size.cx, size.cy, dwFlags);
  450. return TRUE;
  451. }
  452. /***************************************************************************\
  453. * xxxTooltipHandleTimer
  454. *
  455. * 12-Sep-96 vadimg created
  456. \***************************************************************************/
  457. BOOL xxxTooltipHandleTimer(PTOOLTIPWND pttwnd, UINT uTID)
  458. {
  459. BOOL fReturn = TRUE;
  460. switch(uTID) {
  461. case TTT_SHOW: {
  462. /*
  463. * Move the tooltip window to the desktop so it can
  464. * be shown. Then show it.
  465. */
  466. PWND pwndDesktop = PWNDDESKTOP(pttwnd);
  467. TL tlpwnd;
  468. ThreadLockAlways(pwndDesktop, &tlpwnd);
  469. if (xxxSetParent((PWND)pttwnd, pwndDesktop) == NULL) {
  470. fReturn = FALSE;
  471. } else {
  472. fReturn = xxxShowTooltip(pttwnd);
  473. }
  474. ThreadUnlock(&tlpwnd);
  475. break;
  476. }
  477. case TTT_ANIMATE:
  478. /*
  479. * If animation is completed, set timer to hide
  480. */
  481. if (TooltipAnimate(pttwnd)) {
  482. SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay);
  483. }
  484. break;
  485. case TTT_HIDE:
  486. /*
  487. * Hide it
  488. */
  489. xxxResetTooltip(pttwnd);
  490. break;
  491. }
  492. return fReturn;
  493. }
  494. /***************************************************************************\
  495. * xxxTooltipWndProc
  496. *
  497. * The actual WndProc for the tooltip window.
  498. *
  499. * 12-Sep-96 vadimg created
  500. \***************************************************************************/
  501. LRESULT xxxTooltipWndProc(PWND pwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  502. {
  503. PAINTSTRUCT ps;
  504. PTOOLTIPWND pttwnd;
  505. CheckLock(pwnd);
  506. VALIDATECLASSANDSIZE(pwnd, uMsg, wParam, lParam, FNID_TOOLTIP, WM_NCCREATE);
  507. pttwnd = (PTOOLTIPWND)pwnd;
  508. switch(uMsg) {
  509. case WM_TIMER:
  510. xxxTooltipHandleTimer(pttwnd, (UINT)wParam);
  511. break;
  512. case WM_PAINT:
  513. xxxBeginPaint(pwnd, &ps);
  514. xxxTooltipRender(pttwnd, ps.hdc);
  515. xxxEndPaint(pwnd, &ps);
  516. break;
  517. case WM_PRINTCLIENT:
  518. xxxTooltipRender(pttwnd, (HDC)wParam);
  519. break;
  520. case WM_ERASEBKGND:
  521. break;
  522. case WM_NCCREATE:
  523. InitTooltipDelay(pttwnd);
  524. InitTooltipAnimation(pttwnd);
  525. goto CallDWP;
  526. case WM_NCDESTROY:
  527. CleanupTooltipAnimation(pttwnd);
  528. GETPDESK(pttwnd)->dwDTFlags &= ~DF_TOOLTIP;
  529. goto CallDWP;
  530. case WM_WINDOWPOSCHANGED:
  531. if (((LPWINDOWPOS)lParam)->flags & SWP_SHOWWINDOW) {
  532. HDC hdc;
  533. int cx;
  534. int cy;
  535. if (!TestEffectUP(TOOLTIPANIMATION)) {
  536. SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay);
  537. goto CallDWP;
  538. }
  539. hdc = NULL;
  540. cx = pttwnd->rcWindow.right - pttwnd->rcWindow.left;
  541. cy = pttwnd->rcWindow.bottom - pttwnd->rcWindow.top;
  542. /*
  543. * At this point we're sure that the window is showing and the size
  544. * has been changed and we're in the context of the desktop thread.
  545. */
  546. if (TestALPHA(TOOLTIPFADE)) {
  547. hdc = CreateFade((PWND)pttwnd, NULL, CMS_TOOLTIP,
  548. FADE_SHOW | FADE_TOOLTIP);
  549. } else {
  550. if (CreateTooltipBitmap(pttwnd, cx, cy)) {
  551. hdc = pttwnd->hdcMem;
  552. }
  553. }
  554. if (hdc == NULL) {
  555. SetTooltipTimer(pttwnd, TTT_HIDE, 0);
  556. goto CallDWP;
  557. }
  558. xxxSendMessage((PWND)pttwnd, WM_PRINT, (WPARAM)hdc,
  559. PRF_CLIENT | PRF_NONCLIENT | PRF_CHILDREN | PRF_ERASEBKGND);
  560. /*
  561. * Start animation timer
  562. */
  563. if (TestFadeFlags(FADE_TOOLTIP)) {
  564. StartFade();
  565. SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay);
  566. } else {
  567. pttwnd->dwAnimStart = NtGetTickCount();
  568. SetTooltipTimer(pttwnd, TTT_ANIMATE, TT_ANIMATEDELAY);
  569. }
  570. } else if (((LPWINDOWPOS)lParam)->flags & SWP_HIDEWINDOW) {
  571. if (TestFadeFlags(FADE_TOOLTIP)) {
  572. StopFade();
  573. } else {
  574. DestroyTooltipBitmap(pttwnd);
  575. }
  576. }
  577. goto CallDWP;
  578. default:
  579. CallDWP:
  580. return xxxDefWindowProc(pwnd, uMsg, wParam, lParam);
  581. }
  582. return 0;
  583. }
  584. /***************************************************************************\
  585. * IsTrackedHittest
  586. *
  587. * Should we be tracking this hittest code? Return the track string if yes.
  588. * If on caption returning the window strName.Buffer could
  589. * make the system bugcheck if there is a SetWindowText in the callback.
  590. \***************************************************************************/
  591. LPWSTR IsTooltipHittest(PWND pwnd, UINT ht)
  592. {
  593. switch (ht) {
  594. case HTMINBUTTON:
  595. if (TestWF(pwnd, WFMINBOX)) {
  596. return (TestWF(pwnd, WFMINIMIZED)) ? gszRESUP : gszMIN;
  597. }
  598. break;
  599. case HTMAXBUTTON:
  600. if (TestWF(pwnd, WFMAXBOX)) {
  601. return (TestWF(pwnd, WFMAXIMIZED)) ? gszRESDOWN : gszMAX;
  602. }
  603. break;
  604. case HTCLOSE:
  605. case HTMDICLOSE:
  606. return gszSCLOSE;
  607. /* Commented out due to TandyT ...
  608. case HTSYSMENU:
  609. case HTMDISYSMENU:
  610. return gszSMENU;
  611. */
  612. case HTHELP:
  613. return gszHELP;
  614. case HTMDIMINBUTTON:
  615. return gszMIN;
  616. case HTMDIMAXBUTTON:
  617. return gszRESDOWN;
  618. case HTCAPTION:
  619. /*
  620. * We only show the caption tooltip if the window text
  621. * doesn't fit entirely on the caption. We will fill
  622. * gszCAPTIONTOOLTIP right before showing the text
  623. */
  624. if (TestWF(pwnd, WEFTRUNCATEDCAPTION)) {
  625. return gszCAPTIONTOOLTIP;
  626. }
  627. break;
  628. default:
  629. break;
  630. }
  631. return NULL;
  632. }
  633. /***************************************************************************\
  634. * xxxHotTrackMenu
  635. *
  636. * Hot-track a menu item in the menu bar.
  637. \***************************************************************************/
  638. BOOL xxxHotTrackMenu(PWND pwnd, UINT nItem, BOOL fDraw)
  639. {
  640. PMENU pmenu = pwnd->spmenu;
  641. PITEM pItem;
  642. HDC hdc;
  643. UINT oldAlign;
  644. TL tlpmenu;
  645. CheckLock(pwnd);
  646. /*
  647. * The window may have lied about the hit-test code on
  648. * WM_NCHITTEST. Make sure it does indeed have a menu.
  649. */
  650. if (!TestWF(pwnd, WFMPRESENT) || pmenu == NULL)
  651. return FALSE;
  652. if (nItem >= pmenu->cItems) {
  653. RIPMSG0(RIP_WARNING, "xxxHotTrackMenu: menu too large");
  654. return FALSE;
  655. }
  656. pItem = &pmenu->rgItems[nItem];
  657. /*
  658. * Make sure we draw on the right spot
  659. */
  660. ThreadLock(pmenu, &tlpmenu);
  661. xxxMNRecomputeBarIfNeeded(pwnd, pmenu);
  662. ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&tlpmenu, TRUE);
  663. if (fDraw) {
  664. if (TestMFS(pItem, MF_GRAYED)) {
  665. ThreadUnlock(&tlpmenu);
  666. return FALSE;
  667. }
  668. SetMFS(pItem, MFS_HOTTRACK);
  669. } else {
  670. ClearMFS(pItem, MFS_HOTTRACK);
  671. }
  672. hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE);
  673. GreSelectBrush(hdc, SYSHBR(MENUTEXT));
  674. GreSelectFont(hdc, ghMenuFont);
  675. oldAlign = GreGetTextAlign(hdc);
  676. if (pmenu->rgItems && TestMFT(pmenu->rgItems, MFT_RIGHTORDER))
  677. GreSetTextAlign(hdc, oldAlign | TA_RTLREADING);
  678. /*
  679. * When the item is not owner draw, xxxDrawMenuItem does not
  680. * call back and does not leave the critical section.
  681. */
  682. xxxDrawMenuItem(hdc, pmenu, pItem, 0);
  683. GreSetTextAlign(hdc, oldAlign);
  684. ThreadUnlock(&tlpmenu);
  685. _ReleaseDC(hdc);
  686. return TRUE;
  687. }
  688. /***************************************************************************\
  689. * HotTrackCaption
  690. *
  691. * Hot-track a caption button.
  692. \***************************************************************************/
  693. #ifdef COLOR_HOTTRACKING
  694. BOOL xxxHotTrackCaption(PWND pwnd, int ht, BOOL fDraw)
  695. {
  696. DWORD dwWhere;
  697. int x, y;
  698. WORD bm, cmd;
  699. RECT rcBtn;
  700. HDC hdc;
  701. CheckLock(pwnd);
  702. if (!TestWF(pwnd, WFCPRESENT))
  703. return FALSE;
  704. dwWhere = xxxCalcCaptionButton(pwnd, ht, &cmd, &rcBtn, &bm);
  705. x = GET_X_LPARAM(dwWhere);
  706. y = GET_Y_LPARAM(dwWhere);
  707. if (!cmd)
  708. return FALSE;
  709. hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE);
  710. BitBltSysBmp(hdc, x, y, bm + (fDraw ? DOBI_HOT : 0));
  711. _ReleaseDC(hdc);
  712. return TRUE;
  713. }
  714. #endif // COLOR_HOTTRACKING
  715. /***************************************************************************\
  716. * xxxHotTrack
  717. *
  718. \***************************************************************************/
  719. BOOL xxxHotTrack(PWND pwnd, int htEx, BOOL fDraw)
  720. {
  721. int ht = LOWORD(htEx);
  722. CheckLock(pwnd);
  723. switch(ht) {
  724. #ifdef COLOR_HOTTRACKING
  725. case HTMINBUTTON:
  726. case HTMAXBUTTON:
  727. case HTHELP:
  728. case HTCLOSE:
  729. return xxxHotTrackCaption(pwnd, ht, fDraw);
  730. case HTSCROLLUP:
  731. case HTSCROLLDOWN:
  732. case HTSCROLLUPPAGE:
  733. case HTSCROLLDOWNPAGE:
  734. case HTSCROLLTHUMB:
  735. return xxxHotTrackSB(pwnd, htEx, fDraw);
  736. case HTMDIMINBUTTON:
  737. case HTMDIMAXBUTTON:
  738. case HTMDICLOSE:
  739. #endif // COLOR_HOTTRACKING
  740. case HTMENUITEM:
  741. return xxxHotTrackMenu(pwnd, HIWORD(htEx), fDraw);
  742. }
  743. return FALSE;
  744. }
  745. /***************************************************************************\
  746. * xxxCreateTooltip
  747. *
  748. * Call this to show a new tooltip with a new string and delay.
  749. \***************************************************************************/
  750. BOOL xxxCreateTooltip(PTOOLTIPWND pttwnd, LPWSTR pstr)
  751. {
  752. CheckLock(pttwnd);
  753. /*
  754. * Store new text
  755. */
  756. pttwnd->pstr = pstr;
  757. /*
  758. * If already visible, hide it and show it in new place.
  759. * Otherwise, set timer to show.
  760. */
  761. if (TestWF(pttwnd, WFVISIBLE)) {
  762. xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE |
  763. SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
  764. return xxxShowTooltip(pttwnd);
  765. } else {
  766. SetTooltipTimer(pttwnd, TTT_SHOW, pttwnd->dwShowDelay);
  767. }
  768. return TRUE;
  769. }
  770. /***************************************************************************\
  771. * xxxTrackMouseMove
  772. *
  773. * This is the entry point for the system tooltips and hot-tracking.
  774. *
  775. * 12-Sep-96 vadimg created
  776. \***************************************************************************/
  777. void xxxTrackMouseMove(PWND pwnd, int htEx, UINT message)
  778. {
  779. BOOL fNewpwndTrack;
  780. DWORD dwDTCancel = 0;
  781. TL tlpwnd;
  782. LPWSTR pstr;
  783. PDESKTOP pdesk = PtiCurrent()->rpdesk;
  784. PTHREADINFO ptiTrack;
  785. #if DBG
  786. /*
  787. * Let's warn if this function gets reenterd so we can make sure
  788. * nothing bad will follow. This should be a rare situation.
  789. * Look in gptiReEntered to find out who is already here.
  790. */
  791. static UINT gcReEntered = 0;
  792. static PTHREADINFO gptiReEntered;
  793. if(gcReEntered++ != 0){
  794. RIPMSG2(RIP_WARNING, "Reentered xxxTrackMouseMove; previous thread was %#p, current thread is %#p", gptiReEntered, PtiCurrent());
  795. }
  796. gptiReEntered = PtiCurrent();
  797. CheckLock(pwnd);
  798. /*
  799. * We must be on an interactive window station.
  800. */
  801. if (pdesk->rpwinstaParent != NULL &&
  802. pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO) {
  803. RIPMSG0(RIP_ERROR, "Can't use tooltips on non-interactive winsta");
  804. }
  805. {
  806. static POINT pt = {0, 0};
  807. #ifdef UNDONE
  808. /*
  809. * We might have taken a guess on the hit test (see FindNCHitEx)
  810. * so if we're at the same point and same window, something
  811. * might be fishy
  812. */
  813. if ((pt.x == gpsi->ptCursor.x)
  814. && (pt.y == gpsi->ptCursor.y)
  815. && (pdesk->spwndTrack == pwnd)) {
  816. RIPMSG1(RIP_WARNING, "xxxTrackMouseMove: Same point & window. %#p", pwnd);
  817. }
  818. #endif
  819. /*
  820. * Something is supposed to have changed or we're wasting time
  821. */
  822. UserAssert((pt.x != gpsi->ptCursor.x)
  823. || (pt.y != gpsi->ptCursor.y)
  824. || (pdesk->spwndTrack != pwnd)
  825. || (pdesk->htEx != htEx)
  826. || (message != WM_MOUSEMOVE));
  827. /*
  828. * Remember last tracked point
  829. */
  830. pt = gpsi->ptCursor;
  831. }
  832. /*
  833. * pwnd is supposed to be on the current thread and queue
  834. */
  835. UserAssert(PtiCurrent() == GETPTI(pwnd));
  836. UserAssert(PtiCurrent()->pq == GETPTI(pwnd)->pq);
  837. #endif
  838. /*
  839. * Have we switched windows?
  840. */
  841. fNewpwndTrack = (pdesk->spwndTrack != pwnd);
  842. /*
  843. * If no tracking is taking place, just go set the new
  844. * tracking state
  845. */
  846. if (!(pdesk->dwDTFlags & DF_MOUSEMOVETRK)) {
  847. goto SetNewState;
  848. }
  849. /*
  850. * Potentially while we leave the critical section below in
  851. * xxxCancelMouseMoveTracking, spwndTrack could be destroyed and unlocked
  852. * and then we go and create the tooltip. This would mean that
  853. * DF_TOOLTIPACTIVE (part of DF_MOUSEMOVETRK test above) would be set,
  854. * but pdesk->spwndTrack would be NULL and we can AV dereferencing
  855. * pdesk->spwndTrack below. Prevent this by making the check here.
  856. */
  857. if (pdesk->spwndTrack == NULL) {
  858. goto SetNewState;
  859. }
  860. /*
  861. * Nuke hottracking and deactivate tooltip state, if any.
  862. * Do it sychronously if we're tracking on the current queue;
  863. * Otherwise, post an event and let it happen later.
  864. */
  865. ptiTrack = GETPTI(pdesk->spwndTrack);
  866. if (PtiCurrent()->pq == ptiTrack->pq) {
  867. dwDTCancel |= DF_HOTTRACKING;
  868. } else if (pdesk->dwDTFlags & (DF_HOTTRACKING | DF_TOOLTIPACTIVE)) {
  869. PostEventMessage(ptiTrack, ptiTrack->pq,
  870. QEVENT_CANCELMOUSEMOVETRK,
  871. pdesk->spwndTrack,
  872. pdesk->dwDTFlags,
  873. pdesk->htEx, DF_HOTTRACKING);
  874. /*
  875. * Paranoid assertion. If we're switching queues, we must
  876. * be switching windows. Did we just go through
  877. * ReattachThreads?
  878. */
  879. UserAssert(pwnd != pdesk->spwndTrack);
  880. pdesk->dwDTFlags &= ~(DF_HOTTRACKING | DF_TOOLTIPACTIVE);
  881. }
  882. /*
  883. * If we're on the client area or the user clicked,
  884. * nuke the tooltip (if any).
  885. * Since we might want to re-show the tooltip, we don't nuke it
  886. * now if we swichted windows (we'll nuke it later if needed)
  887. */
  888. if ((htEx == HTCLIENT) || (message != WM_MOUSEMOVE)) {
  889. dwDTCancel |= DF_TOOLTIPACTIVE;
  890. }
  891. /*
  892. * If we switched windows or crossed client/nonclinet boundaries,
  893. * end track mouse leave/hover.
  894. */
  895. if (fNewpwndTrack || ((pdesk->htEx == HTCLIENT) ^ (htEx == HTCLIENT))) {
  896. dwDTCancel |= DF_TRACKMOUSEEVENT;
  897. }
  898. /*
  899. * Cancel whatever is active and needs to go away
  900. */
  901. ThreadLockAlways(pdesk->spwndTrack, &tlpwnd);
  902. xxxCancelMouseMoveTracking(pdesk->dwDTFlags,
  903. pdesk->spwndTrack,
  904. pdesk->htEx,
  905. dwDTCancel);
  906. ThreadUnlock(&tlpwnd);
  907. pdesk->dwDTFlags &= ~dwDTCancel;
  908. SetNewState:
  909. /*
  910. * Hottracking/tooltip on mouse move if on NC hitest and enabled
  911. */
  912. if ((htEx != HTCLIENT) && (message == WM_MOUSEMOVE) && TestEffectUP(HOTTRACKING)) {
  913. /*
  914. * Hottrack the new hit test area
  915. */
  916. if (xxxHotTrack(pwnd, htEx, TRUE)) {
  917. pdesk->dwDTFlags |= DF_HOTTRACKING;
  918. }
  919. /*
  920. * Remove/set the tool tip.
  921. * We always do this synchronously because it doesn't mess
  922. * with pwnd's or spwnTrack's queue
  923. */
  924. if ((pstr = IsTooltipHittest(pwnd, LOWORD(htEx))) != NULL) {
  925. PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip;
  926. ThreadLockAlways(pttwnd, &tlpwnd);
  927. if (xxxCreateTooltip(pttwnd, pstr)) {
  928. pdesk->dwDTFlags |= DF_TOOLTIP;
  929. }
  930. ThreadUnlock(&tlpwnd);
  931. } else {
  932. PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip;
  933. ThreadLockAlways(pttwnd, &tlpwnd);
  934. xxxResetTooltip(pttwnd);
  935. ThreadUnlock(&tlpwnd);
  936. }
  937. } /* if (htEx != HTCLIENT) */
  938. ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&pwnd, TRUE);
  939. /*
  940. * Update new track window if needed.
  941. */
  942. if (fNewpwndTrack) {
  943. PWND pwndActivate;
  944. Lock(&pdesk->spwndTrack, pwnd);
  945. /*
  946. * Active window tracking.
  947. * If there is non-zero timeout, get the window we're supposed to activate
  948. * and set the timer. Otherwise, set the queue flag so
  949. * xxxActiveWindowTracking can do its thing.
  950. */
  951. if ((message == WM_MOUSEMOVE) && TestUP(ACTIVEWINDOWTRACKING)) {
  952. if (UP(ACTIVEWNDTRKTIMEOUT) != 0) {
  953. pwndActivate = GetActiveTrackPwnd(pwnd, NULL);
  954. if (pwndActivate != NULL) {
  955. InternalSetTimer(pwndActivate, IDSYS_WNDTRACKING,
  956. UP(ACTIVEWNDTRKTIMEOUT),
  957. xxxSystemTimerProc, TMRF_SYSTEM);
  958. }
  959. } else {
  960. PtiCurrent()->pq->QF_flags |= QF_ACTIVEWNDTRACKING;
  961. } /* if (TestUP(ACTIVEWNDTRKZORDER)) */
  962. } /* if (TestUP(ACTIVEWINDOWTRACKING)) */
  963. }
  964. /*
  965. * Save new hit test code
  966. */
  967. pdesk->htEx = htEx;
  968. #if DBG
  969. --gcReEntered;
  970. #endif
  971. }
  972. /***************************************************************************\
  973. * xxxCancelMouseMoveTracking
  974. *
  975. * History
  976. * 12/07/96 GerardoB Created
  977. \***************************************************************************/
  978. void xxxCancelMouseMoveTracking (DWORD dwDTFlags, PWND pwndTrack, int htEx, DWORD dwDTCancel)
  979. {
  980. CheckLock(pwndTrack);
  981. /*
  982. * Hottracking
  983. */
  984. if ((dwDTFlags & DF_HOTTRACKING) && (dwDTCancel & DF_HOTTRACKING)) {
  985. /*
  986. * The current state must be owned by the current queue.
  987. * Otherwise, we're about to do an inter-queue cancelation.
  988. */
  989. UserAssert(PtiCurrent()->pq == GETPTI(pwndTrack)->pq);
  990. xxxHotTrack(pwndTrack, htEx, FALSE);
  991. }
  992. /*
  993. * Tooltips
  994. */
  995. if ((dwDTFlags & DF_TOOLTIPSHOWING) && (dwDTCancel & DF_TOOLTIP)) {
  996. PTOOLTIPWND pttwnd = (PTOOLTIPWND)PWNDTOOLTIP(pwndTrack);
  997. TL tlpwnd;
  998. ThreadLockAlways(pttwnd, &tlpwnd);
  999. xxxResetTooltip(pttwnd);
  1000. ThreadUnlock(&tlpwnd);
  1001. }
  1002. /*
  1003. * Mouse Leave
  1004. */
  1005. if ((dwDTFlags & DF_TRACKMOUSELEAVE) && (dwDTCancel & DF_TRACKMOUSELEAVE)) {
  1006. _PostMessage(pwndTrack,
  1007. ((htEx == HTCLIENT) ? WM_MOUSELEAVE : WM_NCMOUSELEAVE),
  1008. 0, 0);
  1009. }
  1010. /*
  1011. * Mouse Hover
  1012. */
  1013. if ((dwDTFlags & DF_TRACKMOUSEHOVER) && (dwDTCancel & DF_TRACKMOUSEHOVER)) {
  1014. _KillSystemTimer(pwndTrack, IDSYS_MOUSEHOVER);
  1015. }
  1016. }