|
|
/****************************** Module Header ******************************\
* Module Name: tooltips.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Implements system tooltips. * * History: * 25-Aug-1996 vadimg created \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#define TT_XOFFSET 2
#define TT_YOFFSET 1
#define TTT_SHOW 1
#define TTT_HIDE 2
#define TTT_ANIMATE 3
#define TT_ANIMATEDELAY 20
#define TTF_POSITIVE 0x00000001
#define bitsizeof(x) (sizeof(x) * 8)
LONG GreGetBitmapBits(HBITMAP, ULONG, PBYTE, PLONG); DWORD CalcCaptionButton(PWND pwnd, int hit, LPWORD pcmd, LPRECT prcBtn, LPWORD pbm); int HitTestScrollBar(PWND pwnd, int ht, POINT pt); BOOL xxxHotTrackSB(PWND pwnd, int htEx, BOOL fDraw);
__inline void ZeroTooltip(PTOOLTIPWND pttwnd) { RtlZeroMemory((PBYTE)pttwnd + (sizeof(TOOLTIPWND) - sizeof(TOOLTIP)), sizeof(TOOLTIP)); }
/***************************************************************************\
* GetTooltipDC * * 2/3/1998 vadimg created \***************************************************************************/
HDC GetTooltipDC(PTOOLTIPWND pttwnd) { HDC hdc = _GetDCEx((PWND)pttwnd, NULL, DCX_WINDOW | DCX_CACHE | DCX_USESTYLE);
if (hdc == NULL) return NULL;
GreSelectFont(hdc, ghStatusFont); return hdc; }
/***************************************************************************\
* InitTooltipAnimation * * Creates memory bitmap and DC for use by system tooltips. Gets the screen * DC used throughout. * * 12-Sep-96 vadimg created \***************************************************************************/
void InitTooltipAnimation(PTOOLTIPWND pttwnd) { HDC hdc = GetTooltipDC(pttwnd);
if ((pttwnd->hdcMem = GreCreateCompatibleDC(hdc)) == NULL) { return; } _ReleaseDC(hdc); GreSetDCOwner(pttwnd->hdcMem, OBJECT_OWNER_PUBLIC); }
/***************************************************************************\
* DestroyTooltipBitmap * \***************************************************************************/
void DestroyTooltipBitmap(PTOOLTIPWND pttwnd) { if (pttwnd->hbmMem == NULL) return;
GreSelectBitmap(pttwnd->hdcMem, GreGetStockObject(PRIV_STOCK_BITMAP)); GreDeleteObject(pttwnd->hbmMem); pttwnd->hbmMem = NULL; }
/***************************************************************************\
* CreateTooltipBitmap * \***************************************************************************/
BOOL CreateTooltipBitmap(PTOOLTIPWND pttwnd, UINT cx, UINT cy) { HDC hdc;
if (pttwnd->hdcMem == NULL) { RIPMSG0(RIP_WARNING, "CreateTooltipBitmap: pttwnd->hdcMem is NULL"); return FALSE; }
DestroyTooltipBitmap(pttwnd);
hdc = GetTooltipDC(pttwnd); pttwnd->hbmMem = GreCreateCompatibleBitmap(hdc, cx, cy); _ReleaseDC(hdc);
if (pttwnd->hbmMem == NULL) { RIPMSG0(RIP_WARNING, "CreateTooltipBitmap: hbmMem is NULL"); return FALSE; } GreSelectBitmap(pttwnd->hdcMem, pttwnd->hbmMem); return TRUE; }
/***************************************************************************\
* CleanupTooltipAnimation * * Deletes memory bitmap and DC for use by system tooltips. Release the * screen DC. * * 12-Sep-96 vadimg created \***************************************************************************/
void CleanupTooltipAnimation(PTOOLTIPWND pttwnd) { DestroyTooltipBitmap(pttwnd);
if (pttwnd->hdcMem != NULL) { GreSetDCOwner(pttwnd->hdcMem, OBJECT_OWNER_CURRENT); GreDeleteDC(pttwnd->hdcMem); } }
/***************************************************************************\
* TooltipAnimate * * Perform one frame of window animation. Just a simplified version of * the AnimateWindow API. * * 12-Sep-96 vadimg created \***************************************************************************/
BOOL TooltipAnimate(PTOOLTIPWND pttwnd) { int y, yMem, yReal, ny, iy, cx, cy; DWORD dwElapsed; HDC hdc; BOOL fRet = FALSE;
if (pttwnd->pstr == NULL) return TRUE;
hdc = GetTooltipDC(pttwnd); cx = pttwnd->rcWindow.right - pttwnd->rcWindow.left; cy = pttwnd->rcWindow.bottom - pttwnd->rcWindow.top; dwElapsed = NtGetTickCount() - pttwnd->dwAnimStart; iy = MultDiv(cy, dwElapsed, CMS_TOOLTIP);
if (dwElapsed > CMS_TOOLTIP || iy == cy) { GreBitBlt(hdc, 0, 0, cx, cy, pttwnd->hdcMem, 0, 0, SRCCOPY | NOMIRRORBITMAP, 0); fRet = TRUE; goto Cleanup; } else if (pttwnd->iyAnim == iy) { goto Cleanup; }
if (pttwnd->dwFlags & TTF_POSITIVE) { y = 0; ny = 0; } else { y = cy; ny = -1; }
yReal = y + ny * iy; yMem = (pttwnd->dwFlags & TTF_POSITIVE) ? cy - iy : 0; pttwnd->iyAnim = iy;
GreBitBlt(hdc, 0, yReal, cx, iy, pttwnd->hdcMem, 0, yMem, SRCCOPY | NOMIRRORBITMAP, 0);
Cleanup: _ReleaseDC(hdc); return fRet; }
/***************************************************************************\
* GetCursorHeight * * This is tricky. We need to get the actual cursor size from the hotspot * down to the end. There is no API in windows to do this, CYCURSOR is * just the metric for the size of the bitmap, the cursor starts at the top * of the bitmap and may be smaller than the bitmap itself. * * 12-Sep-96 vadimg ported from common controls \***************************************************************************/
int GetCursorHeight(void) { int iAnd, iXor, dy = 16; WORD wMask[128]; ICONINFO ii; BITMAP bm; PCURSOR pcur; long lOffset = 0;
if ((pcur = PtiCurrent()->pq->spcurCurrent) == NULL) { return dy; }
if (!_InternalGetIconInfo(pcur, &ii, NULL, NULL, NULL, FALSE)) { return dy; }
if (!GreExtGetObjectW(ii.hbmMask, sizeof(bm), (LPSTR)&bm)) { goto Bail; }
/*
* Use the AND mask to get the cursor height if the XOR mask is there. */ if (!GreGetBitmapBits(ii.hbmMask, sizeof(wMask), (BYTE*)wMask, &lOffset)) { goto Bail; }
iAnd = (int)(bm.bmWidth * bm.bmHeight / bitsizeof(WORD));
if (ii.hbmColor == NULL) { /*
* if no color (XOR) bitmap, then the hbmMask is a double height bitmap * with the cursor and the mask stacked. */
iXor = iAnd - 1; iAnd /= 2; } else { iXor = 0; }
if (iAnd >= sizeof(wMask)) { iAnd = sizeof(wMask) - 1; }
if (iXor >= sizeof(wMask)) { iXor = 0; }
for (iAnd--; iAnd >= 0; iAnd--) { if ((iXor != 0 && wMask[iXor--] != 0) || wMask[iAnd] != 0xFFFF) { break; } }
/*
* Compute the distance between the pointer's lowest point and hotspot. */ dy = (iAnd + 1) * bitsizeof(WORD) / (int)bm.bmWidth - (int)ii.yHotspot;
Bail: if (ii.hbmColor) { GreDeleteObject(ii.hbmColor); }
if (ii.hbmMask) { GreDeleteObject(ii.hbmMask); }
return dy; }
/***************************************************************************\
* TooltipGetPosition * * Get the tooltip position on the screen taking into account the size of * the tooltip and the screen. The TTF_POSITIVE flag determines if positive * or negative animation is used. * * 12-Sep-96 vadimg created \***************************************************************************/
BOOL TooltipGetPosition(PTOOLTIPWND pttwnd, SIZE *psize, POINT *ppt) { PMONITOR pMonitor;
*ppt = gpsi->ptCursor; pMonitor = _MonitorFromPoint(*ppt, MONITOR_DEFAULTTONULL); if (pMonitor == NULL) { return FALSE; }
if (ppt->y + psize->cy >= pMonitor->rcMonitor.bottom) { ppt->y = ppt->y - psize->cy; pttwnd->dwFlags &= ~TTF_POSITIVE; } else { ppt->y += GetCursorHeight(); pttwnd->dwFlags |= TTF_POSITIVE; }
if (ppt->x + psize->cx >= pMonitor->rcMonitor.right) { ppt->x = pMonitor->rcMonitor.right - psize->cx; }
if (ppt->x < pMonitor->rcMonitor.left) { ppt->x = pMonitor->rcMonitor.left; }
return TRUE; }
/***************************************************************************\
* xxxTooltipGetSize * * Estimate the size of the tooltip window based on the size of the text. * * 12-Sep-96 vadimg created \***************************************************************************/
void xxxTooltipGetSize(PTOOLTIPWND pttwnd, SIZE *psize) { HDC hdc;
CheckLock(pttwnd);
hdc = GetTooltipDC(pttwnd);
if (CALL_LPK(PtiCurrentShared())) { xxxClientGetTextExtentPointW(hdc, pttwnd->pstr, wcslen(pttwnd->pstr), psize); } else { GreGetTextExtentW(hdc, pttwnd->pstr, wcslen(pttwnd->pstr), psize, GGTE_WIN3_EXTENT); }
_ReleaseDC(hdc); psize->cx += SYSMET(CXEDGE) + 2 * SYSMET(CXBORDER) * TT_XOFFSET; psize->cy += SYSMET(CYEDGE) + 2 * SYSMET(CYBORDER) * TT_YOFFSET; }
/***************************************************************************\
* xxxTooltipRender * * Render the tooltip window into the provided DC. * * 12-Sep-96 vadimg created \***************************************************************************/
void xxxTooltipRender(PTOOLTIPWND pttwnd, HDC hdc) { COLORREF crBk; UINT uFlags; RECT rc;
CheckLock(pttwnd);
if (pttwnd->pstr == NULL) return;
GreSelectFont(hdc, ghStatusFont); GreSetTextColor(hdc, gpsi->argbSystem[COLOR_INFOTEXT]); crBk = gpsi->argbSystem[COLOR_INFOBK];
CopyOffsetRect(&rc, &pttwnd->rcClient, -pttwnd->rcClient.left, -pttwnd->rcClient.top);
/*
* We don't want dithered colors, so FillRect with the nearest color. */ if (crBk == GreGetNearestColor(hdc, crBk)) { GreSetBkColor(hdc, crBk); uFlags = ETO_OPAQUE; } else { FillRect(hdc, &rc, SYSHBR(INFOBK)); GreSetBkMode(hdc, TRANSPARENT); uFlags = ETO_CLIPPED; }
if (CALL_LPK(PtiCurrentShared())) { xxxClientExtTextOutW(hdc, SYSMET(CXBORDER) * TT_XOFFSET, SYSMET(CYBORDER) * TT_YOFFSET, uFlags, &rc, pttwnd->pstr, wcslen(pttwnd->pstr), NULL); } else { GreExtTextOutW(hdc, SYSMET(CXBORDER) * TT_XOFFSET, SYSMET(CYBORDER) * TT_YOFFSET, uFlags, &rc, pttwnd->pstr, wcslen(pttwnd->pstr), NULL); } }
/***************************************************************************\
* FindNcHitEx * * 12-Sep-96 vadimg created \***************************************************************************/
int FindNCHitEx(PWND pwnd, int ht, POINT pt) { /*
* Bug 263057 joejo * It seems that pwnd->spmenu can be released and set to null, * without the WFMPRESENT flag being cleared. Make sure that * we have a good pwnd->spmenu before continuing. */ if (ht == HTMENU && pwnd->spmenu && TestWF(pwnd, WFMPRESENT)) { PMENU spmenu = pwnd->spmenu; PITEM pitem; int nItem;
nItem = MNItemHitTest(spmenu, pwnd, pt); if (nItem >= 0) { pitem = (PITEM)&spmenu->rgItems[nItem]; switch ((ULONG_PTR)pitem->hbmp) { case (ULONG_PTR)HBMMENU_SYSTEM: ht = HTMDISYSMENU; break; case (ULONG_PTR)HBMMENU_MBAR_RESTORE: ht = HTMDIMAXBUTTON; break; case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE: case (ULONG_PTR)HBMMENU_MBAR_MINIMIZE_D: ht = HTMDIMINBUTTON; break; case (ULONG_PTR)HBMMENU_MBAR_CLOSE: case (ULONG_PTR)HBMMENU_MBAR_CLOSE_D: ht = HTMDICLOSE; break; case (ULONG_PTR)HBMMENU_CALLBACK: ht = HTERROR; break; default: ht = HTMENUITEM; break; } } return MAKELONG(ht, nItem); } else if (ht == HTVSCROLL && TestWF(pwnd, WFVPRESENT)) { return MAKELONG(HitTestScrollBar(pwnd, TRUE, pt), 1); } else if (ht == HTHSCROLL && TestWF(pwnd, WFHPRESENT)) { return MAKELONG(HitTestScrollBar(pwnd, FALSE, pt), 0); }
return ht; }
/***************************************************************************\
* KillTooltipTimer * * Kill the timer and zero out the timer id. \***************************************************************************/ void KillTooltipTimer (PTOOLTIPWND pttwnd) { UINT uTID = pttwnd->uTID; if (uTID != 0) { pttwnd->uTID = 0; _KillTimer((PWND)pttwnd, uTID); } } /***************************************************************************\
* SetTooltipTimer * \***************************************************************************/ void SetTooltipTimer (PTOOLTIPWND pttwnd, UINT uTID, UINT uDelay) { KillTooltipTimer(pttwnd); pttwnd->uTID = uTID; InternalSetTimer((PWND)pttwnd, uTID, uDelay, NULL, 0); } /***************************************************************************\
* xxxResetTooltip * * Hide the tooltip, kill the timer, and zero out most of the struct members. \***************************************************************************/
void xxxResetTooltip(PTOOLTIPWND pttwnd) { KillTooltipTimer(pttwnd);
CheckLock(pttwnd);
if (TestWF(pttwnd, WFVISIBLE)) { PWND spwndMessage; TL tlpwnd;
xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER);
spwndMessage = PWNDMESSAGE(pttwnd); ThreadLockAlways(spwndMessage, &tlpwnd); xxxSetParent((PWND)pttwnd, spwndMessage); ThreadUnlock(&tlpwnd); }
ZeroTooltip(pttwnd); pttwnd->head.rpdesk->dwDTFlags &= ~DF_TOOLTIP; }
/***************************************************************************\
* xxxShowTooltip * * Show the tooltip window. * * 12-Sep-96 vadimg created \***************************************************************************/
BOOL xxxShowTooltip(PTOOLTIPWND pttwnd) { SIZE size; POINT pt; DWORD dwFlags;
CheckLock(pttwnd);
if (pttwnd->pstr == NULL) return FALSE;
if (pttwnd->pstr == gszCAPTIONTOOLTIP) {
PWND pwnd = PtiCurrent()->rpdesk->spwndTrack; /*
* The window text might have changed in callbacks, retrieve it now */ if (pwnd && TestWF(pwnd, WEFTRUNCATEDCAPTION) && pwnd->strName.Length) { wcsncpycch(gszCAPTIONTOOLTIP, pwnd->strName.Buffer, CAPTIONTOOLTIPLEN-1); gszCAPTIONTOOLTIP[CAPTIONTOOLTIPLEN-1] = 0; } else { return FALSE; } }
xxxTooltipGetSize(pttwnd, &size);
if (!TooltipGetPosition(pttwnd, &size, &pt)) { return FALSE; }
dwFlags = SWP_CREATESPB | SWP_SHOWWINDOW | SWP_NOACTIVATE; if (TestEffectUP(TOOLTIPANIMATION)) { dwFlags |= SWP_NOREDRAW; }
xxxSetWindowPos((PWND)pttwnd, PWND_TOP, pt.x, pt.y, size.cx, size.cy, dwFlags);
return TRUE; }
/***************************************************************************\
* xxxTooltipHandleTimer * * 12-Sep-96 vadimg created \***************************************************************************/
BOOL xxxTooltipHandleTimer(PTOOLTIPWND pttwnd, UINT uTID) { BOOL fReturn = TRUE;
switch(uTID) { case TTT_SHOW: { /*
* Move the tooltip window to the desktop so it can * be shown. Then show it. */ PWND pwndDesktop = PWNDDESKTOP(pttwnd); TL tlpwnd;
ThreadLockAlways(pwndDesktop, &tlpwnd); if (xxxSetParent((PWND)pttwnd, pwndDesktop) == NULL) { fReturn = FALSE; } else { fReturn = xxxShowTooltip(pttwnd); } ThreadUnlock(&tlpwnd); break; }
case TTT_ANIMATE: /*
* If animation is completed, set timer to hide */ if (TooltipAnimate(pttwnd)) { SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay); } break;
case TTT_HIDE: /*
* Hide it */ xxxResetTooltip(pttwnd); break; }
return fReturn; } /***************************************************************************\
* xxxTooltipWndProc * * The actual WndProc for the tooltip window. * * 12-Sep-96 vadimg created \***************************************************************************/
LRESULT xxxTooltipWndProc(PWND pwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; PTOOLTIPWND pttwnd;
CheckLock(pwnd); VALIDATECLASSANDSIZE(pwnd, uMsg, wParam, lParam, FNID_TOOLTIP, WM_NCCREATE); pttwnd = (PTOOLTIPWND)pwnd;
switch(uMsg) { case WM_TIMER: xxxTooltipHandleTimer(pttwnd, (UINT)wParam); break;
case WM_PAINT: xxxBeginPaint(pwnd, &ps); xxxTooltipRender(pttwnd, ps.hdc); xxxEndPaint(pwnd, &ps); break;
case WM_PRINTCLIENT: xxxTooltipRender(pttwnd, (HDC)wParam); break;
case WM_ERASEBKGND: break;
case WM_NCCREATE: InitTooltipDelay(pttwnd); InitTooltipAnimation(pttwnd); goto CallDWP;
case WM_NCDESTROY: CleanupTooltipAnimation(pttwnd); GETPDESK(pttwnd)->dwDTFlags &= ~DF_TOOLTIP; goto CallDWP;
case WM_WINDOWPOSCHANGED: if (((LPWINDOWPOS)lParam)->flags & SWP_SHOWWINDOW) { HDC hdc; int cx; int cy;
if (!TestEffectUP(TOOLTIPANIMATION)) { SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay); goto CallDWP; }
hdc = NULL; cx = pttwnd->rcWindow.right - pttwnd->rcWindow.left; cy = pttwnd->rcWindow.bottom - pttwnd->rcWindow.top;
/*
* At this point we're sure that the window is showing and the size * has been changed and we're in the context of the desktop thread. */ if (TestALPHA(TOOLTIPFADE)) { hdc = CreateFade((PWND)pttwnd, NULL, CMS_TOOLTIP, FADE_SHOW | FADE_TOOLTIP); } else { if (CreateTooltipBitmap(pttwnd, cx, cy)) { hdc = pttwnd->hdcMem; } }
if (hdc == NULL) { SetTooltipTimer(pttwnd, TTT_HIDE, 0); goto CallDWP; }
xxxSendMessage((PWND)pttwnd, WM_PRINT, (WPARAM)hdc, PRF_CLIENT | PRF_NONCLIENT | PRF_CHILDREN | PRF_ERASEBKGND);
/*
* Start animation timer */
if (TestFadeFlags(FADE_TOOLTIP)) { StartFade(); SetTooltipTimer(pttwnd, TTT_HIDE, pttwnd->dwHideDelay); } else { pttwnd->dwAnimStart = NtGetTickCount(); SetTooltipTimer(pttwnd, TTT_ANIMATE, TT_ANIMATEDELAY); } } else if (((LPWINDOWPOS)lParam)->flags & SWP_HIDEWINDOW) { if (TestFadeFlags(FADE_TOOLTIP)) { StopFade(); } else { DestroyTooltipBitmap(pttwnd); } } goto CallDWP;
default: CallDWP: return xxxDefWindowProc(pwnd, uMsg, wParam, lParam); }
return 0; }
/***************************************************************************\
* IsTrackedHittest * * Should we be tracking this hittest code? Return the track string if yes. * If on caption returning the window strName.Buffer could * make the system bugcheck if there is a SetWindowText in the callback. \***************************************************************************/ LPWSTR IsTooltipHittest(PWND pwnd, UINT ht) { switch (ht) { case HTMINBUTTON: if (TestWF(pwnd, WFMINBOX)) { return (TestWF(pwnd, WFMINIMIZED)) ? gszRESUP : gszMIN; } break;
case HTMAXBUTTON: if (TestWF(pwnd, WFMAXBOX)) { return (TestWF(pwnd, WFMAXIMIZED)) ? gszRESDOWN : gszMAX; } break;
case HTCLOSE: case HTMDICLOSE: return gszSCLOSE;
/* Commented out due to TandyT ...
case HTSYSMENU: case HTMDISYSMENU: return gszSMENU; */ case HTHELP: return gszHELP;
case HTMDIMINBUTTON: return gszMIN;
case HTMDIMAXBUTTON: return gszRESDOWN;
case HTCAPTION: /*
* We only show the caption tooltip if the window text * doesn't fit entirely on the caption. We will fill * gszCAPTIONTOOLTIP right before showing the text */ if (TestWF(pwnd, WEFTRUNCATEDCAPTION)) { return gszCAPTIONTOOLTIP; } break;
default: break; } return NULL; }
/***************************************************************************\
* xxxHotTrackMenu * * Hot-track a menu item in the menu bar. \***************************************************************************/ BOOL xxxHotTrackMenu(PWND pwnd, UINT nItem, BOOL fDraw) { PMENU pmenu = pwnd->spmenu; PITEM pItem; HDC hdc; UINT oldAlign; TL tlpmenu;
CheckLock(pwnd);
/*
* The window may have lied about the hit-test code on * WM_NCHITTEST. Make sure it does indeed have a menu. */ if (!TestWF(pwnd, WFMPRESENT) || pmenu == NULL) return FALSE;
if (nItem >= pmenu->cItems) { RIPMSG0(RIP_WARNING, "xxxHotTrackMenu: menu too large"); return FALSE; }
pItem = &pmenu->rgItems[nItem];
/*
* Make sure we draw on the right spot */ ThreadLock(pmenu, &tlpmenu); xxxMNRecomputeBarIfNeeded(pwnd, pmenu); ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&tlpmenu, TRUE);
if (fDraw) { if (TestMFS(pItem, MF_GRAYED)) { ThreadUnlock(&tlpmenu); return FALSE; } SetMFS(pItem, MFS_HOTTRACK); } else { ClearMFS(pItem, MFS_HOTTRACK); }
hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE); GreSelectBrush(hdc, SYSHBR(MENUTEXT)); GreSelectFont(hdc, ghMenuFont);
oldAlign = GreGetTextAlign(hdc); if (pmenu->rgItems && TestMFT(pmenu->rgItems, MFT_RIGHTORDER)) GreSetTextAlign(hdc, oldAlign | TA_RTLREADING);
/*
* When the item is not owner draw, xxxDrawMenuItem does not * call back and does not leave the critical section. */ xxxDrawMenuItem(hdc, pmenu, pItem, 0); GreSetTextAlign(hdc, oldAlign); ThreadUnlock(&tlpmenu);
_ReleaseDC(hdc); return TRUE; }
/***************************************************************************\
* HotTrackCaption * * Hot-track a caption button. \***************************************************************************/
#ifdef COLOR_HOTTRACKING
BOOL xxxHotTrackCaption(PWND pwnd, int ht, BOOL fDraw) { DWORD dwWhere; int x, y; WORD bm, cmd; RECT rcBtn; HDC hdc;
CheckLock(pwnd);
if (!TestWF(pwnd, WFCPRESENT)) return FALSE;
dwWhere = xxxCalcCaptionButton(pwnd, ht, &cmd, &rcBtn, &bm); x = GET_X_LPARAM(dwWhere); y = GET_Y_LPARAM(dwWhere);
if (!cmd) return FALSE;
hdc = _GetDCEx(pwnd, NULL, DCX_WINDOW | DCX_USESTYLE | DCX_CACHE); BitBltSysBmp(hdc, x, y, bm + (fDraw ? DOBI_HOT : 0)); _ReleaseDC(hdc); return TRUE; }
#endif // COLOR_HOTTRACKING
/***************************************************************************\
* xxxHotTrack * \***************************************************************************/
BOOL xxxHotTrack(PWND pwnd, int htEx, BOOL fDraw) { int ht = LOWORD(htEx);
CheckLock(pwnd);
switch(ht) { #ifdef COLOR_HOTTRACKING
case HTMINBUTTON: case HTMAXBUTTON: case HTHELP: case HTCLOSE: return xxxHotTrackCaption(pwnd, ht, fDraw);
case HTSCROLLUP: case HTSCROLLDOWN: case HTSCROLLUPPAGE: case HTSCROLLDOWNPAGE: case HTSCROLLTHUMB: return xxxHotTrackSB(pwnd, htEx, fDraw);
case HTMDIMINBUTTON: case HTMDIMAXBUTTON: case HTMDICLOSE: #endif // COLOR_HOTTRACKING
case HTMENUITEM: return xxxHotTrackMenu(pwnd, HIWORD(htEx), fDraw);
}
return FALSE; }
/***************************************************************************\
* xxxCreateTooltip * * Call this to show a new tooltip with a new string and delay. \***************************************************************************/
BOOL xxxCreateTooltip(PTOOLTIPWND pttwnd, LPWSTR pstr) { CheckLock(pttwnd);
/*
* Store new text */ pttwnd->pstr = pstr; /*
* If already visible, hide it and show it in new place. * Otherwise, set timer to show. */ if (TestWF(pttwnd, WFVISIBLE)) { xxxSetWindowPos((PWND)pttwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING); return xxxShowTooltip(pttwnd); } else { SetTooltipTimer(pttwnd, TTT_SHOW, pttwnd->dwShowDelay); }
return TRUE; }
/***************************************************************************\
* xxxTrackMouseMove * * This is the entry point for the system tooltips and hot-tracking. * * 12-Sep-96 vadimg created \***************************************************************************/
void xxxTrackMouseMove(PWND pwnd, int htEx, UINT message) { BOOL fNewpwndTrack; DWORD dwDTCancel = 0; TL tlpwnd; LPWSTR pstr; PDESKTOP pdesk = PtiCurrent()->rpdesk; PTHREADINFO ptiTrack;
#if DBG
/*
* Let's warn if this function gets reenterd so we can make sure * nothing bad will follow. This should be a rare situation. * Look in gptiReEntered to find out who is already here. */ static UINT gcReEntered = 0; static PTHREADINFO gptiReEntered; if(gcReEntered++ != 0){ RIPMSG2(RIP_WARNING, "Reentered xxxTrackMouseMove; previous thread was %#p, current thread is %#p", gptiReEntered, PtiCurrent()); } gptiReEntered = PtiCurrent();
CheckLock(pwnd);
/*
* We must be on an interactive window station. */ if (pdesk->rpwinstaParent != NULL && pdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO) { RIPMSG0(RIP_ERROR, "Can't use tooltips on non-interactive winsta"); }
{ static POINT pt = {0, 0};
#ifdef UNDONE
/*
* We might have taken a guess on the hit test (see FindNCHitEx) * so if we're at the same point and same window, something * might be fishy */ if ((pt.x == gpsi->ptCursor.x) && (pt.y == gpsi->ptCursor.y) && (pdesk->spwndTrack == pwnd)) { RIPMSG1(RIP_WARNING, "xxxTrackMouseMove: Same point & window. %#p", pwnd); } #endif
/*
* Something is supposed to have changed or we're wasting time */ UserAssert((pt.x != gpsi->ptCursor.x) || (pt.y != gpsi->ptCursor.y) || (pdesk->spwndTrack != pwnd) || (pdesk->htEx != htEx) || (message != WM_MOUSEMOVE)); /*
* Remember last tracked point */ pt = gpsi->ptCursor; } /*
* pwnd is supposed to be on the current thread and queue */ UserAssert(PtiCurrent() == GETPTI(pwnd)); UserAssert(PtiCurrent()->pq == GETPTI(pwnd)->pq); #endif
/*
* Have we switched windows? */ fNewpwndTrack = (pdesk->spwndTrack != pwnd); /*
* If no tracking is taking place, just go set the new * tracking state */ if (!(pdesk->dwDTFlags & DF_MOUSEMOVETRK)) { goto SetNewState; } /*
* Potentially while we leave the critical section below in * xxxCancelMouseMoveTracking, spwndTrack could be destroyed and unlocked * and then we go and create the tooltip. This would mean that * DF_TOOLTIPACTIVE (part of DF_MOUSEMOVETRK test above) would be set, * but pdesk->spwndTrack would be NULL and we can AV dereferencing * pdesk->spwndTrack below. Prevent this by making the check here. */ if (pdesk->spwndTrack == NULL) { goto SetNewState; }
/*
* Nuke hottracking and deactivate tooltip state, if any. * Do it sychronously if we're tracking on the current queue; * Otherwise, post an event and let it happen later. */ ptiTrack = GETPTI(pdesk->spwndTrack); if (PtiCurrent()->pq == ptiTrack->pq) { dwDTCancel |= DF_HOTTRACKING; } else if (pdesk->dwDTFlags & (DF_HOTTRACKING | DF_TOOLTIPACTIVE)) { PostEventMessage(ptiTrack, ptiTrack->pq, QEVENT_CANCELMOUSEMOVETRK, pdesk->spwndTrack, pdesk->dwDTFlags, pdesk->htEx, DF_HOTTRACKING); /*
* Paranoid assertion. If we're switching queues, we must * be switching windows. Did we just go through * ReattachThreads? */ UserAssert(pwnd != pdesk->spwndTrack); pdesk->dwDTFlags &= ~(DF_HOTTRACKING | DF_TOOLTIPACTIVE); } /*
* If we're on the client area or the user clicked, * nuke the tooltip (if any). * Since we might want to re-show the tooltip, we don't nuke it * now if we swichted windows (we'll nuke it later if needed) */ if ((htEx == HTCLIENT) || (message != WM_MOUSEMOVE)) { dwDTCancel |= DF_TOOLTIPACTIVE; } /*
* If we switched windows or crossed client/nonclinet boundaries, * end track mouse leave/hover. */ if (fNewpwndTrack || ((pdesk->htEx == HTCLIENT) ^ (htEx == HTCLIENT))) { dwDTCancel |= DF_TRACKMOUSEEVENT; } /*
* Cancel whatever is active and needs to go away */ ThreadLockAlways(pdesk->spwndTrack, &tlpwnd); xxxCancelMouseMoveTracking(pdesk->dwDTFlags, pdesk->spwndTrack, pdesk->htEx, dwDTCancel); ThreadUnlock(&tlpwnd); pdesk->dwDTFlags &= ~dwDTCancel;
SetNewState: /*
* Hottracking/tooltip on mouse move if on NC hitest and enabled */ if ((htEx != HTCLIENT) && (message == WM_MOUSEMOVE) && TestEffectUP(HOTTRACKING)) { /*
* Hottrack the new hit test area */ if (xxxHotTrack(pwnd, htEx, TRUE)) { pdesk->dwDTFlags |= DF_HOTTRACKING; }
/*
* Remove/set the tool tip. * We always do this synchronously because it doesn't mess * with pwnd's or spwnTrack's queue */ if ((pstr = IsTooltipHittest(pwnd, LOWORD(htEx))) != NULL) { PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip; ThreadLockAlways(pttwnd, &tlpwnd); if (xxxCreateTooltip(pttwnd, pstr)) { pdesk->dwDTFlags |= DF_TOOLTIP; } ThreadUnlock(&tlpwnd); } else { PTOOLTIPWND pttwnd = (PTOOLTIPWND)pdesk->spwndTooltip; ThreadLockAlways(pttwnd, &tlpwnd); xxxResetTooltip(pttwnd); ThreadUnlock(&tlpwnd); } } /* if (htEx != HTCLIENT) */
ValidateThreadLocks(NULL, PtiCurrent()->ptl, (ULONG_PTR)&pwnd, TRUE);
/*
* Update new track window if needed. */ if (fNewpwndTrack) { PWND pwndActivate;
Lock(&pdesk->spwndTrack, pwnd); /*
* Active window tracking. * If there is non-zero timeout, get the window we're supposed to activate * and set the timer. Otherwise, set the queue flag so * xxxActiveWindowTracking can do its thing. */ if ((message == WM_MOUSEMOVE) && TestUP(ACTIVEWINDOWTRACKING)) { if (UP(ACTIVEWNDTRKTIMEOUT) != 0) { pwndActivate = GetActiveTrackPwnd(pwnd, NULL); if (pwndActivate != NULL) { InternalSetTimer(pwndActivate, IDSYS_WNDTRACKING, UP(ACTIVEWNDTRKTIMEOUT), xxxSystemTimerProc, TMRF_SYSTEM); } } else { PtiCurrent()->pq->QF_flags |= QF_ACTIVEWNDTRACKING; } /* if (TestUP(ACTIVEWNDTRKZORDER)) */ } /* if (TestUP(ACTIVEWINDOWTRACKING)) */
}
/*
* Save new hit test code */ pdesk->htEx = htEx;
#if DBG
--gcReEntered; #endif
}
/***************************************************************************\
* xxxCancelMouseMoveTracking * * History * 12/07/96 GerardoB Created \***************************************************************************/ void xxxCancelMouseMoveTracking (DWORD dwDTFlags, PWND pwndTrack, int htEx, DWORD dwDTCancel) {
CheckLock(pwndTrack); /*
* Hottracking */ if ((dwDTFlags & DF_HOTTRACKING) && (dwDTCancel & DF_HOTTRACKING)) { /*
* The current state must be owned by the current queue. * Otherwise, we're about to do an inter-queue cancelation. */ UserAssert(PtiCurrent()->pq == GETPTI(pwndTrack)->pq);
xxxHotTrack(pwndTrack, htEx, FALSE); }
/*
* Tooltips */ if ((dwDTFlags & DF_TOOLTIPSHOWING) && (dwDTCancel & DF_TOOLTIP)) { PTOOLTIPWND pttwnd = (PTOOLTIPWND)PWNDTOOLTIP(pwndTrack); TL tlpwnd;
ThreadLockAlways(pttwnd, &tlpwnd); xxxResetTooltip(pttwnd); ThreadUnlock(&tlpwnd); }
/*
* Mouse Leave */ if ((dwDTFlags & DF_TRACKMOUSELEAVE) && (dwDTCancel & DF_TRACKMOUSELEAVE)) { _PostMessage(pwndTrack, ((htEx == HTCLIENT) ? WM_MOUSELEAVE : WM_NCMOUSELEAVE), 0, 0); }
/*
* Mouse Hover */ if ((dwDTFlags & DF_TRACKMOUSEHOVER) && (dwDTCancel & DF_TRACKMOUSEHOVER)) { _KillSystemTimer(pwndTrack, IDSYS_MOUSEHOVER); } }
|