1{%MainUnit win32wsextctrls.pp} 2{ $Id: win32trayicon.inc 11994 2007-09-10 22:30:15Z marc $ } 3{****************************************************************************** 4 Implementation of TWin32WSCustomTrayIcon 5 6 ***************************************************************************** 7 This file is part of the Lazarus Component Library (LCL) 8 9 See the file COPYING.modifiedLGPL.txt, included in this distribution, 10 for details about the license. 11 ***************************************************************************** 12} 13 14{ TWin32WSCustomTrayIcon } 15 16type 17 // IE 5+ version of TNotifyIconDataW 18 TNotifyIconDataW2 = record 19 cbSize: DWORD; 20 hWnd: HWND; 21 uID: UINT; 22 uFlags: UINT; 23 uCallbackMessage: UINT; 24 hIcon: HICON; 25 szTip: array [0..127] of WideChar; 26 dwState: DWORD; 27 dwStateMask: DWORD; 28 szInfo: array [0..255] of WideChar; 29 u: record 30 case longint of 31 0 : ( uTimeout : UINT ); 32 1 : ( uVersion : UINT ); 33 end; 34 szInfoTitle: array[0..63] of WideChar; 35 dwInfoFlags: DWORD; 36 end; 37 38const 39 szClassName = 'TTrayIconClass'; 40 szAppTitle = 'apptitle'; 41 uIDTrayIcon = 25; 42 43var 44 msgTaskbarRestart: DWord = DWord(-1); 45 46{******************************************************************* 47* TrayWndProc () 48* 49* DESCRIPTION: Window procedure that processes messages for the 50* systray icon 51* 52* PARAMETERS: Standard Mouse Messages have this parameters: 53* 54* fwKeys = wParam; // key flags 55* xPos = LOWORD(lParam); // horizontal position of cursor 56* yPos = HIWORD(lParam); // vertical position of cursor 57* //* Those positions seam to be wrong 58* // Use Mouse.CursorPos instead 59* 60* RETURNS: A pointer to the newly created object 61* 62*******************************************************************} 63function TrayWndProc(Handle: HWND; iMsg: UINT; WParam_: WPARAM; LParam_:LPARAM):LRESULT; stdcall; 64var 65 pt: TPoint; 66 vwsTrayIcon: TCustomTrayIcon; 67begin 68 if iMsg = WM_USER + uIDTrayIcon then 69 begin 70 vwsTrayIcon := TCustomTrayIcon(PtrUInt(GetWindowLong(Handle, GWL_USERDATA))); 71 case LParam_ of 72 WM_RBUTTONUP: 73 begin 74 pt := Mouse.CursorPos; 75 if Assigned(vwsTrayIcon.OnMouseUp) then 76 vwsTrayIcon.OnMouseUp(Application, mbRight, KeysToShiftState(WParam_), pt.x, pt.y); 77 if Assigned(vwsTrayIcon.PopUpMenu) then 78 begin 79 // Apparently SetForegroundWindow and PostMessage are necessary 80 // because we're invoking the shortcut menu from a notification icon 81 // This is an attempt to prevent from messing with the Z-order 82 SetForegroundWindow(Handle); 83 PostMessage(Handle, WM_NULL, 0, 0); 84 vwsTrayIcon.PopUpMenu.Popup(pt.x, pt.y); 85 end; 86 end; 87 WM_RBUTTONDOWN: 88 if Assigned(vwsTrayIcon.OnMouseDown) then 89 begin 90 pt := Mouse.CursorPos; 91 vwsTrayIcon.OnMouseDown(Application, mbRight, KeysToShiftState(WParam_), pt.x, pt.y); 92 end; 93 WM_RBUTTONDBLCLK: 94 if Assigned(vwsTrayIcon.OnDblClick) then 95 vwsTrayIcon.OnDblClick(Application); 96 WM_MBUTTONDOWN: 97 if Assigned(vwsTrayIcon.OnMouseDown) then 98 begin 99 pt := Mouse.CursorPos; 100 vwsTrayIcon.OnMouseDown(Application, mbMiddle, KeysToShiftState(WParam_), pt.x, pt.y); 101 end; 102 WM_MBUTTONUP: 103 if Assigned(vwsTrayIcon.OnMouseUp) then 104 begin 105 pt := Mouse.CursorPos; 106 vwsTrayIcon.OnMouseUp(Application, mbMiddle, KeysToShiftState(WParam_), pt.x, pt.y); 107 end; 108 WM_LBUTTONUP: 109 begin 110 pt := Mouse.CursorPos; 111 if Assigned(vwsTrayIcon.OnMouseUp) then 112 vwsTrayIcon.OnMouseUp(Application, mbLeft, KeysToShiftState(WParam_), pt.x, pt.y); 113 if Assigned(vwsTrayIcon.OnClick) then 114 vwsTrayIcon.OnClick(Application); 115 end; 116 WM_LBUTTONDOWN: 117 if Assigned(vwsTrayIcon.OnMouseDown) then 118 begin 119 pt := Mouse.CursorPos; 120 vwsTrayIcon.OnMouseDown(Application, mbLeft, KeysToShiftState(WParam_), pt.x, pt.y); 121 end; 122 WM_LBUTTONDBLCLK: 123 if Assigned(vwsTrayIcon.OnDblClick) then 124 vwsTrayIcon.OnDblClick(Application); 125 WM_MOUSEMOVE: 126 if Assigned(vwsTrayIcon.OnMouseMove) then 127 begin 128 pt := Mouse.CursorPos; 129 vwsTrayIcon.OnMouseMove(Application, KeysToShiftState(WParam_), pt.x, pt.y); 130 end; 131 end; 132 133 Result := 1; 134 Exit; 135 end 136 else 137 if iMsg = WM_CREATE then 138 begin 139 msgTaskbarRestart := RegisterWindowMessage('TaskbarCreated'); 140 SetWindowLong(Handle, GWL_USERDATA, PtrInt(PCREATESTRUCT(LParam_)^.lpCreateParams)); 141 end 142 else 143 if (iMsg = msgTaskbarRestart) then 144 begin 145 // add taskbar icon 146 vwsTrayIcon := TCustomTrayIcon(PtrUInt(GetWindowLong(Handle, GWL_USERDATA))); 147 if Assigned(vwsTrayIcon) then 148 TWin32WSCustomTrayIcon.AddIcon(vwsTrayIcon); 149 end; 150 151 Result := DefWindowProc(Handle, iMsg, WParam_, LParam_); 152end; 153 154{ TWin32WSCustomTrayIcon } 155 156class function TWin32WSCustomTrayIcon.AddIcon(ATrayIcon: TCustomTrayIcon): Boolean; 157var 158 tnidw: TNotifyIconDataW2; 159 WideBuffer: widestring; 160begin 161 // Fill TNotifyIconDataW 162 FillChar(tnidw, SizeOf(tnidw), 0); 163 tnidw.cbSize := SizeOf(tnidw); 164 tnidw.hWnd := ATrayIcon.Handle; 165 tnidw.uID := uIDTrayIcon; 166 tnidw.uFlags := NIF_MESSAGE or NIF_ICON; 167 if (ATrayIcon.Hint <> '') then tnidw.uFlags := tnidw.uFlags or NIF_TIP; 168 tnidw.uCallbackMessage := WM_USER + uIDTrayIcon; 169 tnidw.hIcon := ATrayIcon.Icon.Handle; 170 171 WideBuffer := UTF8ToUTF16(ATrayIcon.Hint); 172 WideStrLCopy(@tnidw.szTip, PWideChar(WideBuffer), 127); 173 174 Result := Shell_NotifyIconW(NIM_ADD, @tnidw); 175 if not Result then 176 begin 177 // Try old version of TNotifyIconDataW 178 tnidw.cbSize := SizeOf(TNotifyIconDataW); 179 WideStrLCopy(@tnidw.szTip, PWideChar(WideBuffer), 63); 180 Result := Shell_NotifyIconW(NIM_MODIFY, @tnidw); 181 end; 182end; 183 184{******************************************************************* 185* TWin32WSCustomTrayIcon.Hide () 186* 187* DESCRIPTION: Hides the main tray icon of the program 188* 189* PARAMETERS: None 190* 191* RETURNS: True if sucessfull, otherwise False 192* 193*******************************************************************} 194 195class function TWin32WSCustomTrayIcon.Hide(const ATrayIcon: TCustomTrayIcon): Boolean; 196var 197 tnid: TNotifyIconData; 198begin 199 // Fill TNotifyIconData 200 FillChar(tnid, SizeOf(tnid), 0); 201 tnid.cbSize := SizeOf(TNotifyIconData); 202 tnid.hWnd := ATrayIcon.Handle; 203 tnid.uID := uIDTrayIcon; 204 205 // Remove the icon 206 Result := Shell_NotifyIconA(NIM_DELETE, @tnid); 207 208 // Destroys the helper Windows 209 SendMessage(ATrayIcon.Handle, WM_CLOSE, 0, 0); 210 SendMessage(ATrayIcon.Handle, WM_DESTROY, 0, 0); 211end; 212 213{******************************************************************* 214* TWin32WSCustomTrayIcon.Show () 215* 216* DESCRIPTION: Shows the main tray icon of the program 217* 218* PARAMETERS: None 219* 220* RETURNS: True if sucessfull, otherwise False 221* 222*******************************************************************} 223class function TWin32WSCustomTrayIcon.Show(const ATrayIcon: TCustomTrayIcon): Boolean; 224var 225 Window: Windows.TWndClassEx; 226begin 227 if not GetClassInfo(hInstance, szClassName, @Window) then 228 begin 229 ZeroMemory(@Window, SizeOf(TWndClassEx)); 230 Window.cbSize := SizeOf(TWndClassEx); 231 Window.style := CS_OWNDC; 232 Window.lpfnWndProc := @TrayWndProc; 233 Window.cbClsExtra := 0; 234 Window.cbWndExtra := 0; 235 Window.hInstance := hInstance; 236 Window.hCursor := Windows.LoadCursor(0, IDC_ARROW); 237 Window.hbrBackground := HBRUSH(GetStockObject(NULL_BRUSH)); 238 Window.lpszMenuName := nil; 239 Window.lpszClassName := szClassName; 240 Windows.RegisterClassEx(Window); 241 end; 242 243 ATrayIcon.Handle := CreateWindowEx( 244 0, //* Ensure that there will be no button in the bar */ 245 szClassName, //* Name of the registered class */ 246 szAppTitle, //* Title of the window */ 247 0, //* Style of the window */ 248 0, //* x-position (at beginning) */ 249 0, //* y-position (at beginning) */ 250 CW_USEDEFAULT, //* window width */ 251 CW_USEDEFAULT, //* window height */ 252 0, //* handle to parent or owner window */ 253 0, //* handle to menu */ 254 hInstance, //* handle to application instance */ 255 ATrayIcon); //* pointer to window-creation data */ 256 257 Result := AddIcon(ATrayIcon); 258end; 259 260{******************************************************************* 261* TWin32WSCustomTrayIcon.InternalUpdate () 262* 263* DESCRIPTION: Makes modifications to the Icon while running 264* i.e. without hiding it and showing again 265* 266*******************************************************************} 267class procedure TWin32WSCustomTrayIcon.InternalUpdate(const ATrayIcon: TCustomTrayIcon); 268var 269 tnidw: TNotifyIconDataW2; 270 WideBuffer: widestring; 271begin 272 // Fill TNotifyIconDataW 273 FillChar(tnidw, SizeOf(tnidw), 0); 274 tnidw.cbSize := SizeOf(tnidw); 275 tnidw.hWnd := ATrayIcon.Handle; 276 tnidw.uID := uIDTrayIcon; 277 tnidw.hIcon := ATrayIcon.Icon.Handle; 278 tnidw.uFlags := NIF_TIP or NIF_ICON; 279 280 WideBuffer := UTF8ToUTF16(ATrayIcon.Hint); 281 WideStrLCopy(@tnidw.szTip, PWideChar(WideBuffer), 127); 282 283 if not Shell_NotifyIconW(NIM_MODIFY, @tnidw) then 284 begin 285 // Try old version of TNotifyIconDataW 286 tnidw.cbSize := SizeOf(TNotifyIconDataW); 287 WideStrLCopy(@tnidw.szTip, PWideChar(WideBuffer), 63); 288 Shell_NotifyIconW(NIM_MODIFY, @tnidw); 289 end; 290end; 291 292{******************************************************************* 293* TWin32WSCustomTrayIcon.ShowBalloonHint () 294* 295* DESCRIPTION: Shows a small message balloon near the tray icon 296* 297* RETURNS: False if the default cross-platform hint should be used 298* True if a platform-specific hint will be used 299* 300*******************************************************************} 301class function TWin32WSCustomTrayIcon.ShowBalloonHint(const ATrayIcon: TCustomTrayIcon): Boolean; 302const 303 FlagsMap: array[TBalloonFlags] of dword = (NIIF_NONE, NIIF_INFO, NIIF_WARNING, NIIF_ERROR); 304var 305 NotifyData: TNotifyIconDataW2; 306 w: WideString; 307begin 308 NotifyData.cbSize:=SizeOf(NotifyData); 309 NotifyData.hWnd := ATrayIcon.Handle; 310 NotifyData.uID := uIDTrayIcon; 311 NotifyData.uFlags:=NIF_INFO; 312 NotifyData.u.uTimeout:=ATrayIcon.BalloonTimeout; 313 w:=UTF8ToUTF16(ATrayIcon.BalloonHint); 314 WideStrLCopy(@NotifyData.szInfo, PWideChar(w), High(NotifyData.szInfo)); 315 w:=UTF8ToUTF16(ATrayIcon.BalloonTitle); 316 WideStrLCopy(@NotifyData.szInfoTitle, PWideChar(w), High(NotifyData.szInfoTitle)); 317 NotifyData.dwInfoFlags:=FlagsMap[ATrayIcon.BalloonFlags]; 318 319 Result:= Shell_NotifyIconW(NIM_MODIFY, @NotifyData); 320end; 321 322{******************************************************************* 323* TWin32WSCustomTrayIcon.GetPosition () 324* 325* DESCRIPTION: Returns the position of the tray icon on the display. 326* This function is utilized to show message boxes near 327* the icon 328* 329*******************************************************************} 330function EnumChildProc(handle: HWND; lp: LParam): LongBool; stdcall; 331begin 332 if Pos('ToolbarWindow32', WndClassName(handle)) > 0 then 333 begin 334 LParam(Pointer(lp)^) := handle; 335 Result := False; 336 end 337 else 338 Result := True; 339end; 340 341class function TWin32WSCustomTrayIcon.GetPosition(const ATrayIcon: TCustomTrayIcon): TPoint; 342var 343 hWndTaskbar, hWndTray: HWND; 344 TaskbarRect, TrayRect: TRect; 345 TaskbarMonitor: TMonitor; 346begin 347 Result := Point(0, 0); 348 349 { First we get the Taskbar window and it's screen position } 350 hWndTaskbar := FindWindow('Shell_TrayWnd', nil); 351 352 if hWndTaskbar = 0 then Exit; 353 354 Windows.GetWindowRect(hWndTaskbar, @TaskbarRect); 355 356 hWndTray := ATrayIcon.Handle; 357 358 { Then we locate inside the Tray area, which is just a Toolbar control } 359 EnumChildWindows(hWndTaskbar, @EnumChildProc, LParam(@hWndTray)); 360 361 if hWndTray = 0 then Exit; 362 363 { And we get the size of that control } 364 Windows.GetWindowRect(hWndTray, @TrayRect); 365 // OBS: Here TrayRect seams to have a wrong value, so we don't use it 366 367 { We need this in order to "normalize" in later if statements. This is required to get right coordinates on multiple monitors } 368 TaskbarMonitor := Screen.MonitorFromWindow(hWndTaskbar); 369 370 { Returns an aproximate position of the tray area } 371 if (TaskbarRect.Top - TaskbarMonitor.Top = 0) and (TaskbarRect.Left - TaskbarMonitor.Left = 0) then 372 begin 373 { Taskbar is at the top of the monitor area OR on the left side of the monitor area } 374 Result.X := TaskbarRect.Right; 375 Result.Y := TaskbarRect.Bottom; 376 end 377 else if (TaskbarRect.Left - TaskbarMonitor.Left = 0) then 378 begin 379 { Taskbar is at the bottom of the monitor area } 380 Result.X := TaskbarRect.Right; 381 Result.Y := TaskbarRect.Top; 382 end 383 else 384 begin 385 Result.X := TaskbarRect.Left; 386 Result.Y := TaskbarRect.Bottom; 387 { Taskbar is on the right side of the monitor area } 388 end; 389end; 390 391 392