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
1204 lines
35 KiB
/****************************** 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);
|
|
}
|
|
}
|