2 * Copyright (C) 2004 Mike Hearn, for CodeWeavers
3 * Copyright (C) 2005 Robert Shearman
4 * Copyright (C) 2008 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define NONAMELESSUNION
24 #define _WIN32_IE 0x500
29 #include <wine/debug.h>
30 #include <wine/list.h>
32 #include "explorer_private.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(systray
);
37 struct notify_data
/* platform-independent format for NOTIFYICONDATA */
42 UINT uCallbackMessage
;
51 WCHAR szInfoTitle
[64];
54 /* data for the icon bitmap */
61 static int (CDECL
*wine_notify_icon
)(DWORD
,NOTIFYICONDATAW
*);
63 /* an individual systray icon, unpacked from the NOTIFYICONDATA and always in unicode */
67 HICON image
; /* the image to render */
68 HWND owner
; /* the HWND passed in to the Shell_NotifyIcon call */
69 HWND tooltip
; /* Icon tooltip */
70 UINT state
; /* state flags */
71 UINT id
; /* the unique id given by the app */
72 UINT callback_message
;
73 int display
; /* index in display list, or -1 if hidden */
74 WCHAR tiptext
[128]; /* Tooltip text. If empty => tooltip disabled */
75 WCHAR info_text
[256]; /* info balloon text */
76 WCHAR info_title
[64]; /* info balloon title */
77 UINT info_flags
; /* flags for info balloon */
78 UINT info_timeout
; /* timeout for info balloon */
79 HICON info_icon
; /* info balloon icon */
80 UINT version
; /* notify icon api version */
83 static struct list icon_list
= LIST_INIT( icon_list
);
94 static struct list taskbar_buttons
= LIST_INIT( taskbar_buttons
);
96 static HWND tray_window
;
98 static unsigned int alloc_displayed
;
99 static unsigned int nb_displayed
;
100 static struct icon
**displayed
; /* array of currently displayed icons */
102 static BOOL hide_systray
, enable_shell
;
103 static int icon_cx
, icon_cy
, tray_width
, tray_height
;
104 static int start_button_width
, taskbar_button_width
;
105 static WCHAR start_label
[50];
107 static struct icon
*balloon_icon
;
108 static HWND balloon_window
;
110 #define MIN_DISPLAYED 8
111 #define ICON_BORDER 2
113 #define BALLOON_CREATE_TIMER 1
114 #define BALLOON_SHOW_TIMER 2
116 #define BALLOON_CREATE_TIMEOUT 2000
117 #define BALLOON_SHOW_MIN_TIMEOUT 10000
118 #define BALLOON_SHOW_MAX_TIMEOUT 30000
120 #define WM_POPUPSYSTEMMENU 0x0313
122 static void do_hide_systray(void);
123 static void do_show_systray(void);
125 /* Retrieves icon record by owner window and ID */
126 static struct icon
*get_icon(HWND owner
, UINT id
)
130 /* search for the icon */
131 LIST_FOR_EACH_ENTRY( this, &icon_list
, struct icon
, entry
)
132 if ((this->id
== id
) && (this->owner
== owner
)) return this;
137 static RECT
get_icon_rect( struct icon
*icon
)
141 rect
.right
= tray_width
- icon_cx
* icon
->display
;
142 rect
.left
= rect
.right
- icon_cx
;
143 rect
.top
= (tray_height
- icon_cy
) / 2;
144 rect
.bottom
= rect
.top
+ icon_cy
;
148 static void init_common_controls(void)
150 static BOOL initialized
= FALSE
;
154 INITCOMMONCONTROLSEX init_tooltip
;
156 init_tooltip
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
157 init_tooltip
.dwICC
= ICC_TAB_CLASSES
|ICC_STANDARD_CLASSES
;
159 InitCommonControlsEx(&init_tooltip
);
164 /* Creates tooltip window for icon. */
165 static void create_tooltip(struct icon
*icon
)
169 init_common_controls();
170 icon
->tooltip
= CreateWindowExW(WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
171 WS_POPUP
| TTS_ALWAYSTIP
,
172 CW_USEDEFAULT
, CW_USEDEFAULT
,
173 CW_USEDEFAULT
, CW_USEDEFAULT
,
174 tray_window
, NULL
, NULL
, NULL
);
176 ZeroMemory(&ti
, sizeof(ti
));
177 ti
.cbSize
= sizeof(TTTOOLINFOW
);
178 ti
.hwnd
= tray_window
;
179 ti
.lpszText
= icon
->tiptext
;
180 if (icon
->display
!= -1) ti
.rect
= get_icon_rect( icon
);
181 SendMessageW(icon
->tooltip
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
184 static void set_balloon_position( struct icon
*icon
)
186 RECT rect
= get_icon_rect( icon
);
189 MapWindowPoints( tray_window
, 0, (POINT
*)&rect
, 2 );
190 pos
.x
= (rect
.left
+ rect
.right
) / 2;
191 pos
.y
= (rect
.top
+ rect
.bottom
) / 2;
192 SendMessageW( balloon_window
, TTM_TRACKPOSITION
, 0, MAKELONG( pos
.x
, pos
.y
));
195 static void balloon_create_timer(void)
199 init_common_controls();
200 balloon_window
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
201 WS_POPUP
| TTS_ALWAYSTIP
| TTS_NOPREFIX
| TTS_BALLOON
| TTS_CLOSE
,
202 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
203 tray_window
, NULL
, NULL
, NULL
);
205 memset( &ti
, 0, sizeof(ti
) );
206 ti
.cbSize
= sizeof(TTTOOLINFOW
);
207 ti
.hwnd
= tray_window
;
208 ti
.uFlags
= TTF_TRACK
;
209 ti
.lpszText
= balloon_icon
->info_text
;
210 SendMessageW( balloon_window
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
211 if ((balloon_icon
->info_flags
& NIIF_ICONMASK
) == NIIF_USER
)
212 SendMessageW( balloon_window
, TTM_SETTITLEW
, (WPARAM
)balloon_icon
->info_icon
,
213 (LPARAM
)balloon_icon
->info_title
);
215 SendMessageW( balloon_window
, TTM_SETTITLEW
, balloon_icon
->info_flags
,
216 (LPARAM
)balloon_icon
->info_title
);
217 set_balloon_position( balloon_icon
);
218 SendMessageW( balloon_window
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
219 KillTimer( tray_window
, BALLOON_CREATE_TIMER
);
220 SetTimer( tray_window
, BALLOON_SHOW_TIMER
, balloon_icon
->info_timeout
, NULL
);
223 static BOOL
show_balloon( struct icon
*icon
)
225 if (icon
->display
== -1) return FALSE
; /* not displayed */
226 if (!icon
->info_text
[0]) return FALSE
; /* no balloon */
228 SetTimer( tray_window
, BALLOON_CREATE_TIMER
, BALLOON_CREATE_TIMEOUT
, NULL
);
232 static void hide_balloon(void)
234 if (!balloon_icon
) return;
237 KillTimer( tray_window
, BALLOON_SHOW_TIMER
);
238 DestroyWindow( balloon_window
);
241 else KillTimer( tray_window
, BALLOON_CREATE_TIMER
);
245 static void show_next_balloon(void)
249 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct icon
, entry
)
250 if (show_balloon( icon
)) break;
253 static void update_balloon( struct icon
*icon
)
255 if (balloon_icon
== icon
)
258 show_balloon( icon
);
260 else if (!balloon_icon
)
262 if (!show_balloon( icon
)) return;
264 if (!balloon_icon
) show_next_balloon();
267 static void balloon_timer(void)
269 if (balloon_icon
) balloon_icon
->info_text
[0] = 0; /* clear text now that balloon has been shown */
274 /* Synchronize tooltip text with tooltip window */
275 static void update_tooltip_text(struct icon
*icon
)
279 ZeroMemory(&ti
, sizeof(ti
));
280 ti
.cbSize
= sizeof(TTTOOLINFOW
);
281 ti
.hwnd
= tray_window
;
282 ti
.lpszText
= icon
->tiptext
;
284 SendMessageW(icon
->tooltip
, TTM_UPDATETIPTEXTW
, 0, (LPARAM
)&ti
);
287 /* synchronize tooltip position with tooltip window */
288 static void update_tooltip_position( struct icon
*icon
)
292 ZeroMemory(&ti
, sizeof(ti
));
293 ti
.cbSize
= sizeof(TTTOOLINFOW
);
294 ti
.hwnd
= tray_window
;
295 if (icon
->display
!= -1) ti
.rect
= get_icon_rect( icon
);
296 SendMessageW( icon
->tooltip
, TTM_NEWTOOLRECTW
, 0, (LPARAM
)&ti
);
297 if (balloon_icon
== icon
) set_balloon_position( icon
);
300 /* find the icon located at a certain point in the tray window */
301 static struct icon
*icon_from_point( int x
, int y
)
303 if (y
< 0 || y
>= icon_cy
) return NULL
;
305 if (x
< 0 || x
>= icon_cx
* nb_displayed
) return NULL
;
306 return displayed
[x
/ icon_cx
];
309 /* invalidate the portion of the tray window that contains the specified icons */
310 static void invalidate_icons( unsigned int start
, unsigned int end
)
314 rect
.left
= tray_width
- (end
+ 1) * icon_cx
;
315 rect
.top
= (tray_height
- icon_cy
) / 2;
316 rect
.right
= tray_width
- start
* icon_cx
;
317 rect
.bottom
= rect
.top
+ icon_cy
;
318 InvalidateRect( tray_window
, &rect
, TRUE
);
321 /* make an icon visible */
322 static BOOL
show_icon(struct icon
*icon
)
324 WINE_TRACE("id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
326 if (icon
->display
!= -1) return TRUE
; /* already displayed */
328 if (nb_displayed
>= alloc_displayed
)
330 unsigned int new_count
= max( alloc_displayed
* 2, 32 );
332 if (displayed
) ptr
= HeapReAlloc( GetProcessHeap(), 0, displayed
, new_count
* sizeof(*ptr
) );
333 else ptr
= HeapAlloc( GetProcessHeap(), 0, new_count
* sizeof(*ptr
) );
334 if (!ptr
) return FALSE
;
336 alloc_displayed
= new_count
;
339 icon
->display
= nb_displayed
;
340 displayed
[nb_displayed
++] = icon
;
341 update_tooltip_position( icon
);
342 invalidate_icons( nb_displayed
-1, nb_displayed
-1 );
344 if (nb_displayed
== 1 && !hide_systray
) do_show_systray();
346 create_tooltip(icon
);
347 update_balloon( icon
);
351 /* make an icon invisible */
352 static BOOL
hide_icon(struct icon
*icon
)
356 WINE_TRACE("id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
358 if (icon
->display
== -1) return TRUE
; /* already hidden */
360 assert( nb_displayed
);
361 for (i
= icon
->display
; i
< nb_displayed
- 1; i
++)
363 displayed
[i
] = displayed
[i
+ 1];
364 displayed
[i
]->display
= i
;
365 update_tooltip_position( displayed
[i
] );
368 invalidate_icons( icon
->display
, nb_displayed
);
371 if (!nb_displayed
&& !enable_shell
) do_hide_systray();
373 update_balloon( icon
);
374 update_tooltip_position( icon
);
378 /* Modifies an existing icon record */
379 static BOOL
modify_icon( struct icon
*icon
, NOTIFYICONDATAW
*nid
)
381 WINE_TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
383 /* demarshal the request from the NID */
386 WINE_WARN("Invalid icon ID (0x%x) for HWND %p\n", nid
->uID
, nid
->hWnd
);
390 if (nid
->uFlags
& NIF_STATE
)
392 icon
->state
= (icon
->state
& ~nid
->dwStateMask
) | (nid
->dwState
& nid
->dwStateMask
);
395 if (nid
->uFlags
& NIF_ICON
)
397 if (icon
->image
) DestroyIcon(icon
->image
);
398 icon
->image
= CopyIcon(nid
->hIcon
);
399 if (icon
->display
!= -1) invalidate_icons( icon
->display
, icon
->display
);
402 if (nid
->uFlags
& NIF_MESSAGE
)
404 icon
->callback_message
= nid
->uCallbackMessage
;
406 if (nid
->uFlags
& NIF_TIP
)
408 lstrcpynW(icon
->tiptext
, nid
->szTip
, sizeof(icon
->tiptext
)/sizeof(WCHAR
));
409 if (icon
->display
!= -1) update_tooltip_text(icon
);
411 if (nid
->uFlags
& NIF_INFO
&& nid
->cbSize
>= NOTIFYICONDATAA_V2_SIZE
)
413 lstrcpynW( icon
->info_text
, nid
->szInfo
, sizeof(icon
->info_text
)/sizeof(WCHAR
) );
414 lstrcpynW( icon
->info_title
, nid
->szInfoTitle
, sizeof(icon
->info_title
)/sizeof(WCHAR
) );
415 icon
->info_flags
= nid
->dwInfoFlags
;
416 icon
->info_timeout
= max(min(nid
->u
.uTimeout
, BALLOON_SHOW_MAX_TIMEOUT
), BALLOON_SHOW_MIN_TIMEOUT
);
417 icon
->info_icon
= nid
->hBalloonIcon
;
418 update_balloon( icon
);
420 if (icon
->state
& NIS_HIDDEN
) hide_icon( icon
);
421 else show_icon( icon
);
425 /* Adds a new icon record to the list */
426 static BOOL
add_icon(NOTIFYICONDATAW
*nid
)
430 WINE_TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
432 if ((icon
= get_icon(nid
->hWnd
, nid
->uID
)))
434 WINE_WARN("duplicate tray icon add, buggy app?\n");
438 if (!(icon
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*icon
))))
440 WINE_ERR("out of memory\n");
444 ZeroMemory(icon
, sizeof(struct icon
));
446 icon
->owner
= nid
->hWnd
;
449 list_add_tail(&icon_list
, &icon
->entry
);
451 return modify_icon( icon
, nid
);
454 /* Deletes tray icon window and icon record */
455 static BOOL
delete_icon(struct icon
*icon
)
458 list_remove(&icon
->entry
);
459 DestroyIcon(icon
->image
);
460 HeapFree(GetProcessHeap(), 0, icon
);
464 /* cleanup icons belonging to a window that has been destroyed */
465 static void cleanup_systray_window( HWND hwnd
)
467 struct icon
*icon
, *next
;
469 LIST_FOR_EACH_ENTRY_SAFE( icon
, next
, &icon_list
, struct icon
, entry
)
470 if (icon
->owner
== hwnd
) delete_icon( icon
);
472 if (wine_notify_icon
)
474 NOTIFYICONDATAW nid
= { sizeof(nid
), hwnd
};
475 wine_notify_icon( 0xdead, &nid
);
479 /* update the taskbar buttons when something changed */
480 static void sync_taskbar_buttons(void)
482 struct taskbar_button
*win
;
483 int pos
= 0, count
= 0;
484 int width
= taskbar_button_width
;
485 int right
= tray_width
- nb_displayed
* icon_cx
;
486 HWND foreground
= GetAncestor( GetForegroundWindow(), GA_ROOTOWNER
);
488 if (!IsWindowVisible( tray_window
)) return;
490 LIST_FOR_EACH_ENTRY( win
, &taskbar_buttons
, struct taskbar_button
, entry
)
492 if (!win
->hwnd
) /* start button */
494 SetWindowPos( win
->button
, 0, pos
, 0, start_button_width
, tray_height
,
495 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
496 pos
+= start_button_width
;
499 win
->active
= (win
->hwnd
== foreground
);
500 win
->visible
= IsWindowVisible( win
->hwnd
) && !GetWindow( win
->hwnd
, GW_OWNER
);
501 if (win
->visible
) count
++;
504 /* shrink buttons if space is tight */
505 if (count
&& (count
* width
> right
- pos
))
506 width
= max( taskbar_button_width
/ 4, (right
- pos
) / count
);
508 LIST_FOR_EACH_ENTRY( win
, &taskbar_buttons
, struct taskbar_button
, entry
)
510 if (!win
->hwnd
) continue; /* start button */
511 if (win
->visible
&& right
- pos
>= width
)
513 SetWindowPos( win
->button
, 0, pos
, 0, width
, tray_height
,
514 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
515 InvalidateRect( win
->button
, NULL
, TRUE
);
518 else SetWindowPos( win
->button
, 0, 0, 0, 0, 0, SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_HIDEWINDOW
);
522 static BOOL
handle_incoming(HWND hwndSource
, COPYDATASTRUCT
*cds
)
524 struct icon
*icon
= NULL
;
525 const struct notify_data
*data
;
529 if (cds
->cbData
< sizeof(*data
)) return FALSE
;
532 nid
.cbSize
= sizeof(nid
);
533 nid
.hWnd
= LongToHandle( data
->hWnd
);
535 nid
.uFlags
= data
->uFlags
;
536 nid
.uCallbackMessage
= data
->uCallbackMessage
;
538 nid
.dwState
= data
->dwState
;
539 nid
.dwStateMask
= data
->dwStateMask
;
540 nid
.u
.uTimeout
= data
->u
.uTimeout
;
541 nid
.dwInfoFlags
= data
->dwInfoFlags
;
542 nid
.guidItem
= data
->guidItem
;
543 lstrcpyW( nid
.szTip
, data
->szTip
);
544 lstrcpyW( nid
.szInfo
, data
->szInfo
);
545 lstrcpyW( nid
.szInfoTitle
, data
->szInfoTitle
);
546 nid
.hBalloonIcon
= 0;
548 /* FIXME: if statement only needed because we don't support interprocess
550 if ((nid
.uFlags
& NIF_ICON
) && cds
->cbData
> sizeof(*data
))
554 const char *buffer
= (const char *)(data
+ 1);
556 cbMaskBits
= (data
->width
* data
->height
+ 15) / 16 * 2;
557 cbColourBits
= (data
->planes
* data
->width
* data
->height
* data
->bpp
+ 15) / 16 * 2;
559 if (cds
->cbData
< sizeof(*data
) + cbMaskBits
+ cbColourBits
)
561 WINE_ERR("buffer underflow\n");
564 nid
.hIcon
= CreateIcon(NULL
, data
->width
, data
->height
, data
->planes
, data
->bpp
,
565 buffer
, buffer
+ cbMaskBits
);
568 /* try forwarding to the display driver first */
569 if (cds
->dwData
== NIM_ADD
|| !(icon
= get_icon( nid
.hWnd
, nid
.uID
)))
571 if (wine_notify_icon
&& ((ret
= wine_notify_icon( cds
->dwData
, &nid
)) != -1))
573 if (nid
.hIcon
) DestroyIcon( nid
.hIcon
);
582 ret
= add_icon(&nid
);
585 if (icon
) ret
= delete_icon( icon
);
588 if (icon
) ret
= modify_icon( icon
, &nid
);
593 icon
->version
= nid
.u
.uVersion
;
598 WINE_FIXME("unhandled tray message: %ld\n", cds
->dwData
);
602 if (nid
.hIcon
) DestroyIcon( nid
.hIcon
);
603 sync_taskbar_buttons();
607 static void add_taskbar_button( HWND hwnd
)
609 struct taskbar_button
*win
;
611 if (hide_systray
) return;
613 /* ignore our own windows */
617 if (!GetWindowThreadProcessId( hwnd
, &process
) || process
== GetCurrentProcessId()) return;
620 if (!(win
= HeapAlloc( GetProcessHeap(), 0, sizeof(*win
) ))) return;
622 win
->button
= CreateWindowW( WC_BUTTONW
, NULL
, WS_CHILD
| BS_OWNERDRAW
,
623 0, 0, 0, 0, tray_window
, (HMENU
)hwnd
, 0, 0 );
624 list_add_tail( &taskbar_buttons
, &win
->entry
);
627 static struct taskbar_button
*find_taskbar_button( HWND hwnd
)
629 struct taskbar_button
*win
;
631 LIST_FOR_EACH_ENTRY( win
, &taskbar_buttons
, struct taskbar_button
, entry
)
632 if (win
->hwnd
== hwnd
) return win
;
637 static void remove_taskbar_button( HWND hwnd
)
639 struct taskbar_button
*win
= find_taskbar_button( hwnd
);
642 list_remove( &win
->entry
);
643 DestroyWindow( win
->button
);
644 HeapFree( GetProcessHeap(), 0, win
);
647 static void paint_taskbar_button( const DRAWITEMSTRUCT
*dis
)
650 UINT flags
= DC_TEXT
;
651 struct taskbar_button
*win
= find_taskbar_button( LongToHandle( dis
->CtlID
));
654 GetClientRect( dis
->hwndItem
, &rect
);
655 DrawFrameControl( dis
->hDC
, &rect
, DFC_BUTTON
, DFCS_BUTTONPUSH
| DFCS_ADJUSTRECT
|
656 ((dis
->itemState
& ODS_SELECTED
) ? DFCS_PUSHED
: 0 ));
659 flags
|= win
->active
? DC_ACTIVE
: DC_INBUTTON
;
660 DrawCaptionTempW( win
->hwnd
, dis
->hDC
, &rect
, 0, 0, NULL
, flags
);
662 else /* start button */
663 DrawCaptionTempW( 0, dis
->hDC
, &rect
, 0, 0, start_label
, flags
| DC_INBUTTON
| DC_ICON
);
666 static void click_taskbar_button( HWND button
)
668 LONG_PTR id
= GetWindowLongPtrW( button
, GWLP_ID
);
669 HWND hwnd
= (HWND
)id
;
671 if (!hwnd
) /* start button */
673 do_startmenu( tray_window
);
677 if (IsIconic( hwnd
))
679 SendMessageW( hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0 );
683 if (IsWindowEnabled( hwnd
))
685 if (hwnd
== GetForegroundWindow())
687 SendMessageW( hwnd
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0 );
691 else /* look for an enabled window owned by this one */
693 HWND owned
= GetWindow( GetDesktopWindow(), GW_CHILD
);
694 while (owned
&& owned
!= hwnd
)
696 if (IsWindowVisible( owned
) &&
697 IsWindowEnabled( owned
) &&
698 (GetWindow( owned
, GW_OWNER
) == hwnd
))
700 owned
= GetWindow( owned
, GW_HWNDNEXT
);
704 SetForegroundWindow( hwnd
);
707 static void show_taskbar_contextmenu( HWND button
, LPARAM lparam
)
709 ULONG_PTR id
= GetWindowLongPtrW( button
, GWLP_ID
);
711 if (id
) SendNotifyMessageW( (HWND
)id
, WM_POPUPSYSTEMMENU
, 0, lparam
);
714 static void do_hide_systray(void)
716 SetWindowPos( tray_window
, 0,
717 GetSystemMetrics(SM_XVIRTUALSCREEN
) + GetSystemMetrics(SM_CXVIRTUALSCREEN
),
718 GetSystemMetrics(SM_YVIRTUALSCREEN
) + GetSystemMetrics(SM_CYVIRTUALSCREEN
),
719 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
722 static BOOL
notify_owner( struct icon
*icon
, UINT msg
, POINT pt
)
724 WPARAM wp
= icon
->id
;
727 if (icon
->version
>= NOTIFY_VERSION_4
)
729 ClientToScreen( tray_window
, &pt
);
730 wp
= MAKEWPARAM( pt
.x
, pt
.y
);
731 lp
= MAKELPARAM( msg
, icon
->id
);
734 TRACE( "relaying 0x%x\n", msg
);
735 if (!PostMessageW( icon
->owner
, icon
->callback_message
, wp
, lp
) &&
736 (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
))
738 WARN( "application window was destroyed, removing icon %u\n", icon
->id
);
745 static void do_show_systray(void)
748 NONCLIENTMETRICSW ncm
;
750 HDC hdc
= GetDC( 0 );
752 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
753 SystemParametersInfoW( SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0 );
754 font
= CreateFontIndirectW( &ncm
.lfCaptionFont
);
755 /* FIXME: Implement BCM_GETIDEALSIZE and use that instead. */
756 SelectObject( hdc
, font
);
757 GetTextExtentPointA( hdc
, "abcdefghijklmnopqrstuvwxyz", 26, &size
);
758 taskbar_button_width
= size
.cx
;
759 GetTextExtentPointW( hdc
, start_label
, lstrlenW(start_label
), &size
);
760 /* add some margins (FIXME) */
761 size
.cx
+= 12 + GetSystemMetrics( SM_CXSMICON
);
764 DeleteObject( font
);
766 tray_width
= GetSystemMetrics( SM_CXSCREEN
);
767 tray_height
= max( icon_cy
, size
.cy
);
768 start_button_width
= size
.cx
;
769 SetWindowPos( tray_window
, HWND_TOPMOST
, 0, GetSystemMetrics( SM_CYSCREEN
) - tray_height
,
770 tray_width
, tray_height
, SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
771 sync_taskbar_buttons();
774 static LRESULT WINAPI
tray_wndproc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
779 return handle_incoming((HWND
)wparam
, (COPYDATASTRUCT
*)lparam
);
781 case WM_DISPLAYCHANGE
:
782 if (hide_systray
|| (!nb_displayed
&& !enable_shell
)) do_hide_systray();
783 else do_show_systray();
789 case BALLOON_CREATE_TIMER
: balloon_create_timer(); break;
790 case BALLOON_SHOW_TIMER
: balloon_timer(); break;
800 hdc
= BeginPaint( hwnd
, &ps
);
801 for (i
= 0; i
< nb_displayed
; i
++)
803 RECT dummy
, rect
= get_icon_rect( displayed
[i
] );
804 if (IntersectRect( &dummy
, &rect
, &ps
.rcPaint
))
805 DrawIconEx( hdc
, rect
.left
+ ICON_BORDER
, rect
.top
+ ICON_BORDER
, displayed
[i
]->image
,
806 icon_cx
- 2*ICON_BORDER
, icon_cy
- 2*ICON_BORDER
,
807 0, 0, DI_DEFAULTSIZE
|DI_NORMAL
);
809 EndPaint( hwnd
, &ps
);
820 case WM_LBUTTONDBLCLK
:
821 case WM_RBUTTONDBLCLK
:
822 case WM_MBUTTONDBLCLK
:
825 POINT pt
= { (short)LOWORD(lparam
), (short)HIWORD(lparam
) };
826 struct icon
*icon
= icon_from_point( pt
.x
, pt
.y
);
830 message
.message
= msg
;
831 message
.wParam
= wparam
;
832 message
.lParam
= lparam
;
833 SendMessageW( icon
->tooltip
, TTM_RELAYEVENT
, 0, (LPARAM
)&message
);
835 if (!notify_owner( icon
, msg
, pt
)) break;
837 if (icon
->version
> 0)
842 notify_owner( icon
, WM_CONTEXTMENU
, pt
);
845 notify_owner( icon
, NIN_SELECT
, pt
);
853 /* don't destroy the tray window, just hide it */
854 ShowWindow( hwnd
, SW_HIDE
);
858 paint_taskbar_button( (const DRAWITEMSTRUCT
*)lparam
);
862 if (HIWORD(wparam
) == BN_CLICKED
) click_taskbar_button( (HWND
)lparam
);
866 show_taskbar_contextmenu( (HWND
)wparam
, lparam
);
869 case WM_MOUSEACTIVATE
:
870 return MA_NOACTIVATE
;
872 case WM_INITMENUPOPUP
:
874 return menu_wndproc(hwnd
, msg
, wparam
, lparam
);
877 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
882 /* notification posted to the desktop window */
883 void handle_parent_notify( HWND hwnd
, WPARAM wp
)
888 add_taskbar_button( hwnd
);
891 remove_taskbar_button( hwnd
);
892 cleanup_systray_window( hwnd
);
895 sync_taskbar_buttons();
898 /* this function creates the listener window */
899 void initialize_systray( HMODULE graphics_driver
, BOOL using_root
, BOOL arg_enable_shell
)
902 static const WCHAR classname
[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0};
904 if (using_root
) wine_notify_icon
= (void *)GetProcAddress( graphics_driver
, "wine_notify_icon" );
906 icon_cx
= GetSystemMetrics( SM_CXSMICON
) + 2*ICON_BORDER
;
907 icon_cy
= GetSystemMetrics( SM_CYSMICON
) + 2*ICON_BORDER
;
908 hide_systray
= using_root
;
909 enable_shell
= arg_enable_shell
;
911 /* register the systray listener window class */
912 ZeroMemory(&class, sizeof(class));
913 class.cbSize
= sizeof(class);
914 class.style
= CS_DBLCLKS
| CS_HREDRAW
;
915 class.lpfnWndProc
= tray_wndproc
;
916 class.hInstance
= NULL
;
917 class.hIcon
= LoadIconW(0, (LPCWSTR
)IDI_WINLOGO
);
918 class.hCursor
= LoadCursorW(0, (LPCWSTR
)IDC_ARROW
);
919 class.hbrBackground
= (HBRUSH
) COLOR_WINDOW
;
920 class.lpszClassName
= classname
;
922 if (!RegisterClassExW(&class))
924 WINE_ERR("Could not register SysTray window class\n");
928 tray_window
= CreateWindowExW( WS_EX_NOACTIVATE
, classname
, NULL
, WS_POPUP
,
929 0, GetSystemMetrics( SM_CYSCREEN
), 0, 0, 0, 0, 0, 0 );
932 WINE_ERR("Could not create tray window\n");
936 LoadStringW( NULL
, IDS_START_LABEL
, start_label
, sizeof(start_label
)/sizeof(WCHAR
) );
938 add_taskbar_button( 0 );
940 if (hide_systray
) do_hide_systray();
941 else if (enable_shell
) do_show_systray();