|
|
//---------------------------------------------------------------------------
//
// TrackME.C (TrackMouseEvent)
//
// Created by: Sankar on 1/24/96
//
// What:
// This emulates the TrackMouseEvent() API for the Nashville project
// in comctl32.dll
//
// How:
// This subclasses the given window to get mouse messages and uses a
// high frequency timer to learn about mouse leaves.
//
//---------------------------------------------------------------------------
#include "ctlspriv.h"
#ifdef TrackMouseEvent
#undef TrackMouseEvent
#endif
extern const TCHAR FAR c_szTMEdata[];
extern DWORD g_dwHoverSelectTimeout;
#define ID_MOUSEHOVER 0xFFFFFFF0L
#define ID_MOUSELEAVE 0xFFFFFFF1L
#define TME_MOUSELEAVE_TIME (GetDoubleClickTime() / 5)
#define IsKeyDown(Key) (GetKeyState(Key) & 0x8000)
// This is the structure whose pointer gets added as a property of a window
// being tracked.
typedef struct tagTMEDATA { TRACKMOUSEEVENT TrackMouseEvent; RECT rcMouseHover; //In screen co-ordinates.
} TMEDATA, FAR *LPTMEDATA;
void NEAR TME_ResetMouseHover(LPTRACKMOUSEEVENT lpTME, LPTMEDATA lpTMEdata); LRESULT CALLBACK TME_SubclassProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData);
LPTMEDATA NEAR GetTMEdata(HWND hwnd) { LPTMEDATA lpTMEdata;
GetWindowSubclass(hwnd, TME_SubclassProc, 0, (ULONG_PTR *)&lpTMEdata);
return lpTMEdata; }
void NEAR TME_PostMouseLeave(HWND hwnd) { PostMessage(hwnd, WM_MOUSELEAVE, 0, 0L); }
void NEAR TME_CancelMouseLeave(LPTMEDATA lpTMEdata) { if(!(lpTMEdata->TrackMouseEvent.dwFlags & TME_LEAVE)) return;
// Remove the flag.
lpTMEdata->TrackMouseEvent.dwFlags &= ~(TME_LEAVE);
// We leave the timer set here since our hover implementation uses it too.
// TME_CancelTracking will kill it later.
}
void NEAR TME_CancelMouseHover(LPTMEDATA lpTMEdata) { if(!(lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER)) return;
lpTMEdata->TrackMouseEvent.dwFlags &= ~(TME_HOVER);
KillTimer(lpTMEdata->TrackMouseEvent.hwndTrack, ID_MOUSEHOVER); }
void NEAR TME_CancelTracking(LPTMEDATA lpTMEdata) { HWND hwndTrack;
//If either MouseLeave or MouseHover is ON, don't cancel tracking.
if(lpTMEdata->TrackMouseEvent.dwFlags & (TME_HOVER | TME_LEAVE)) return;
hwndTrack = lpTMEdata->TrackMouseEvent.hwndTrack;
// Uninstall our subclass callback.
RemoveWindowSubclass(hwndTrack, TME_SubclassProc, 0);
// Kill the mouseleave timer.
KillTimer(hwndTrack, ID_MOUSELEAVE);
// Free the tracking data.
LocalFree((HANDLE)lpTMEdata); }
void NEAR TME_RemoveAllTracking(LPTMEDATA lpTMEdata) { TME_CancelMouseLeave(lpTMEdata); TME_CancelMouseHover(lpTMEdata); TME_CancelTracking(lpTMEdata); }
//---------------------------------------------------------------------------
//
// TME_MouseHasLeft()
// The mouse has left the region being tracked. Send the MOUSELEAVE msg
// and then cancel all tracking.
//
//---------------------------------------------------------------------------
void NEAR TME_MouseHasLeft(LPTMEDATA lpTMEdata) { DWORD dwFlags;
//Is WM_MOUSELEAVE notification requied?
if((dwFlags = lpTMEdata->TrackMouseEvent.dwFlags) & TME_LEAVE) TME_PostMouseLeave(lpTMEdata->TrackMouseEvent.hwndTrack); //Then, do it!
// Cancel all the tracking since the mouse has left.
TME_RemoveAllTracking(lpTMEdata); }
// --------------------------------------------------------------------------
//
// TME_SubclassWndProc()
//
// The subclass proc used for TrackMouseEvent()...!
//
// --------------------------------------------------------------------------
LRESULT CALLBACK TME_SubclassProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData) { LPTMEDATA lpTMEdata = (LPTMEDATA)dwRefData;
ASSERT(lpTMEdata);
switch(message) { case WM_DESTROY: case WM_NCDESTROY: TME_RemoveAllTracking(lpTMEdata); break;
case WM_ENTERMENULOOP: // If the window being tracked enters menu mode, then we need to
// act asif the mouse has left.
// NOTE: Because when we are in menu mode, the SCREEN_CAPTURE has occurred
// and we don't see any mouse moves. This is the only way out!
// Post mouse leave and cancel all tracking!
TME_MouseHasLeft(lpTMEdata); break;
case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_NCLBUTTONDOWN: case WM_NCLBUTTONUP: case WM_NCMBUTTONDOWN: case WM_NCMBUTTONUP: case WM_NCRBUTTONDOWN: case WM_NCRBUTTONUP: //Whenever there is a mouse click, reset mouse hover.
if(lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER) TME_ResetMouseHover(&(lpTMEdata->TrackMouseEvent), lpTMEdata); break;
case WM_NCMOUSEMOVE: TME_MouseHasLeft(lpTMEdata); break;
case WM_MOUSEMOVE: { POINT Pt;
Pt.x = GET_X_LPARAM(lParam); Pt.y = GET_Y_LPARAM(lParam);
ClientToScreen(hwnd, &Pt); //Check if the mouse is within the hover rect.
if((lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER) && !PtInRect(&(lpTMEdata->rcMouseHover), Pt)) TME_ResetMouseHover(&(lpTMEdata->TrackMouseEvent), lpTMEdata); } break; }
return DefSubclassProc(hwnd, message, wParam, lParam); }
// --------------------------------------------------------------------------
//
// TME_CheckInWindow()
//
// This get the current cursor position and checks if it still lies in the
// "valid" area.
// Returns TRUE, if it lies in the valid area.
// FALSE, otherwise.
//
// --------------------------------------------------------------------------
BOOL NEAR TME_CheckInWindow(LPTRACKMOUSEEVENT lpTME, LPPOINT lpPt) { POINT pt; HWND hwnd; // Given window.
HWND hwndPt; //Window from the given point.
HWND hwndCapture;
hwnd = lpTME->hwndTrack; //Given window handle.
//See if anyone has captured the mouse input.
if((hwndCapture = GetCapture()) && IsWindow(hwndCapture)) { // If tracking is required for a window other than the one that
// has the capture, forget it! It is not possible!
if(hwndCapture != hwnd) return(FALSE); }
GetCursorPos(&pt); //Get cursor point in screen co-ordinates.
if (!hwndCapture) { hwndPt = WindowFromPoint(pt);
if (!hwndPt || !IsWindow(hwndPt) || (hwnd != hwndPt)) return FALSE;
if (SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM((SHORT)pt.x, (SHORT)pt.y)) != HTCLIENT) { return FALSE; } }
// The current point falls on the same area of the same window.
// It is a valid location.
if (lpPt) *lpPt = pt;
return(TRUE); }
// --------------------------------------------------------------------------
// TME_MouseLeaveTimer()
//
// Timer callback for WM_MOUSELEAVE generation and cancelling HOVER!
//
// --------------------------------------------------------------------------
VOID CALLBACK TME_MouseLeaveTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD dwTime) { LPTMEDATA lpTMEdata;
if(!(lpTMEdata = GetTMEdata(hwnd))) return;
// YIELD!!!
if(TME_CheckInWindow(&(lpTMEdata->TrackMouseEvent), NULL)) return; //The mouse is still in the valid region. So, do nothing.
if (!IsWindow(hwnd)) return;
//The mouse has left the valid region. So, post mouse-leave if requested
//Because we are cancelling mouse-leave, we need to cancel mouse-hover too!
// There can be no hover tracking, if the mouse has already left!
TME_MouseHasLeft(lpTMEdata); }
WPARAM NEAR GetMouseKeyFlags() { WPARAM wParam = 0;
if (IsKeyDown(VK_LBUTTON)) wParam |= MK_LBUTTON; if (IsKeyDown(VK_RBUTTON)) wParam |= MK_RBUTTON; if (IsKeyDown(VK_MBUTTON)) wParam |= MK_MBUTTON; if (IsKeyDown(VK_SHIFT)) wParam |= MK_SHIFT; if (IsKeyDown(VK_CONTROL)) wParam |= MK_CONTROL;
return wParam; }
// --------------------------------------------------------------------------
// TME_MouseHoverTimer()
//
// Timer callback for WM_MOUSEHOVER/WM_NCMOUSEHOVER generation.
//
// --------------------------------------------------------------------------
VOID CALLBACK TME_MouseHoverTimer(HWND hwnd, UINT msg, UINT_PTR id, DWORD dwTime) { POINT pt; WPARAM wParam; LPTMEDATA lpTMEdata;
if (!(lpTMEdata = GetTMEdata(hwnd))) return;
//BOGUS: we can not detect hwndSysModal from here!
//Also, tracking is for a per-window basis now!
//
// BOGUS: We don't have to worry about JournalPlayback?
//pt = fJournalPlayback? Lpq(hwnd->hq)->ptLast : ptTrueCursor;
// YIELD!!!
if(!TME_CheckInWindow(&(lpTMEdata->TrackMouseEvent), &pt)) { // Mouse has left the valid region of the window. So, cancel all
// the tracking.
TME_MouseHasLeft(lpTMEdata); return; }
if (!IsWindow(hwnd)) return;
if (!PtInRect(&(lpTMEdata->rcMouseHover), pt)) { // Mouse has gone out of the hover rectangle. Reset the hovering.
TME_ResetMouseHover(&(lpTMEdata->TrackMouseEvent), lpTMEdata); return; }
//
// set up to check the tolerance and
//
wParam = GetMouseKeyFlags(); ScreenToClient(hwnd, &pt);
//Mouse is still within the hover rectangle. Let's post hover msg
PostMessage(hwnd, WM_MOUSEHOVER, wParam, MAKELPARAM(pt.x, pt.y));
//And then cancel the hovering.
TME_CancelMouseHover(lpTMEdata); TME_CancelTracking(lpTMEdata); //Cancel the tracking, if needed.
}
BOOL NEAR TME_SubclassWnd(LPTMEDATA lpTMEdata) { BOOL fResult;
fResult = SetWindowSubclass(lpTMEdata->TrackMouseEvent.hwndTrack, TME_SubclassProc, 0, (ULONG_PTR)lpTMEdata);
ASSERT(fResult); return fResult; }
void NEAR TME_ResetMouseLeave(LPTRACKMOUSEEVENT lpTME, LPTMEDATA lpTMEdata) { //See if already MouseLeave is being tracked.
if(lpTMEdata->TrackMouseEvent.dwFlags & TME_LEAVE) return; // Nothing else to do.
//Else, set the flag.
lpTMEdata ->TrackMouseEvent.dwFlags |= TME_LEAVE;
//Set the high frequency Timer.
SetTimer(lpTME->hwndTrack, ID_MOUSELEAVE, TME_MOUSELEAVE_TIME, TME_MouseLeaveTimer); }
void NEAR TME_ResetMouseHover(LPTRACKMOUSEEVENT lpTME, LPTMEDATA lpTMEdata) { DWORD dwMouseHoverTime; POINT pt;
// Even if the hover tracking is already happening, the caller might
// change the timer value, restart the timer or change the hover
// rectangle.
lpTMEdata->TrackMouseEvent.dwFlags |= TME_HOVER;
dwMouseHoverTime = lpTME->dwHoverTime; if (!dwMouseHoverTime || (dwMouseHoverTime == HOVER_DEFAULT)) dwMouseHoverTime = (g_dwHoverSelectTimeout ? g_dwHoverSelectTimeout : GetDoubleClickTime()*4/5); // BUGBUG: Can't we remember this?
GetCursorPos(&pt);
//
// update the tolerance rectangle for the hover window.
//
*((POINT *)&(lpTMEdata->rcMouseHover.left)) = *((POINT *)&(lpTMEdata->rcMouseHover.right)) = pt;
//BOGUS: Can we use globals to remeber these metrics. What about NT?
InflateRect(&(lpTMEdata->rcMouseHover), g_cxDoubleClk/2, g_cyDoubleClk/2); // We need to remember the timer interval we are setting. This value
// needs to be returned when TME_QUERY is used.
lpTME->dwHoverTime = dwMouseHoverTime; lpTMEdata->TrackMouseEvent.dwHoverTime = dwMouseHoverTime; SetTimer(lpTME->hwndTrack, ID_MOUSEHOVER, dwMouseHoverTime, TME_MouseHoverTimer); }
// --------------------------------------------------------------------------
// QueryTrackMouseEvent()
//
// Fills in a TRACKMOUSEEVENT structure describing current tracking state
// for a given window. The given window is in lpTME->hwndTrack.
//
// --------------------------------------------------------------------------
BOOL NEAR QueryTrackMouseEvent(LPTRACKMOUSEEVENT lpTME) { HWND hwndTrack; LPTMEDATA lpTMEdata;
//
// if there isn't anything being tracked get out
//
if((!(hwndTrack = lpTME->hwndTrack)) || !IsWindow(hwndTrack)) goto Sorry;
if(!(lpTMEdata = GetTMEdata(hwndTrack))) goto Sorry;
if(!(lpTMEdata->TrackMouseEvent.dwFlags & (TME_HOVER | TME_LEAVE))) goto Sorry;
//
// fill in the requested information
//
lpTME->dwFlags = lpTMEdata->TrackMouseEvent.dwFlags;
if (lpTMEdata->TrackMouseEvent.dwFlags & TME_HOVER) lpTME->dwHoverTime = lpTMEdata->TrackMouseEvent.dwHoverTime; else lpTME->dwHoverTime = 0;
goto Done;
Sorry: // zero out the struct
lpTME->dwFlags = 0; lpTME->hwndTrack = NULL; lpTME->dwHoverTime = 0;
Done: return TRUE; }
// --------------------------------------------------------------------------
// EmulateTrackMouseEvent()
//
// emulate API for requesting extended mouse notifications (hover, leave...)
//
// --------------------------------------------------------------------------
BOOL WINAPI EmulateTrackMouseEvent(LPTRACKMOUSEEVENT lpTME) { HWND hwnd; DWORD dwFlags; LPTMEDATA lpTMEdata;
if (lpTME->dwFlags & ~TME_VALID) return FALSE;
#ifdef TME_NONCLIENT
//
// this implementation does not handle TME_NONCLIENT (anymore)
// we agreed with the NT team to rip it out until the system uses it...
//
if (lpTME->dwFlags & TME_NONCLIENT) return FALSE; #endif
//
// implement queries separately
//
if (lpTME->dwFlags & TME_QUERY) return QueryTrackMouseEvent(lpTME); //
// Check the validity of the request.
//
hwnd = lpTME->hwndTrack; dwFlags = lpTME->dwFlags;
if (!IsWindow(hwnd)) return FALSE;
// Check if the mouse is currently in a valid position
// Use GetCursorPos() to get the mouse position and then check if
// it lies within the client/non-client portion of the window as
// defined in this call;
// YIELD!!!
if(!TME_CheckInWindow(lpTME, NULL)) { //If the mouse leave is requested when the mouse is already outside
// the window, then generate one mouse leave immly.
if((dwFlags & TME_LEAVE) && !(dwFlags & TME_CANCEL)) TME_PostMouseLeave(hwnd); //Because it is an invalid request, we return immly.
return(TRUE); }
if (!IsWindow(hwnd)) return FALSE;
//It is a valid request, either to install or remove tracking.
//See if we already have tracking for this window.
if(!(lpTMEdata = GetTMEdata(hwnd))) { //We are not tracking this window already.
if(dwFlags & TME_CANCEL) return(TRUE); //There is nothing to cancel; Ignore!
//Do they want any tracking at all?
ASSERT(dwFlags & (TME_HOVER | TME_LEAVE));
//Allocate global mem to remember the tracking data
if(!(lpTMEdata = (LPTMEDATA)LocalAlloc(LPTR, sizeof(TMEDATA)))) return(FALSE);
// copy in the hwnd
lpTMEdata->TrackMouseEvent.hwndTrack = lpTME->hwndTrack;
// Make sure our subclass callback is installed.
if (!TME_SubclassWnd(lpTMEdata)) { TME_CancelTracking(lpTMEdata); return(FALSE); } }
//Else fall through!
if(dwFlags & TME_CANCEL) { if(dwFlags & TME_HOVER) TME_CancelMouseHover(lpTMEdata); if(dwFlags & TME_LEAVE) TME_CancelMouseLeave(lpTMEdata);
// If both hover and leave are cancelled, then we don't need any
// tracking.
TME_CancelTracking(lpTMEdata);
return(TRUE); // Cancelled whatever they asked for.
}
if(dwFlags & TME_HOVER) TME_ResetMouseHover(lpTME, lpTMEdata);
if(dwFlags & TME_LEAVE) TME_ResetMouseLeave(lpTME, lpTMEdata);
return(TRUE); }
typedef BOOL (WINAPI* PFNTME)(LPTRACKMOUSEEVENT);
PFNTME g_pfnTME = NULL;
// --------------------------------------------------------------------------
// _TrackMouseEvent() entrypoint
//
// calls TrackMouseEvent if present, otherwise uses EmulateTrackMouseEvent
//
// --------------------------------------------------------------------------
BOOL WINAPI _TrackMouseEvent(LPTRACKMOUSEEVENT lpTME) { if (!g_pfnTME) { HMODULE hmod = GetModuleHandle(TEXT("USER32"));
if (hmod) g_pfnTME = (PFNTME)GetProcAddress(hmod, "TrackMouseEvent");
if (!g_pfnTME) g_pfnTME = EmulateTrackMouseEvent; }
return g_pfnTME(lpTME); }
|