Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

tooltips.c

Go to the documentation of this file.
00001 /****************************** Module Header ******************************\ 00002 * Module Name: tooltips.c 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Implements system tooltips. 00007 * 00008 * History: 00009 * 25-Aug-1996 vadimg created 00010 \***************************************************************************/ 00011 00012 #include "precomp.h" 00013 #pragma hdrstop 00014 00015 #define TT_XOFFSET 2 00016 #define TT_YOFFSET 1 00017 #define TTT_SHOW 1 00018 #define TTT_HIDE 2 00019 #define TTT_ANIMATE 3 00020 #define TT_ANIMATEDELAY 20 00021 00022 #define TTF_POSITIVE 0x00000001 00023 00024 #define bitsizeof(x) (sizeof(x) * 8) 00025 00026 UINT MNItemHitTest(PMENU, PWND, POINT); 00027 HANDLE NtGdiGetDCObject(HDC, int); 00028 LONG GreGetBitmapBits(HBITMAP, ULONG, PBYTE, PLONG); 00029 DWORD CalcCaptionButton(PWND pwnd, int hit, LPWORD pcmd, LPRECT prcBtn, LPWORD pbm); 00030 int HitTestScrollBar(PWND pwnd, int ht, POINT pt); 00031 BOOL xxxHotTrackSB(PWND pwnd, int htEx, BOOL fDraw); 00032 00033 __inline void ZeroTooltip(PTOOLTIPWND pttwnd) 00034 { 00035 RtlZeroMemory((PBYTE)pttwnd + (sizeof(TOOLTIPWND) - sizeof(TOOLTIP)), 00036 sizeof(TOOLTIP)); 00037 } 00038 00039 /***************************************************************************\ 00040 * GetTooltipDC 00041 * 00042 * 2/3/1998 vadimg created 00043 \***************************************************************************/ 00044 00045 HDC GetTooltipDC(PTOOLTIPWND pttwnd) 00046 { 00047 HDC hdc = _GetDCEx((PWND)pttwnd, NULL, DCX_WINDOW | DCX_CACHE | 00048 DCX_USESTYLE); 00049 00050 if (hdc == NULL) 00051 return NULL; 00052 00053 GreSelectFont(hdc, ghStatusFont); 00054 return hdc; 00055 } 00056 00057 /***************************************************************************\ 00058 * InitTooltipAnimation 00059 * 00060 * Creates memory bitmap and DC for use by system tooltips. Gets the screen 00061 * DC used throughout. 00062 * 00063 * 12-Sep-96 vadimg created 00064 \***************************************************************************/ 00065 00066 void InitTooltipAnimation(PTOOLTIPWND pttwnd) 00067 { 00068 HDC hdc = GetTooltipDC(pttwnd); 00069 00070 if ((pttwnd->hdcMem = GreCreateCompatibleDC(hdc)) == NULL) { 00071 return; 00072 } 00073 _ReleaseDC(hdc); 00074 GreSetDCOwner(pttwnd->hdcMem, OBJECT_OWNER_PUBLIC); 00075 } 00076 00077 /***************************************************************************\ 00078 * DestroyTooltipBitmap 00079 * 00080 \***************************************************************************/ 00081 00082 void DestroyTooltipBitmap(PTOOLTIPWND pttwnd) 00083 { 00084 if (pttwnd->hbmMem == NULL) 00085 return; 00086 00087 GreSelectBitmap(pttwnd->hdcMem, GreGetStockObject(PRIV_STOCK_BITMAP)); 00088 GreDeleteObject(pttwnd->hbmMem); 00089 pttwnd->hbmMem = NULL; 00090 } 00091 00092 /***************************************************************************\ 00093 * CreateTooltipBitmap 00094 * 00095 \***************************************************************************/ 00096 00097 BOOL CreateTooltipBitmap(PTOOLTIPWND pttwnd, UINT cx, UINT cy) 00098 { 00099 HDC hdc; 00100 00101 if (pttwnd->hdcMem == NULL) { 00102 RIPMSG0(RIP_WARNING, "CreateTooltipBitmap: pttwnd->hdcMem is NULL"); 00103 return FALSE; 00104 } 00105 00106 DestroyTooltipBitmap(pttwnd); 00107 00108 hdc = GetTooltipDC(pttwnd); 00109 pttwnd->hbmMem = GreCreateCompatibleBitmap(hdc, cx, cy); 00110 _ReleaseDC(hdc); 00111 00112 if (pttwnd->hbmMem == NULL) { 00113 RIPMSG0(RIP_WARNING, "CreateTooltipBitmap: hbmMem is NULL"); 00114 return FALSE; 00115 } 00116 GreSelectBitmap(pttwnd->hdcMem, pttwnd->hbmMem); 00117 return TRUE; 00118 } 00119 00120 /***************************************************************************\ 00121 * CleanupTooltipAnimation 00122 * 00123 * Deletes memory bitmap and DC for use by system tooltips. Release the 00124 * screen DC. 00125 * 00126 * 12-Sep-96 vadimg created 00127 \***************************************************************************/ 00128 00129 void CleanupTooltipAnimation(PTOOLTIPWND pttwnd) 00130 { 00131 DestroyTooltipBitmap(pttwnd); 00132 00133 if (pttwnd->hdcMem != NULL) { 00134 GreSetDCOwner(pttwnd->hdcMem, OBJECT_OWNER_CURRENT); 00135 GreDeleteDC(pttwnd->hdcMem); 00136 } 00137 } 00138 00139 /***************************************************************************\ 00140 * TooltipAnimate 00141 * 00142 * Perform one frame of window animation. Just a simplified version of 00143 * the AnimateWindow API. 00144 * 00145 * 12-Sep-96 vadimg created 00146 \***************************************************************************/ 00147 00148 BOOL TooltipAnimate(PTOOLTIPWND pttwnd) 00149 { 00150 int y, yMem, yReal, ny, iy, cx, cy; 00151 DWORD dwElapsed; 00152 HDC hdc; 00153 BOOL fRet = FALSE; 00154 00155 if (pttwnd->pstr == NULL) 00156 return TRUE; 00157 00158 hdc = GetTooltipDC(pttwnd); 00159 cx = pttwnd->rcWindow.right - pttwnd->rcWindow.left; 00160 cy = pttwnd->rcWindow.bottom - pttwnd->rcWindow.top; 00161 dwElapsed = NtGetTickCount() - pttwnd->dwAnimStart; 00162 iy = MultDiv(cy, dwElapsed, CMS_TOOLTIP); 00163 00164 if (dwElapsed > CMS_TOOLTIP || iy == cy) { 00165 GreBitBlt(hdc, 0, 0, cx, cy, pttwnd->hdcMem, 0, 0, SRCCOPY | NOMIRRORBITMAP, 0); 00166 fRet = TRUE; 00167 goto Cleanup; 00168 } else if (pttwnd->iyAnim == iy) { 00169 goto Cleanup; 00170 } 00171 00172 if (pttwnd->dwFlags & TTF_POSITIVE) { 00173 y = 0; 00174 ny = 0; 00175 } else { 00176 y = cy; 00177 ny = -1; 00178 } 00179 00180 yReal = y + ny * iy; 00181 yMem = (pttwnd->dwFlags & TTF_POSITIVE) ? cy - iy : 0; 00182 pttwnd->iyAnim = iy; 00183 00184 GreBitBlt(hdc, 0, yReal, cx, iy, pttwnd->hdcMem, 0, yMem, SRCCOPY | NOMIRRORBITMAP, 0); 00185 00186 Cleanup: 00187 _ReleaseDC(hdc); 00188 return fRet; 00189 } 00190 00191 /***************************************************************************\ 00192 * GetCursorHeight 00193 * 00194 * This is tricky. We need to get the actual cursor size from the hotspot 00195 * down to the end. There is no API in windows to do this, CYCURSOR is 00196 * just the metric for the size of the bitmap, the cursor starts at the top 00197 * of the bitmap and may be smaller than the bitmap itself. 00198 * 00199 * 12-Sep-96 vadimg ported from common controls 00200 \***************************************************************************/ 00201 00202 int GetCursorHeight(void) 00203 { 00204 int iAnd, iXor, dy = 16; 00205 WORD wMask[128]; 00206 ICONINFO ii; 00207 BITMAP bm; 00208 PCURSOR pcur; 00209 long lOffset = 0; 00210 00211 if ((pcur = PtiCurrent()->pq->spcurCurrent) == NULL) { 00212 return dy; 00213 } 00214 00215 if (!_InternalGetIconInfo(pcur, &ii, NULL, NULL, NULL, FALSE)) { 00216 return dy; 00217 } 00218 00219 if (!GreExtGetObjectW(ii.hbmMask, sizeof(bm), (LPSTR)&bm)) { 00220 goto Bail; 00221 } 00222 00223 /* 00224 * Use the AND mask to get the cursor height if the XOR mask is there. 00225 */ 00226 if (!GreGetBitmapBits(ii.hbmMask, sizeof(wMask), (BYTE*)wMask, &lOffset)) { 00227 goto Bail; 00228 } 00229 00230 iAnd = (int)(bm.bmWidth * bm.bmHeight / bitsizeof(WORD)); 00231 00232 if (ii.hbmColor == NULL) { 00233 /* 00234 * if no color (XOR) bitmap, then the hbmMask is a double height bitmap 00235 * with the cursor and the mask stacked. 00236 */ 00237 00238 iXor = iAnd - 1; 00239 iAnd /= 2; 00240 } else { 00241 iXor = 0; 00242 } 00243 00244 if (iAnd >= sizeof(wMask)) { 00245 iAnd = sizeof(wMask) - 1; 00246 } 00247 00248 if (iXor >= sizeof(wMask)) { 00249 iXor = 0; 00250 } 00251 00252 for (iAnd--; iAnd >= 0; iAnd--) { 00253 if ((iXor != 0 && wMask[iXor--] != 0) || wMask[iAnd] != 0xFFFF) { 00254 break; 00255 } 00256 } 00257 00258 /* 00259 * Compute the distance between the pointer's lowest point and hotspot. 00260 */ 00261 dy = (iAnd + 1) * bitsizeof(WORD) / (int)bm.bmWidth - (int)ii.yHotspot; 00262 00263 Bail: 00264 if (ii.hbmColor) { 00265 GreDeleteObject(ii.hbmColor); 00266 } 00267 00268 if (ii.hbmMask) { 00269 GreDeleteObject(ii.hbmMask); 00270 } 00271 00272 return dy; 00273 } 00274 00275 /***************************************************************************\ 00276 * TooltipGetPosition 00277 * 00278 * Get the tooltip position on the screen taking into account the size of 00279 * the tooltip and the screen. The TTF_POSITIVE flag determines if positive 00280 * or negative animation is used. 00281 * 00282 * 12-Sep-96 vadimg created 00283 \***************************************************************************/ 00284 00285 void TooltipGetPosition(PTOOLTIPWND pttwnd, SIZE *psize, POINT *ppt) 00286 { 00287 PMONITOR pMonitor; 00288 00289 *ppt = gpsi->ptCursor; 00290 pMonitor = _MonitorFromPoint(*ppt, MONITOR_DEFAULTTONULL); 00291 UserAssert(pMonitor); 00292 00293 if (ppt->y + psize->cy >= pMonitor->rcMonitor.bottom) { 00294 ppt->y = ppt->y - psize->cy; 00295 pttwnd->dwFlags &= ~TTF_POSITIVE; 00296 } else { 00297 ppt->y += GetCursorHeight(); 00298 pttwnd->dwFlags |= TTF_POSITIVE; 00299 } 00300 00301 if (ppt->x + psize->cx >= pMonitor->rcMonitor.right) { 00302 ppt->x = pMonitor->rcMonitor.right - psize->cx; 00303 } 00304 00305 if (ppt->x < pMonitor->rcMonitor.left) { 00306 ppt->x = pMonitor->rcMonitor.left; 00307 } 00308 } 00309 00310 /***************************************************************************\ 00311 * TooltipGetSize 00312 * 00313 * Estimate the size of the tooltip window based on the size of the text. 00314 * 00315 * 12-Sep-96 vadimg created 00316 \***************************************************************************/ 00317 00318 void TooltipGetSize(PTOOLTIPWND pttwnd, SIZE *psize) 00319 { 00320 HDC hdc = GetTooltipDC(pttwnd); 00321 GreGetTextExtentW(hdc, pttwnd->pstr, wcslen(pttwnd->pstr), 00322 psize, GGTE_WIN3_EXTENT); 00323 _ReleaseDC(hdc); 00324 psize->cx += SYSMET(CXEDGE) + 2 * SYSMET(CXBORDER) * TT_XOFFSET; 00325 psize->cy += SYSMET(CYEDGE) + 2 * SYSMET(CYBORDER) * TT_YOFFSET; 00326 } 00327 00328 /***************************************************************************\ 00329 * TooltipRender 00330 * 00331 * Render the tooltip window into the provided DC. 00332 * 00333 * 12-Sep-96 vadimg created 00334 \***************************************************************************/ 00335 00336 void TooltipRender(PTOOLTIPWND pttwnd, HDC hdc) 00337 { 00338 COLORREF crBk; 00339 UINT uFlags; 00340 RECT rc; 00341 00342 if (pttwnd->pstr == NULL) 00343 return; 00344 00345 GreSelectFont(hdc, ghStatusFont); 00346 GreSetTextColor(hdc, gpsi->argbSystem[COLOR_INFOTEXT]); 00347 crBk = gpsi->argbSystem[COLOR_INFOBK]; 00348 00349 CopyOffsetRect(&rc, &pttwnd->rcClient, -pttwnd->rcClient.left, 00350 -pttwnd->rcClient.top); 00351 00352 /* 00353 * We don't want dithered colors, so FillRect with the nearest color. 00354 */ 00355 if (crBk == GreGetNearestColor(hdc, crBk)) { 00356 GreSetBkColor(hdc, crBk); 00357 uFlags = ETO_OPAQUE; 00358 } else { 00359 FillRect(hdc, &rc, SYSHBR(INFOBK)); 00360 GreSetBkMode(hdc, TRANSPARENT); 00361 uFlags = ETO_CLIPPED; 00362 } 00363 00364 GreExtTextOutW(hdc, SYSMET(CXBORDER) * TT_XOFFSET, 00365 SYSMET(CYBORDER) * TT_YOFFSET, uFlags, &rc, pttwnd->pstr, 00366 wcslen(pttwnd->pstr), NULL); 00367 } 00368 00369 /***************************************************************************\ 00370 * FindNcHitEx 00371 * 00372 * 12-Sep-96 vadimg created 00373 \***************************************************************************/ 00374 00375 int FindNCHitEx(PWND pwnd, int ht, POINT pt) 00376 { 00377 /* 00378 * Bug 263057 joejo 00379 * It seems that pwnd->spmenu can be released and set to null, 00380 * without the WFMPRESENT flag being cleared. Make sure that 00381 * we have a good pwnd->spmenu before continuing. 00382 */ 00383 if (ht == HTMENU && pwnd->spmenu && TestWF(pwnd, WFMPRESENT)) { 00384 PMENU spmenu = pwnd->spmenu; 00385 PITEM pitem; 00386 int nItem; 00387 00388 nItem = MNItemHitTest(spmenu, pwnd, pt); 00389 if (nItem >= 0) { 00390 pitem = (PITEM)&spmenu->rgItems[nItem]; 00391 switch ((ULONG_PTR)pitem->hbmp) { 00392 case (ULONG_PTR)HBMMENU_SYSTEM: 00393 ht = HTMDISYSMENU; 00394 break; 00395 case (ULONG_PTR)HBMMENU_MBAR_RESTORE: 00396 ht = HTMDIMAXBUTTON; 00397 break; 00398 case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE: 00399 case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE_D: 00400 ht = HTMDIMINBUTTON; 00401 break; 00402 case (ULONG_PTR)HBMMENU_MBAR_CLOSE: 00403 case (ULONG_PTR)HBMMENU_MBAR_CLOSE_D: 00404 ht = HTMDICLOSE; 00405 break; 00406 case (ULONG_PTR)HBMMENU_CALLBACK: 00407 ht = HTERROR; 00408 break; 00409 default: 00410 ht = HTMENUITEM; 00411 break; 00412 } 00413 } 00414 return MAKELONG(ht, nItem); 00415 } else if (ht == HTVSCROLL && TestWF(pwnd, WFVPRESENT)) { 00416 return MAKELONG(HitTestScrollBar(pwnd, TRUE, pt), 1); 00417 } else if (ht == HTHSCROLL && TestWF(pwnd, WFHPRESENT)) { 00418 return MAKELONG(HitTestScrollBar(pwnd, FALSE, pt), 0); 00419 } 00420 00421 return ht; 00422 } 00423 00424 /***************************************************************************\ 00425 * KillTooltipTimer 00426 * 00427 * Kill the timer and zero out the timer id. 00428 \***************************************************************************/ 00429 void KillTooltipTimer (PTOOLTIPWND pttwnd) 00430 { 00431 UINT uTID = pttwnd->uTID; 00432 if (uTID != 0) { 00433 pttwnd->uTID = 0; 00434 _KillTimer((PWND)pttwnd, uTID); 00435 } 00436 } 00437 /***************************************************************************\ 00438 * SetTooltipTimer 00439 * 00440 \***************************************************************************/ 00441 void SetTooltipTimer (PTOOLTIPWND pttwnd, UINT uTID, UINT uDelay) 00442 { 00443 KillTooltipTimer(pttwnd); 00444 pttwnd->uTID = uTID; 00445 InternalSetTimer((PWND)pttwnd, uTID, uDelay, NULL, 0); 00446 } 00447 /***************************************************************************\ 00448 * xxxResetTooltip 00449 * 00450 * Hide the tooltip, kill the timer, and zero out most of the struct members. 00451 \***************************************************************************/ 00452 00453 void xxxResetTooltip(PTOOLTIPWND pttwnd) 00454 { 00455 KillTooltipTimer(pttwnd); 00456 00457 CheckLock(pttwnd); 00458 00459 if (TestWF(pttwnd, WFVISIBLE)) { 00460 PWND spwndMessage; 00461 TL tlpwnd; 00462 00463 xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | 00464 SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER); 00465 00466 spwndMessage = PWNDMESSAGE(pttwnd); 00467 ThreadLockAlways(spwndMessage, &tlpwnd); 00468 xxxSetParent((PWND)pttwnd, spwndMessage); 00469 ThreadUnlock(&tlpwnd); 00470 } 00471 00472 ZeroTooltip(pttwnd); 00473 pttwnd->head.rpdesk->dwDTFlags &= ~DF_TOOLTIP; 00474 } 00475 00476 /***************************************************************************\ 00477 * xxxShowTooltip 00478 * 00479 * Show the tooltip window. 00480 * 00481 * 12-Sep-96 vadimg created 00482 \***************************************************************************/ 00483 00484 void xxxShowTooltip(PTOOLTIPWND pttwnd) 00485 { 00486 SIZE size; 00487 POINT pt; 00488 DWORD dwFlags; 00489 00490 CheckLock(pttwnd); 00491 00492 if (pttwnd->pstr == NULL) 00493 return; 00494 00495 if (pttwnd->pstr == gszCAPTIONTOOLTIP) { 00496 00497 PWND pwnd = PtiCurrent()->rpdesk->spwndTrack; 00498 /* 00499 * The window text might have changed in callbacks, retrieve it now 00500 */ 00501 if (TestWF(pwnd, WEFTRUNCATEDCAPTION) && pwnd->strName.Length) { 00502 wcsncpycch(gszCAPTIONTOOLTIP, pwnd->strName.Buffer, CAPTIONTOOLTIPLEN-1); 00503 gszCAPTIONTOOLTIP[CAPTIONTOOLTIPLEN-1] = 0; 00504 } else { 00505 return; 00506 } 00507 } 00508 00509 TooltipGetSize(pttwnd, &size); 00510 TooltipGetPosition(pttwnd, &size, &pt); 00511 00512 dwFlags = SWP_CREATESPB | SWP_SHOWWINDOW | SWP_NOACTIVATE; 00513 if (TestEffectUP(TOOLTIPANIMATION)) { 00514 dwFlags |= SWP_NOREDRAW; 00515 } 00516 xxxSetWindowPos((PWND)pttwnd, PWND_TOP, pt.x, pt.y, size.cx, size.cy, 00517 dwFlags); 00518 } 00519 00520 /***************************************************************************\ 00521 * xxxTooltipHandleTimer 00522 * 00523 * 12-Sep-96 vadimg created 00524 \***************************************************************************/ 00525 00526 void xxxTooltipHandleTimer(PTOOLTIPWND pttwnd, UINT uTID) 00527 { 00528 00529 switch(uTID) { 00530 case TTT_SHOW: { 00531 /* 00532 * Move the tooltip window to the desktop so it can 00533 * be shown. Then show it. 00534 */ 00535 PWND pwndDesktop = PWNDDESKTOP(pttwnd); 00536 TL tlpwnd; 00537 ThreadLockAlways(pwndDesktop, &tlpwnd); 00538 xxxSetParent((PWND)pttwnd, pwndDesktop); 00539 ThreadUnlock(&tlpwnd); 00540 00541 xxxShowTooltip(pttwnd); 00542 break; 00543 } 00544 00545 case TTT_ANIMATE: 00546 /* 00547 * If animation is completed, set timer to hide 00548 */ 00549 if (TooltipAnimate(pttwnd)) { 00550 SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay); 00551 } 00552 break; 00553 00554 case TTT_HIDE: 00555 /* 00556 * Hide it 00557 */ 00558 xxxResetTooltip(pttwnd); 00559 break; 00560 } 00561 } 00562 /***************************************************************************\ 00563 * xxxTooltipWndProc 00564 * 00565 * The actual WndProc for the tooltip window. 00566 * 00567 * 12-Sep-96 vadimg created 00568 \***************************************************************************/ 00569 00570 LRESULT xxxTooltipWndProc(PWND pwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 00571 { 00572 PAINTSTRUCT ps; 00573 PTOOLTIPWND pttwnd; 00574 00575 CheckLock(pwnd); 00576 VALIDATECLASSANDSIZE(pwnd, uMsg, wParam, lParam, FNID_TOOLTIP, WM_NCCREATE); 00577 pttwnd = (PTOOLTIPWND)pwnd; 00578 00579 switch(uMsg) { 00580 case WM_TIMER: 00581 xxxTooltipHandleTimer(pttwnd, (UINT)wParam); 00582 break; 00583 00584 case WM_PAINT: 00585 xxxBeginPaint(pwnd, &ps); 00586 TooltipRender(pttwnd, ps.hdc); 00587 xxxEndPaint(pwnd, &ps); 00588 break; 00589 00590 case WM_PRINTCLIENT: 00591 TooltipRender(pttwnd, (HDC)wParam); 00592 break; 00593 00594 case WM_ERASEBKGND: 00595 break; 00596 00597 case WM_NCCREATE: 00598 InitTooltipDelay(pttwnd); 00599 InitTooltipAnimation(pttwnd); 00600 goto CallDWP; 00601 00602 case WM_NCDESTROY: 00603 CleanupTooltipAnimation(pttwnd); 00604 GETPDESK(pttwnd)->dwDTFlags &= ~DF_TOOLTIP; 00605 goto CallDWP; 00606 00607 case WM_WINDOWPOSCHANGED: 00608 if (((LPWINDOWPOS)lParam)->flags & SWP_SHOWWINDOW) { 00609 HDC hdc; 00610 int cx; 00611 int cy; 00612 00613 if (!TestEffectUP(TOOLTIPANIMATION)) { 00614 SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay); 00615 goto CallDWP; 00616 } 00617 00618 hdc = NULL; 00619 cx = pttwnd->rcWindow.right - pttwnd->rcWindow.left; 00620 cy = pttwnd->rcWindow.bottom - pttwnd->rcWindow.top; 00621 00622 /* 00623 * At this point we're sure that the window is showing and the size 00624 * has been changed and we're in the context of the desktop thread. 00625 */ 00626 if (TestALPHA(TOOLTIPFADE)) { 00627 hdc = CreateFade((PWND)pttwnd, NULL, CMS_TOOLTIP, 00628 FADE_SHOW | FADE_TOOLTIP); 00629 } else { 00630 if (CreateTooltipBitmap(pttwnd, cx, cy)) { 00631 hdc = pttwnd->hdcMem; 00632 } 00633 } 00634 00635 if (hdc == NULL) { 00636 SetTooltipTimer(pttwnd, TTT_HIDE, 0); 00637 goto CallDWP; 00638 } 00639 00640 xxxSendMessage((PWND)pttwnd, WM_PRINT, (WPARAM)hdc, 00641 PRF_CLIENT | PRF_NONCLIENT | PRF_CHILDREN | PRF_ERASEBKGND); 00642 00643 /* 00644 * Start animation timer 00645 */ 00646 00647 if (TestFadeFlags(FADE_TOOLTIP)) { 00648 StartFade(); 00649 SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay); 00650 } else { 00651 pttwnd->dwAnimStart = NtGetTickCount(); 00652 SetTooltipTimer(pttwnd, TTT_ANIMATE, TT_ANIMATEDELAY); 00653 } 00654 } else if (((LPWINDOWPOS)lParam)->flags & SWP_HIDEWINDOW) { 00655 if (TestFadeFlags(FADE_TOOLTIP)) { 00656 StopFade(); 00657 } else { 00658 DestroyTooltipBitmap(pttwnd); 00659 } 00660 } 00661 goto CallDWP; 00662 00663 default: 00664 CallDWP: 00665 return xxxDefWindowProc(pwnd, uMsg, wParam, lParam); 00666 } 00667 00668 return 0; 00669 } 00670 00671 /***************************************************************************\ 00672 * IsTrackedHittest 00673 * 00674 * Should we be tracking this hittest code? Return the track string if yes. 00675 * If on caption returning the window strName.Buffer could 00676 * make the system bugcheck if there is a SetWindowText in the callback. 00677 \***************************************************************************/ 00678 LPWSTR IsTooltipHittest(PWND pwnd, UINT ht) 00679 { 00680 switch (ht) { 00681 case HTMINBUTTON: 00682 if (TestWF(pwnd, WFMINBOX)) { 00683 return (TestWF(pwnd, WFMINIMIZED)) ? gszRESUP : gszMIN; 00684 } 00685 break; 00686 00687 case HTMAXBUTTON: 00688 if (TestWF(pwnd, WFMAXBOX)) { 00689 return (TestWF(pwnd, WFMAXIMIZED)) ? gszRESDOWN : gszMAX; 00690 } 00691 break; 00692 00693 case HTCLOSE: 00694 case HTMDICLOSE: 00695 return gszSCLOSE; 00696 00697 /* Commented out due to TandyT ... 00698 case HTSYSMENU: 00699 case HTMDISYSMENU: 00700 return gszSMENU; 00701 */ 00702 case HTHELP: 00703 return GETGPSIMBPSTR(SEB_HELP); 00704 00705 case HTMDIMINBUTTON: 00706 return gszMIN; 00707 00708 case HTMDIMAXBUTTON: 00709 return gszRESDOWN; 00710 00711 case HTCAPTION: 00712 /* 00713 * We only show the caption tooltip if the window text 00714 * doesn't fit entirely on the caption. We will fill 00715 * gszCAPTIONTOOLTIP right before showing the text 00716 */ 00717 if (TestWF(pwnd, WEFTRUNCATEDCAPTION)) { 00718 return gszCAPTIONTOOLTIP; 00719 } 00720 break; 00721 00722 default: 00723 break; 00724 } 00725 return NULL; 00726 } 00727 00728 /***************************************************************************\ 00729 * xxxHotTrackMenu 00730 * 00731 * Hot-track a menu item in the menu bar. 00732 \***************************************************************************/ 00733 BOOL xxxHotTrackMenu(PWND pwnd, UINT nItem, BOOL fDraw) 00734 { 00735 PMENU pmenu = pwnd->spmenu; 00736 PITEM pItem; 00737 HDC hdc; 00738 UINT oldAlign; 00739 TL tlpmenu; 00740 00741 CheckLock(pwnd); 00742 00743 /* 00744 * The window may have lied about the hit-test code on 00745 * WM_NCHITTEST. Make sure it does indeed have a menu. 00746 */ 00747 if (!TestWF(pwnd, WFMPRESENT) || pmenu == NULL) 00748 return FALSE; 00749 00750 if (nItem >= pmenu->cItems) { 00751 RIPMSG0(RIP_WARNING, "xxxHotTrackMenu: menu too large"); 00752 return FALSE; 00753 } 00754 00755 pItem = &pmenu->rgItems[nItem]; 00756 00757 /* 00758 * Make sure we draw on the right spot 00759 */ 00760 ThreadLock(pmenu, &tlpmenu); 00761 xxxMNRecomputeBarIfNeeded(pwnd, pmenu); 00762 ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&tlpmenu, TRUE); 00763 00764 if (fDraw) { 00765 if (TestMFS(pItem, MF_GRAYED)) { 00766 ThreadUnlock(&tlpmenu); 00767 return FALSE; 00768 } 00769 SetMFS(pItem, MFS_HOTTRACK); 00770 } else { 00771 ClearMFS(pItem, MFS_HOTTRACK); 00772 } 00773 00774 hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE); 00775 GreSelectBrush(hdc, SYSHBR(MENUTEXT)); 00776 GreSelectFont(hdc, ghMenuFont); 00777 00778 oldAlign = GreGetTextAlign(hdc); 00779 if (pmenu->rgItems && TestMFT(pmenu->rgItems, MFT_RIGHTORDER)) 00780 GreSetTextAlign(hdc, oldAlign | TA_RTLREADING); 00781 00782 /* 00783 * When the item is not owner draw, xxxDrawMenuItem does not 00784 * call back and does not leave the critical section. 00785 */ 00786 xxxDrawMenuItem(hdc, pmenu, pItem, 0); 00787 GreSetTextAlign(hdc, oldAlign); 00788 ThreadUnlock(&tlpmenu); 00789 00790 _ReleaseDC(hdc); 00791 return TRUE; 00792 } 00793 00794 00795 /***************************************************************************\ 00796 * HotTrackCaption 00797 * 00798 * Hot-track a caption button. 00799 \***************************************************************************/ 00800 00801 #ifdef COLOR_HOTTRACKING 00802 00803 BOOL xxxHotTrackCaption(PWND pwnd, int ht, BOOL fDraw) 00804 { 00805 DWORD dwWhere; 00806 int x, y; 00807 WORD bm, cmd; 00808 RECT rcBtn; 00809 HDC hdc; 00810 00811 CheckLock(pwnd); 00812 00813 if (!TestWF(pwnd, WFCPRESENT)) 00814 return FALSE; 00815 00816 dwWhere = xxxCalcCaptionButton(pwnd, ht, &cmd, &rcBtn, &bm); 00817 x = GET_X_LPARAM(dwWhere); 00818 y = GET_Y_LPARAM(dwWhere); 00819 00820 if (!cmd) 00821 return FALSE; 00822 00823 hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE); 00824 BitBltSysBmp(hdc, x, y, bm + (fDraw ? DOBI_HOT : 0)); 00825 _ReleaseDC(hdc); 00826 return TRUE; 00827 } 00828 00829 #endif // COLOR_HOTTRACKING 00830 00831 /***************************************************************************\ 00832 * xxxHotTrack 00833 * 00834 \***************************************************************************/ 00835 00836 BOOL xxxHotTrack(PWND pwnd, int htEx, BOOL fDraw) 00837 { 00838 int ht = LOWORD(htEx); 00839 00840 CheckLock(pwnd); 00841 00842 switch(ht) { 00843 #ifdef COLOR_HOTTRACKING 00844 case HTMINBUTTON: 00845 case HTMAXBUTTON: 00846 case HTHELP: 00847 case HTCLOSE: 00848 return xxxHotTrackCaption(pwnd, ht, fDraw); 00849 00850 case HTSCROLLUP: 00851 case HTSCROLLDOWN: 00852 case HTSCROLLUPPAGE: 00853 case HTSCROLLDOWNPAGE: 00854 case HTSCROLLTHUMB: 00855 return xxxHotTrackSB(pwnd, htEx, fDraw); 00856 00857 case HTMDIMINBUTTON: 00858 case HTMDIMAXBUTTON: 00859 case HTMDICLOSE: 00860 #endif // COLOR_HOTTRACKING 00861 case HTMENUITEM: 00862 return xxxHotTrackMenu(pwnd, HIWORD(htEx), fDraw); 00863 00864 } 00865 00866 return FALSE; 00867 } 00868 00869 /***************************************************************************\ 00870 * xxxCreateTooltip 00871 * 00872 * Call this to show a new tooltip with a new string and delay. 00873 \***************************************************************************/ 00874 00875 void xxxCreateTooltip(PTOOLTIPWND pttwnd, LPWSTR pstr) 00876 { 00877 CheckLock(pttwnd); 00878 00879 /* 00880 * Store new text 00881 */ 00882 pttwnd->pstr = pstr; 00883 /* 00884 * If already visible, hide it and show it in new place. 00885 * Otherwise, set timer to show. 00886 */ 00887 if (TestWF(pttwnd, WFVISIBLE)) { 00888 xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | 00889 SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING); 00890 xxxShowTooltip(pttwnd); 00891 } else { 00892 SetTooltipTimer(pttwnd, TTT_SHOW, pttwnd->dwShowDelay); 00893 } 00894 } 00895 00896 /***************************************************************************\ 00897 * xxxTrackMouseMove 00898 * 00899 * This is the entry point for the system tooltips and hot-tracking. 00900 * 00901 * 12-Sep-96 vadimg created 00902 \***************************************************************************/ 00903 00904 void xxxTrackMouseMove(PWND pwnd, int htEx, UINT message) 00905 { 00906 BOOL fNewpwndTrack; 00907 DWORD dwDTCancel = 0; 00908 TL tlpwnd; 00909 LPWSTR pstr; 00910 PDESKTOP pdesk = PtiCurrent()->rpdesk; 00911 PTHREADINFO ptiTrack; 00912 00913 00914 #if DBG 00915 /* 00916 * Let's warn if this function gets reenterd so we can make sure 00917 * nothing bad will follow. This should be a rare situation. 00918 * Look in gptiReEntered to find out who is already here. 00919 */ 00920 static UINT gcReEntered = 0; 00921 static PTHREADINFO gptiReEntered; 00922 if(gcReEntered++ != 0){ 00923 RIPMSG2(RIP_WARNING, "Reentered xxxTrackMouseMove; previous thread was %#p, current thread is %#p", gptiReEntered, PtiCurrent()); 00924 } 00925 gptiReEntered = PtiCurrent(); 00926 00927 CheckLock(pwnd); 00928 00929 /* 00930 * We must be on an interactive window station. 00931 */ 00932 if (pdesk->rpwinstaParent != NULL && 00933 pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO) { 00934 RIPMSG0(RIP_ERROR, "Can't use tooltips on non-interactive winsta"); 00935 } 00936 00937 { 00938 static POINT pt = {0, 0}; 00939 00940 #ifdef UNDONE 00941 /* 00942 * We might have taken a guess on the hit test (see FindNCHitEx) 00943 * so if we're at the same point and same window, something 00944 * might be fishy 00945 */ 00946 if ((pt.x == gpsi->ptCursor.x) 00947 && (pt.y == gpsi->ptCursor.y) 00948 && (pdesk->spwndTrack == pwnd)) { 00949 RIPMSG1(RIP_WARNING, "xxxTrackMouseMove: Same point & window. %#p", pwnd); 00950 } 00951 #endif 00952 00953 /* 00954 * Something is supposed to have changed or we're wasting time 00955 */ 00956 UserAssert((pt.x != gpsi->ptCursor.x) 00957 || (pt.y != gpsi->ptCursor.y) 00958 || (pdesk->spwndTrack != pwnd) 00959 || (pdesk->htEx != htEx) 00960 || (message != WM_MOUSEMOVE)); 00961 /* 00962 * Remember last tracked point 00963 */ 00964 pt = gpsi->ptCursor; 00965 } 00966 /* 00967 * pwnd is supposed to be on the current thread and queue 00968 */ 00969 UserAssert(PtiCurrent() == GETPTI(pwnd)); 00970 UserAssert(PtiCurrent()->pq == GETPTI(pwnd)->pq); 00971 #endif 00972 00973 /* 00974 * Have we switched windows? 00975 */ 00976 fNewpwndTrack = (pdesk->spwndTrack != pwnd); 00977 /* 00978 * If no tracking is taking place, just go set the new 00979 * tracking state 00980 */ 00981 if (!(pdesk->dwDTFlags & DF_MOUSEMOVETRK)) { 00982 goto SetNewState; 00983 } 00984 /* 00985 * Potentially while we leave the critical section below in 00986 * xxxCancelMouseMoveTracking, spwndTrack could be destroyed and unlocked 00987 * and then we go and create the tooltip. This would mean that 00988 * DF_TOOLTIPACTIVE (part of DF_MOUSEMOVETRK test above) would be set, 00989 * but pdesk->spwndTrack would be NULL and we can AV dereferencing 00990 * pdesk->spwndTrack below. Prevent this by making the check here. 00991 */ 00992 if (pdesk->spwndTrack == NULL) { 00993 goto SetNewState; 00994 } 00995 00996 /* 00997 * Nuke hottracking and deactivate tooltip state, if any. 00998 * Do it sychronously if we're tracking on the current queue; 00999 * Otherwise, post an event and let it happen later. 01000 */ 01001 ptiTrack = GETPTI(pdesk->spwndTrack); 01002 if (PtiCurrent()->pq == ptiTrack->pq) { 01003 dwDTCancel |= DF_HOTTRACKING; 01004 } else if (pdesk->dwDTFlags & (DF_HOTTRACKING | DF_TOOLTIPACTIVE)) { 01005 PostEventMessage(ptiTrack, ptiTrack->pq, 01006 QEVENT_CANCELMOUSEMOVETRK, 01007 pdesk->spwndTrack, 01008 pdesk->dwDTFlags, 01009 pdesk->htEx, DF_HOTTRACKING); 01010 /* 01011 * Paranoid assertion. If we're switching queues, we must 01012 * be switching windows. Did we just go through 01013 * ReattachThreads? 01014 */ 01015 UserAssert(pwnd != pdesk->spwndTrack); 01016 pdesk->dwDTFlags &= ~(DF_HOTTRACKING | DF_TOOLTIPACTIVE); 01017 } 01018 /* 01019 * If we're on the client area or the user clicked, 01020 * nuke the tooltip (if any). 01021 * Since we might want to re-show the tooltip, we don't nuke it 01022 * now if we swichted windows (we'll nuke it later if needed) 01023 */ 01024 if ((htEx == HTCLIENT) || (message != WM_MOUSEMOVE)) { 01025 dwDTCancel |= DF_TOOLTIPACTIVE; 01026 } 01027 /* 01028 * If we switched windows or crossed client/nonclinet boundaries, 01029 * end track mouse leave/hover. 01030 */ 01031 if (fNewpwndTrack || ((pdesk->htEx == HTCLIENT) ^ (htEx == HTCLIENT))) { 01032 dwDTCancel |= DF_TRACKMOUSEEVENT; 01033 } 01034 /* 01035 * Cancel whatever is active and needs to go away 01036 */ 01037 ThreadLockAlways(pdesk->spwndTrack, &tlpwnd); 01038 xxxCancelMouseMoveTracking(pdesk->dwDTFlags, 01039 pdesk->spwndTrack, 01040 pdesk->htEx, 01041 dwDTCancel); 01042 ThreadUnlock(&tlpwnd); 01043 pdesk->dwDTFlags &= ~dwDTCancel; 01044 01045 01046 01047 SetNewState: 01048 /* 01049 * Hottracking/tooltip on mouse move if on NC hitest and enabled 01050 */ 01051 if ((htEx != HTCLIENT) && (message == WM_MOUSEMOVE) && TestEffectUP(HOTTRACKING)) { 01052 /* 01053 * Hottrack the new hit test area 01054 */ 01055 if (xxxHotTrack(pwnd, htEx, TRUE)) { 01056 pdesk->dwDTFlags |= DF_HOTTRACKING; 01057 } 01058 01059 /* 01060 * Remove/set the tool tip. 01061 * We always do this synchronously because it doesn't mess 01062 * with pwnd's or spwnTrack's queue 01063 */ 01064 if ((pstr = IsTooltipHittest(pwnd, LOWORD(htEx))) != NULL) { 01065 PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip; 01066 ThreadLockAlways(pttwnd, &tlpwnd); 01067 xxxCreateTooltip(pttwnd, pstr); 01068 ThreadUnlock(&tlpwnd); 01069 pdesk->dwDTFlags |= DF_TOOLTIP; 01070 } else { 01071 PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip; 01072 ThreadLockAlways(pttwnd, &tlpwnd); 01073 xxxResetTooltip(pttwnd); 01074 ThreadUnlock(&tlpwnd); 01075 } 01076 } /* if (htEx != HTCLIENT) */ 01077 01078 01079 ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&pwnd, TRUE); 01080 01081 /* 01082 * Update new track window if needed. 01083 */ 01084 if (fNewpwndTrack) { 01085 PWND pwndActivate; 01086 01087 Lock(&pdesk->spwndTrack, pwnd); 01088 /* 01089 * Active window tracking. 01090 * If there is non-zero timeout, get the window we're supposed to activate 01091 * and set the timer. Otherwise, set the queue flag so 01092 * xxxActiveWindowTracking can do its thing. 01093 */ 01094 if ((message == WM_MOUSEMOVE) && TestUP(ACTIVEWINDOWTRACKING)) { 01095 if (UP(ACTIVEWNDTRKTIMEOUT) != 0) { 01096 pwndActivate = GetActiveTrackPwnd(pwnd, NULL); 01097 if (pwndActivate != NULL) { 01098 InternalSetTimer(pwndActivate, IDSYS_WNDTRACKING, 01099 UP(ACTIVEWNDTRKTIMEOUT), 01100 xxxSystemTimerProc, TMRF_SYSTEM); 01101 } 01102 } else { 01103 PtiCurrent()->pq->QF_flags |= QF_ACTIVEWNDTRACKING; 01104 } /* if (TestUP(ACTIVEWNDTRKZORDER)) */ 01105 } /* if (TestUP(ACTIVEWINDOWTRACKING)) */ 01106 01107 } 01108 01109 /* 01110 * Save new hit test code 01111 */ 01112 pdesk->htEx = htEx; 01113 01114 #if DBG 01115 --gcReEntered; 01116 #endif 01117 } 01118 01119 /***************************************************************************\ 01120 * xxxCancelMouseMoveTracking 01121 * 01122 * History 01123 * 12/07/96 GerardoB Created 01124 \***************************************************************************/ 01125 void xxxCancelMouseMoveTracking (DWORD dwDTFlags, PWND pwndTrack, int htEx, DWORD dwDTCancel) 01126 { 01127 01128 CheckLock(pwndTrack); 01129 /* 01130 * Hottracking 01131 */ 01132 if ((dwDTFlags & DF_HOTTRACKING) && (dwDTCancel & DF_HOTTRACKING)) { 01133 /* 01134 * The current state must be owned by the current queue. 01135 * Otherwise, we're about to do an inter-queue cancelation. 01136 */ 01137 UserAssert(PtiCurrent()->pq == GETPTI(pwndTrack)->pq); 01138 01139 xxxHotTrack(pwndTrack, htEx, FALSE); 01140 } 01141 01142 /* 01143 * Tooltips 01144 */ 01145 if ((dwDTFlags & DF_TOOLTIPSHOWING) && (dwDTCancel & DF_TOOLTIP)) { 01146 PTOOLTIPWND pttwnd = (PTOOLTIPWND)PWNDTOOLTIP(pwndTrack); 01147 TL tlpwnd; 01148 01149 ThreadLockAlways(pttwnd, &tlpwnd); 01150 xxxResetTooltip(pttwnd); 01151 ThreadUnlock(&tlpwnd); 01152 } 01153 01154 /* 01155 * Mouse Leave 01156 */ 01157 if ((dwDTFlags & DF_TRACKMOUSELEAVE) && (dwDTCancel & DF_TRACKMOUSELEAVE)) { 01158 _PostMessage(pwndTrack, 01159 ((htEx == HTCLIENT) ? WM_MOUSELEAVE : WM_NCMOUSELEAVE), 01160 0, 0); 01161 } 01162 01163 /* 01164 * Mouse Hover 01165 */ 01166 if ((dwDTFlags & DF_TRACKMOUSEHOVER) && (dwDTCancel & DF_TRACKMOUSEHOVER)) { 01167 _KillSystemTimer(pwndTrack, IDSYS_MOUSEHOVER); 01168 } 01169 }

Generated on Sat May 15 19:42:02 2004 for test by doxygen 1.3.7