Mouse Enter/Leave events for systray icons
Posted: Sat Aug 01, 2009 11:48 pm
I read the coding question here: http://www.purebasic.fr/english/viewtopic.php?t=38276
and figured it wouldn't be too difficult to monitor the mouse in/out of systray icons. Turns out it was a bit more involved than I had thought. Processing the mousemove message sent automatically was simple enough, but how to know when the mouse leaves? Nonetheless I believe I have something usable:
A little test prog:
If you have two systray icons, you may get an enter message before you get the leave message from the other one. But you'll always get it.
Tested on XP SP2 only. If it works on other versions, please let me know.
and figured it wouldn't be too difficult to monitor the mouse in/out of systray icons. Turns out it was a bit more involved than I had thought. Processing the mousemove message sent automatically was simple enough, but how to know when the mouse leaves? Nonetheless I believe I have something usable:
Code: Select all
;======================================================================
; Library: MonitorSysTrayMouseEvents
; Author: Lloyd Gallant (netmaestro)
; Date: August 1, 2009
; Target OS: Microsoft Windows XP (maybe more)
; Target Compiler: PureBasic 4.31 and later
; License: Unrestricted, no warranty expressed or implied
;======================================================================
#MSG_ST_MOUSEENTER = #WM_APP+1
#MSG_ST_MOUSELEAVE = #WM_APP+2
Structure TRAYICONRECT
id.l
loc.RECT
hot.b
EndStructure
NewList pbTrayButton.TRAYICONRECT()
Procedure Find_PB_TrayIcons()
Structure TRAYDATA
hwnd.l
uID.l
uCallbackMessage.l
Reserved.l[2]
hIcon.l
EndStructure
Shared pbTrayButton()
; Find the System Tray Icon Toolbar
Protected hwnd
hWnd = FindWindow_("Shell_TrayWnd", #Null)
If hWnd
hWnd = FindWindowEx_(hWnd, #Null, "TrayNotifyWnd", #Null)
If hWnd
hWnd = FindWindowEx_(hWnd,#Null, "SysPager", #Null)
If hWnd
hTray = FindWindowEx_(hWnd, #Null, "ToolbarWindow32", #Null)
Else
ProcedureReturn 0
EndIf
Else
ProcedureReturn 0
EndIf
Else
ProcedureReturn 0
EndIf
; If we're still here we found the systray toolbar
count = SendMessage_(hTray, #TB_BUTTONCOUNT, 0, 0)
Dim button.TBBUTTON (count)
Dim button_td.TRAYDATA(count)
; Put it in the can opener
dwExplorerThreadId=GetWindowThreadProcessId_(hTray, @dwExplorerProcessId)
hProc = OpenProcess_(#PROCESS_ALL_ACCESS, #False, dwExplorerProcessId)
*lpData = VirtualAllocEx_(hProc, #Null, SizeOf(TBBUTTON)*count, #MEM_COMMIT, #PAGE_READWRITE)
*lpRect = VirtualAllocEx_(hProc, #Null, SizeOf(RECT)*count, #MEM_COMMIT, #PAGE_READWRITE)
; Enumerate the systray buttons, ignore all but our own
For i = 0 To count-1
SendMessage_(hTray, #TB_GETBUTTON, i, *lpData+i*SizeOf(TBBUTTON) )
ReadProcessMemory_(hProc, *lpData+(i*SizeOf(TBBUTTON)), @button.TBBUTTON(i), SizeOf(TBBUTTON), #Null)
ReadProcessMemory_(hProc, button(i)\dwData, @button_td.TRAYDATA(i), SizeOf(TRAYDATA), #Null)
; Is it one of ours?
If button_td(i)\uCallbackMessage = 12501 ; PB team-chosen callback messageid, if true it belongs to us
AddElement(pbTrayButton())
SendMessage_(hTray, #TB_GETITEMRECT, i, *lpRect+i*SizeOf(RECT))
ReadProcessMemory_(hProc, *lpRect+(i*SizeOf(RECT)), @pbTrayButton()\loc, SizeOf(RECT), #Null)
pbTraybutton()\id = button_td(i)\uid
MapWindowPoints_(hTray, 0, pbTrayButton()\loc,2)
EndIf
Next
; Wipe off the can opener and put it away
VirtualFreeEx_(hProc, *lpData, #Null, #MEM_RELEASE)
VirtualFreeEx_(hProc, *lpRect, #Null, #MEM_RELEASE)
CloseHandle_(hProc)
; Return the number of our Tray icons found and processed
ProcedureReturn ListSize(pbTrayButton())
EndProcedure
Procedure MonitorTray(window)
Shared pbTrayButton()
If Find_PB_TrayIcons()
Repeat
ForEach pbTrayButton()
GetCursorPos_(@cp.POINT)
If PtInRect_(pbTrayButton()\loc, cp\x|(cp\y<<32))
If Not pbTrayButton()\hot
pbTrayButton()\hot = 1
If IsWindow_(window)
PostMessage_(window, #MSG_ST_MOUSEENTER, pbTrayButton()\id, 0)
EndIf
EndIf
Else
If pbTrayButton()\hot
pbTrayButton()\hot = 0
If IsWindow_(window)
PostMessage_(window, #MSG_ST_MOUSELEAVE, pbTrayButton()\id, 0)
EndIf
EndIf
EndIf
Delay(1)
Next
ForEver
EndIf
EndProcedure
Procedure ProcessSystrayMouseMessages(window)
CreateThread(@MonitorTray(),window)
EndProcedure
Code: Select all
IncludeFile "MonitorSysTrayMouseEvents.pbi"
OpenWindow(0, 100, 150, 300, 100, "PureBasic - SysTray Example", #PB_Window_SystemMenu)
IconName$ = #PB_Compiler_Home+"examples\sources\data\CdPlayer.ico"
AddSysTrayIcon(0, WindowID(0), LoadImage(0, IconName$))
AddSysTrayIcon(1, WindowID(0), LoadImage(0, IconName$))
SysTrayIconToolTip(1, "Icon 1")
ProcessSystrayMouseMessages(WindowID(0))
Repeat
EventID = WaitWindowEvent()
Select EventID
Case #MSG_ST_MOUSEENTER
Debug "Entered Systray Icon " + Str(EventwParam())
Case #MSG_ST_MOUSELEAVE
Debug "Left Systray Icon " + Str(EventwParam())
EndSelect
Until EventID = #PB_Event_CloseWindow
Tested on XP SP2 only. If it works on other versions, please let me know.