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

1204 lines
35 KiB

/****************************** 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);
}
}