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
28 #include <wine/debug.h>
29 #include <wine/list.h>
31 #include "explorer_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(systray
);
36 struct notify_data
/* platform-independent format for NOTIFYICONDATA */
41 UINT uCallbackMessage
;
50 WCHAR szInfoTitle
[64];
53 /* data for the icon bitmap */
60 static int (CDECL
*wine_notify_icon
)(DWORD
,NOTIFYICONDATAW
*);
62 /* an individual systray icon, unpacked from the NOTIFYICONDATA and always in unicode */
66 HICON image
; /* the image to render */
67 HWND owner
; /* the HWND passed in to the Shell_NotifyIcon call */
68 HWND tooltip
; /* Icon tooltip */
69 UINT state
; /* state flags */
70 UINT id
; /* the unique id given by the app */
71 UINT callback_message
;
72 int display
; /* index in display list, or -1 if hidden */
73 WCHAR tiptext
[128]; /* Tooltip text. If empty => tooltip disabled */
74 WCHAR info_text
[256]; /* info balloon text */
75 WCHAR info_title
[64]; /* info balloon title */
76 UINT info_flags
; /* flags for info balloon */
77 UINT info_timeout
; /* timeout for info balloon */
78 HICON info_icon
; /* info balloon icon */
79 UINT version
; /* notify icon api version */
82 static struct list icon_list
= LIST_INIT( icon_list
);
93 static struct list taskbar_buttons
= LIST_INIT( taskbar_buttons
);
95 static HWND tray_window
;
97 static unsigned int alloc_displayed
;
98 static unsigned int nb_displayed
;
99 static struct icon
**displayed
; /* array of currently displayed icons */
101 static BOOL hide_systray
, enable_shell
;
102 static int icon_cx
, icon_cy
, tray_width
, tray_height
;
103 static int start_button_width
, taskbar_button_width
;
104 static WCHAR start_label
[50];
106 static struct icon
*balloon_icon
;
107 static HWND balloon_window
;
109 #define MIN_DISPLAYED 8
110 #define ICON_BORDER 2
112 #define BALLOON_CREATE_TIMER 1
113 #define BALLOON_SHOW_TIMER 2
115 #define BALLOON_CREATE_TIMEOUT 2000
116 #define BALLOON_SHOW_MIN_TIMEOUT 10000
117 #define BALLOON_SHOW_MAX_TIMEOUT 30000
119 #define WM_POPUPSYSTEMMENU 0x0313
121 static void do_hide_systray(void);
122 static void do_show_systray(void);
124 /* Retrieves icon record by owner window and ID */
125 static struct icon
*get_icon(HWND owner
, UINT id
)
129 /* search for the icon */
130 LIST_FOR_EACH_ENTRY( this, &icon_list
, struct icon
, entry
)
131 if ((this->id
== id
) && (this->owner
== owner
)) return this;
136 static RECT
get_icon_rect( struct icon
*icon
)
140 rect
.right
= tray_width
- icon_cx
* icon
->display
;
141 rect
.left
= rect
.right
- icon_cx
;
142 rect
.top
= (tray_height
- icon_cy
) / 2;
143 rect
.bottom
= rect
.top
+ icon_cy
;
147 static void init_common_controls(void)
149 static BOOL initialized
= FALSE
;
153 INITCOMMONCONTROLSEX init_tooltip
;
155 init_tooltip
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
156 init_tooltip
.dwICC
= ICC_TAB_CLASSES
|ICC_STANDARD_CLASSES
;
158 InitCommonControlsEx(&init_tooltip
);
163 /* Creates tooltip window for icon. */
164 static void create_tooltip(struct icon
*icon
)
168 init_common_controls();
169 icon
->tooltip
= CreateWindowExW(WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
170 WS_POPUP
| TTS_ALWAYSTIP
,
171 CW_USEDEFAULT
, CW_USEDEFAULT
,
172 CW_USEDEFAULT
, CW_USEDEFAULT
,
173 tray_window
, NULL
, NULL
, NULL
);
175 ZeroMemory(&ti
, sizeof(ti
));
176 ti
.cbSize
= sizeof(TTTOOLINFOW
);
177 ti
.hwnd
= tray_window
;
178 ti
.lpszText
= icon
->tiptext
;
179 if (icon
->display
!= -1) ti
.rect
= get_icon_rect( icon
);
180 SendMessageW(icon
->tooltip
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
183 static void set_balloon_position( struct icon
*icon
)
185 RECT rect
= get_icon_rect( icon
);
188 MapWindowPoints( tray_window
, 0, (POINT
*)&rect
, 2 );
189 pos
.x
= (rect
.left
+ rect
.right
) / 2;
190 pos
.y
= (rect
.top
+ rect
.bottom
) / 2;
191 SendMessageW( balloon_window
, TTM_TRACKPOSITION
, 0, MAKELONG( pos
.x
, pos
.y
));
194 static void balloon_create_timer(void)
198 init_common_controls();
199 balloon_window
= CreateWindowExW( WS_EX_TOPMOST
, TOOLTIPS_CLASSW
, NULL
,
200 WS_POPUP
| TTS_ALWAYSTIP
| TTS_NOPREFIX
| TTS_BALLOON
| TTS_CLOSE
,
201 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
202 tray_window
, NULL
, NULL
, NULL
);
204 memset( &ti
, 0, sizeof(ti
) );
205 ti
.cbSize
= sizeof(TTTOOLINFOW
);
206 ti
.hwnd
= tray_window
;
207 ti
.uFlags
= TTF_TRACK
;
208 ti
.lpszText
= balloon_icon
->info_text
;
209 SendMessageW( balloon_window
, TTM_ADDTOOLW
, 0, (LPARAM
)&ti
);
210 if ((balloon_icon
->info_flags
& NIIF_ICONMASK
) == NIIF_USER
)
211 SendMessageW( balloon_window
, TTM_SETTITLEW
, (WPARAM
)balloon_icon
->info_icon
,
212 (LPARAM
)balloon_icon
->info_title
);
214 SendMessageW( balloon_window
, TTM_SETTITLEW
, balloon_icon
->info_flags
,
215 (LPARAM
)balloon_icon
->info_title
);
216 set_balloon_position( balloon_icon
);
217 SendMessageW( balloon_window
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
218 KillTimer( tray_window
, BALLOON_CREATE_TIMER
);
219 SetTimer( tray_window
, BALLOON_SHOW_TIMER
, balloon_icon
->info_timeout
, NULL
);
222 static BOOL
show_balloon( struct icon
*icon
)
224 if (icon
->display
== -1) return FALSE
; /* not displayed */
225 if (!icon
->info_text
[0]) return FALSE
; /* no balloon */
227 SetTimer( tray_window
, BALLOON_CREATE_TIMER
, BALLOON_CREATE_TIMEOUT
, NULL
);
231 static void hide_balloon(void)
233 if (!balloon_icon
) return;
236 KillTimer( tray_window
, BALLOON_SHOW_TIMER
);
237 DestroyWindow( balloon_window
);
240 else KillTimer( tray_window
, BALLOON_CREATE_TIMER
);
244 static void show_next_balloon(void)
248 LIST_FOR_EACH_ENTRY( icon
, &icon_list
, struct icon
, entry
)
249 if (show_balloon( icon
)) break;
252 static void update_balloon( struct icon
*icon
)
254 if (balloon_icon
== icon
)
257 show_balloon( icon
);
259 else if (!balloon_icon
)
261 if (!show_balloon( icon
)) return;
263 if (!balloon_icon
) show_next_balloon();
266 static void balloon_timer(void)
268 if (balloon_icon
) balloon_icon
->info_text
[0] = 0; /* clear text now that balloon has been shown */
273 /* Synchronize tooltip text with tooltip window */
274 static void update_tooltip_text(struct icon
*icon
)
278 ZeroMemory(&ti
, sizeof(ti
));
279 ti
.cbSize
= sizeof(TTTOOLINFOW
);
280 ti
.hwnd
= tray_window
;
281 ti
.lpszText
= icon
->tiptext
;
283 SendMessageW(icon
->tooltip
, TTM_UPDATETIPTEXTW
, 0, (LPARAM
)&ti
);
286 /* synchronize tooltip position with tooltip window */
287 static void update_tooltip_position( struct icon
*icon
)
291 ZeroMemory(&ti
, sizeof(ti
));
292 ti
.cbSize
= sizeof(TTTOOLINFOW
);
293 ti
.hwnd
= tray_window
;
294 if (icon
->display
!= -1) ti
.rect
= get_icon_rect( icon
);
295 SendMessageW( icon
->tooltip
, TTM_NEWTOOLRECTW
, 0, (LPARAM
)&ti
);
296 if (balloon_icon
== icon
) set_balloon_position( icon
);
299 /* find the icon located at a certain point in the tray window */
300 static struct icon
*icon_from_point( int x
, int y
)
302 if (y
< 0 || y
>= icon_cy
) return NULL
;
304 if (x
< 0 || x
>= icon_cx
* nb_displayed
) return NULL
;
305 return displayed
[x
/ icon_cx
];
308 /* invalidate the portion of the tray window that contains the specified icons */
309 static void invalidate_icons( unsigned int start
, unsigned int end
)
313 rect
.left
= tray_width
- (end
+ 1) * icon_cx
;
314 rect
.top
= (tray_height
- icon_cy
) / 2;
315 rect
.right
= tray_width
- start
* icon_cx
;
316 rect
.bottom
= rect
.top
+ icon_cy
;
317 InvalidateRect( tray_window
, &rect
, TRUE
);
320 /* make an icon visible */
321 static BOOL
show_icon(struct icon
*icon
)
323 WINE_TRACE("id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
325 if (icon
->display
!= -1) return TRUE
; /* already displayed */
327 if (nb_displayed
>= alloc_displayed
)
329 unsigned int new_count
= max( alloc_displayed
* 2, 32 );
331 if (displayed
) ptr
= HeapReAlloc( GetProcessHeap(), 0, displayed
, new_count
* sizeof(*ptr
) );
332 else ptr
= HeapAlloc( GetProcessHeap(), 0, new_count
* sizeof(*ptr
) );
333 if (!ptr
) return FALSE
;
335 alloc_displayed
= new_count
;
338 icon
->display
= nb_displayed
;
339 displayed
[nb_displayed
++] = icon
;
340 update_tooltip_position( icon
);
341 invalidate_icons( nb_displayed
-1, nb_displayed
-1 );
343 if (nb_displayed
== 1 && !hide_systray
) do_show_systray();
345 create_tooltip(icon
);
346 update_balloon( icon
);
350 /* make an icon invisible */
351 static BOOL
hide_icon(struct icon
*icon
)
355 WINE_TRACE("id=0x%x, hwnd=%p\n", icon
->id
, icon
->owner
);
357 if (icon
->display
== -1) return TRUE
; /* already hidden */
359 assert( nb_displayed
);
360 for (i
= icon
->display
; i
< nb_displayed
- 1; i
++)
362 displayed
[i
] = displayed
[i
+ 1];
363 displayed
[i
]->display
= i
;
364 update_tooltip_position( displayed
[i
] );
367 invalidate_icons( icon
->display
, nb_displayed
);
370 if (!nb_displayed
&& !enable_shell
) do_hide_systray();
372 update_balloon( icon
);
373 update_tooltip_position( icon
);
377 /* Modifies an existing icon record */
378 static BOOL
modify_icon( struct icon
*icon
, NOTIFYICONDATAW
*nid
)
380 WINE_TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
382 /* demarshal the request from the NID */
385 WINE_WARN("Invalid icon ID (0x%x) for HWND %p\n", nid
->uID
, nid
->hWnd
);
389 if (nid
->uFlags
& NIF_STATE
)
391 icon
->state
= (icon
->state
& ~nid
->dwStateMask
) | (nid
->dwState
& nid
->dwStateMask
);
394 if (nid
->uFlags
& NIF_ICON
)
396 if (icon
->image
) DestroyIcon(icon
->image
);
397 icon
->image
= CopyIcon(nid
->hIcon
);
398 if (icon
->display
!= -1) invalidate_icons( icon
->display
, icon
->display
);
401 if (nid
->uFlags
& NIF_MESSAGE
)
403 icon
->callback_message
= nid
->uCallbackMessage
;
405 if (nid
->uFlags
& NIF_TIP
)
407 lstrcpynW( icon
->tiptext
, nid
->szTip
, ARRAY_SIZE( icon
->tiptext
));
408 if (icon
->display
!= -1) update_tooltip_text(icon
);
410 if (nid
->uFlags
& NIF_INFO
&& nid
->cbSize
>= NOTIFYICONDATAA_V2_SIZE
)
412 lstrcpynW( icon
->info_text
, nid
->szInfo
, ARRAY_SIZE( icon
->info_text
));
413 lstrcpynW( icon
->info_title
, nid
->szInfoTitle
, ARRAY_SIZE( icon
->info_title
));
414 icon
->info_flags
= nid
->dwInfoFlags
;
415 icon
->info_timeout
= max(min(nid
->u
.uTimeout
, BALLOON_SHOW_MAX_TIMEOUT
), BALLOON_SHOW_MIN_TIMEOUT
);
416 icon
->info_icon
= nid
->hBalloonIcon
;
417 update_balloon( icon
);
419 if (icon
->state
& NIS_HIDDEN
) hide_icon( icon
);
420 else show_icon( icon
);
424 /* Adds a new icon record to the list */
425 static BOOL
add_icon(NOTIFYICONDATAW
*nid
)
429 WINE_TRACE("id=0x%x, hwnd=%p\n", nid
->uID
, nid
->hWnd
);
431 if ((icon
= get_icon(nid
->hWnd
, nid
->uID
)))
433 WINE_WARN("duplicate tray icon add, buggy app?\n");
437 if (!(icon
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*icon
))))
439 WINE_ERR("out of memory\n");
443 ZeroMemory(icon
, sizeof(struct icon
));
445 icon
->owner
= nid
->hWnd
;
448 list_add_tail(&icon_list
, &icon
->entry
);
450 return modify_icon( icon
, nid
);
453 /* Deletes tray icon window and icon record */
454 static BOOL
delete_icon(struct icon
*icon
)
457 list_remove(&icon
->entry
);
458 DestroyIcon(icon
->image
);
459 HeapFree(GetProcessHeap(), 0, icon
);
463 /* cleanup icons belonging to a window that has been destroyed */
464 static void cleanup_systray_window( HWND hwnd
)
466 struct icon
*icon
, *next
;
468 LIST_FOR_EACH_ENTRY_SAFE( icon
, next
, &icon_list
, struct icon
, entry
)
469 if (icon
->owner
== hwnd
) delete_icon( icon
);
471 if (wine_notify_icon
)
473 NOTIFYICONDATAW nid
= { sizeof(nid
), hwnd
};
474 wine_notify_icon( 0xdead, &nid
);
478 /* update the taskbar buttons when something changed */
479 static void sync_taskbar_buttons(void)
481 struct taskbar_button
*win
;
482 int pos
= 0, count
= 0;
483 int width
= taskbar_button_width
;
484 int right
= tray_width
- nb_displayed
* icon_cx
;
485 HWND foreground
= GetAncestor( GetForegroundWindow(), GA_ROOTOWNER
);
487 if (!IsWindowVisible( tray_window
)) return;
489 LIST_FOR_EACH_ENTRY( win
, &taskbar_buttons
, struct taskbar_button
, entry
)
491 if (!win
->hwnd
) /* start button */
493 SetWindowPos( win
->button
, 0, pos
, 0, start_button_width
, tray_height
,
494 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
495 pos
+= start_button_width
;
498 win
->active
= (win
->hwnd
== foreground
);
499 win
->visible
= IsWindowVisible( win
->hwnd
) && !GetWindow( win
->hwnd
, GW_OWNER
);
500 if (win
->visible
) count
++;
503 /* shrink buttons if space is tight */
504 if (count
&& (count
* width
> right
- pos
))
505 width
= max( taskbar_button_width
/ 4, (right
- pos
) / count
);
507 LIST_FOR_EACH_ENTRY( win
, &taskbar_buttons
, struct taskbar_button
, entry
)
509 if (!win
->hwnd
) continue; /* start button */
510 if (win
->visible
&& right
- pos
>= width
)
512 SetWindowPos( win
->button
, 0, pos
, 0, width
, tray_height
,
513 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
514 InvalidateRect( win
->button
, NULL
, TRUE
);
517 else SetWindowPos( win
->button
, 0, 0, 0, 0, 0, SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_HIDEWINDOW
);
521 static BOOL
handle_incoming(HWND hwndSource
, COPYDATASTRUCT
*cds
)
523 struct icon
*icon
= NULL
;
524 const struct notify_data
*data
;
528 if (cds
->cbData
< sizeof(*data
)) return FALSE
;
531 nid
.cbSize
= sizeof(nid
);
532 nid
.hWnd
= LongToHandle( data
->hWnd
);
534 nid
.uFlags
= data
->uFlags
;
535 nid
.uCallbackMessage
= data
->uCallbackMessage
;
537 nid
.dwState
= data
->dwState
;
538 nid
.dwStateMask
= data
->dwStateMask
;
539 nid
.u
.uTimeout
= data
->u
.uTimeout
;
540 nid
.dwInfoFlags
= data
->dwInfoFlags
;
541 nid
.guidItem
= data
->guidItem
;
542 lstrcpyW( nid
.szTip
, data
->szTip
);
543 lstrcpyW( nid
.szInfo
, data
->szInfo
);
544 lstrcpyW( nid
.szInfoTitle
, data
->szInfoTitle
);
545 nid
.hBalloonIcon
= 0;
547 /* FIXME: if statement only needed because we don't support interprocess
549 if ((nid
.uFlags
& NIF_ICON
) && cds
->cbData
> sizeof(*data
))
553 const char *buffer
= (const char *)(data
+ 1);
555 cbMaskBits
= (data
->width
* data
->height
+ 15) / 16 * 2;
556 cbColourBits
= (data
->planes
* data
->width
* data
->height
* data
->bpp
+ 15) / 16 * 2;
558 if (cds
->cbData
< sizeof(*data
) + cbMaskBits
+ cbColourBits
)
560 WINE_ERR("buffer underflow\n");
563 nid
.hIcon
= CreateIcon(NULL
, data
->width
, data
->height
, data
->planes
, data
->bpp
,
564 buffer
, buffer
+ cbMaskBits
);
567 /* try forwarding to the display driver first */
568 if (cds
->dwData
== NIM_ADD
|| !(icon
= get_icon( nid
.hWnd
, nid
.uID
)))
570 if (wine_notify_icon
&& ((ret
= wine_notify_icon( cds
->dwData
, &nid
)) != -1))
572 if (nid
.hIcon
) DestroyIcon( nid
.hIcon
);
581 ret
= add_icon(&nid
);
584 if (icon
) ret
= delete_icon( icon
);
587 if (icon
) ret
= modify_icon( icon
, &nid
);
592 icon
->version
= nid
.u
.uVersion
;
597 WINE_FIXME("unhandled tray message: %ld\n", cds
->dwData
);
601 if (nid
.hIcon
) DestroyIcon( nid
.hIcon
);
602 sync_taskbar_buttons();
606 static void add_taskbar_button( HWND hwnd
)
608 struct taskbar_button
*win
;
610 if (hide_systray
) return;
612 /* ignore our own windows */
616 if (!GetWindowThreadProcessId( hwnd
, &process
) || process
== GetCurrentProcessId()) return;
619 if (!(win
= HeapAlloc( GetProcessHeap(), 0, sizeof(*win
) ))) return;
621 win
->button
= CreateWindowW( WC_BUTTONW
, NULL
, WS_CHILD
| BS_OWNERDRAW
,
622 0, 0, 0, 0, tray_window
, (HMENU
)hwnd
, 0, 0 );
623 list_add_tail( &taskbar_buttons
, &win
->entry
);
626 static struct taskbar_button
*find_taskbar_button( HWND hwnd
)
628 struct taskbar_button
*win
;
630 LIST_FOR_EACH_ENTRY( win
, &taskbar_buttons
, struct taskbar_button
, entry
)
631 if (win
->hwnd
== hwnd
) return win
;
636 static void remove_taskbar_button( HWND hwnd
)
638 struct taskbar_button
*win
= find_taskbar_button( hwnd
);
641 list_remove( &win
->entry
);
642 DestroyWindow( win
->button
);
643 HeapFree( GetProcessHeap(), 0, win
);
646 static void paint_taskbar_button( const DRAWITEMSTRUCT
*dis
)
649 UINT flags
= DC_TEXT
;
650 struct taskbar_button
*win
= find_taskbar_button( LongToHandle( dis
->CtlID
));
653 GetClientRect( dis
->hwndItem
, &rect
);
654 DrawFrameControl( dis
->hDC
, &rect
, DFC_BUTTON
, DFCS_BUTTONPUSH
| DFCS_ADJUSTRECT
|
655 ((dis
->itemState
& ODS_SELECTED
) ? DFCS_PUSHED
: 0 ));
658 flags
|= win
->active
? DC_ACTIVE
: DC_INBUTTON
;
659 DrawCaptionTempW( win
->hwnd
, dis
->hDC
, &rect
, 0, 0, NULL
, flags
);
661 else /* start button */
662 DrawCaptionTempW( 0, dis
->hDC
, &rect
, 0, 0, start_label
, flags
| DC_INBUTTON
| DC_ICON
);
665 static void click_taskbar_button( HWND button
)
667 LONG_PTR id
= GetWindowLongPtrW( button
, GWLP_ID
);
668 HWND hwnd
= (HWND
)id
;
670 if (!hwnd
) /* start button */
672 do_startmenu( tray_window
);
676 if (IsIconic( hwnd
))
678 SendMessageW( hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0 );
682 if (IsWindowEnabled( hwnd
))
684 if (hwnd
== GetForegroundWindow())
686 SendMessageW( hwnd
, WM_SYSCOMMAND
, SC_MINIMIZE
, 0 );
690 else /* look for an enabled window owned by this one */
692 HWND owned
= GetWindow( GetDesktopWindow(), GW_CHILD
);
693 while (owned
&& owned
!= hwnd
)
695 if (IsWindowVisible( owned
) &&
696 IsWindowEnabled( owned
) &&
697 (GetWindow( owned
, GW_OWNER
) == hwnd
))
699 owned
= GetWindow( owned
, GW_HWNDNEXT
);
703 SetForegroundWindow( hwnd
);
706 static void show_taskbar_contextmenu( HWND button
, LPARAM lparam
)
708 ULONG_PTR id
= GetWindowLongPtrW( button
, GWLP_ID
);
710 if (id
) SendNotifyMessageW( (HWND
)id
, WM_POPUPSYSTEMMENU
, 0, lparam
);
713 static void do_hide_systray(void)
715 SetWindowPos( tray_window
, 0,
716 GetSystemMetrics(SM_XVIRTUALSCREEN
) + GetSystemMetrics(SM_CXVIRTUALSCREEN
),
717 GetSystemMetrics(SM_YVIRTUALSCREEN
) + GetSystemMetrics(SM_CYVIRTUALSCREEN
),
718 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
721 static BOOL
notify_owner( struct icon
*icon
, UINT msg
, POINT pt
)
723 WPARAM wp
= icon
->id
;
726 if (icon
->version
>= NOTIFYICON_VERSION_4
)
728 ClientToScreen( tray_window
, &pt
);
729 wp
= MAKEWPARAM( pt
.x
, pt
.y
);
730 lp
= MAKELPARAM( msg
, icon
->id
);
733 TRACE( "relaying 0x%x\n", msg
);
734 if (!SendNotifyMessageW( icon
->owner
, icon
->callback_message
, wp
, lp
) &&
735 (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
))
737 WARN( "application window was destroyed, removing icon %u\n", icon
->id
);
744 static void do_show_systray(void)
747 NONCLIENTMETRICSW ncm
;
749 HDC hdc
= GetDC( 0 );
751 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
752 SystemParametersInfoW( SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0 );
753 font
= CreateFontIndirectW( &ncm
.lfCaptionFont
);
754 /* FIXME: Implement BCM_GETIDEALSIZE and use that instead. */
755 SelectObject( hdc
, font
);
756 GetTextExtentPointA( hdc
, "abcdefghijklmnopqrstuvwxyz", 26, &size
);
757 taskbar_button_width
= size
.cx
;
758 GetTextExtentPointW( hdc
, start_label
, lstrlenW(start_label
), &size
);
759 /* add some margins (FIXME) */
760 size
.cx
+= 12 + GetSystemMetrics( SM_CXSMICON
);
763 DeleteObject( font
);
765 tray_width
= GetSystemMetrics( SM_CXSCREEN
);
766 tray_height
= max( icon_cy
, size
.cy
);
767 start_button_width
= size
.cx
;
768 SetWindowPos( tray_window
, 0, 0, GetSystemMetrics( SM_CYSCREEN
) - tray_height
,
769 tray_width
, tray_height
, SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_SHOWWINDOW
);
770 sync_taskbar_buttons();
773 static LRESULT WINAPI
tray_wndproc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
778 return handle_incoming((HWND
)wparam
, (COPYDATASTRUCT
*)lparam
);
780 case WM_DISPLAYCHANGE
:
781 if (hide_systray
|| (!nb_displayed
&& !enable_shell
)) do_hide_systray();
782 else do_show_systray();
788 case BALLOON_CREATE_TIMER
: balloon_create_timer(); break;
789 case BALLOON_SHOW_TIMER
: balloon_timer(); break;
799 hdc
= BeginPaint( hwnd
, &ps
);
800 for (i
= 0; i
< nb_displayed
; i
++)
802 RECT dummy
, rect
= get_icon_rect( displayed
[i
] );
803 if (IntersectRect( &dummy
, &rect
, &ps
.rcPaint
))
804 DrawIconEx( hdc
, rect
.left
+ ICON_BORDER
, rect
.top
+ ICON_BORDER
, displayed
[i
]->image
,
805 icon_cx
- 2*ICON_BORDER
, icon_cy
- 2*ICON_BORDER
,
806 0, 0, DI_DEFAULTSIZE
|DI_NORMAL
);
808 EndPaint( hwnd
, &ps
);
819 case WM_LBUTTONDBLCLK
:
820 case WM_RBUTTONDBLCLK
:
821 case WM_MBUTTONDBLCLK
:
824 POINT pt
= { (short)LOWORD(lparam
), (short)HIWORD(lparam
) };
825 struct icon
*icon
= icon_from_point( pt
.x
, pt
.y
);
829 message
.message
= msg
;
830 message
.wParam
= wparam
;
831 message
.lParam
= lparam
;
832 SendMessageW( icon
->tooltip
, TTM_RELAYEVENT
, 0, (LPARAM
)&message
);
834 if (!notify_owner( icon
, msg
, pt
)) break;
836 if (icon
->version
> 0)
841 notify_owner( icon
, WM_CONTEXTMENU
, pt
);
844 notify_owner( icon
, NIN_SELECT
, pt
);
852 /* don't destroy the tray window, just hide it */
853 ShowWindow( hwnd
, SW_HIDE
);
857 paint_taskbar_button( (const DRAWITEMSTRUCT
*)lparam
);
861 if (HIWORD(wparam
) == BN_CLICKED
) click_taskbar_button( (HWND
)lparam
);
865 show_taskbar_contextmenu( (HWND
)wparam
, lparam
);
868 case WM_MOUSEACTIVATE
:
869 return MA_NOACTIVATE
;
871 case WM_INITMENUPOPUP
:
873 return menu_wndproc(hwnd
, msg
, wparam
, lparam
);
876 return DefWindowProcW( hwnd
, msg
, wparam
, lparam
);
881 /* notification posted to the desktop window */
882 void handle_parent_notify( HWND hwnd
, WPARAM wp
)
887 add_taskbar_button( hwnd
);
890 remove_taskbar_button( hwnd
);
891 cleanup_systray_window( hwnd
);
894 sync_taskbar_buttons();
897 /* this function creates the listener window */
898 void initialize_systray( HMODULE graphics_driver
, BOOL using_root
, BOOL arg_enable_shell
)
901 static const WCHAR classname
[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0};
903 if (using_root
&& graphics_driver
) wine_notify_icon
= (void *)GetProcAddress( graphics_driver
, "wine_notify_icon" );
905 icon_cx
= GetSystemMetrics( SM_CXSMICON
) + 2*ICON_BORDER
;
906 icon_cy
= GetSystemMetrics( SM_CYSMICON
) + 2*ICON_BORDER
;
907 hide_systray
= using_root
;
908 enable_shell
= arg_enable_shell
;
910 /* register the systray listener window class */
911 ZeroMemory(&class, sizeof(class));
912 class.cbSize
= sizeof(class);
913 class.style
= CS_DBLCLKS
| CS_HREDRAW
;
914 class.lpfnWndProc
= tray_wndproc
;
915 class.hInstance
= NULL
;
916 class.hIcon
= LoadIconW(0, (LPCWSTR
)IDI_WINLOGO
);
917 class.hCursor
= LoadCursorW(0, (LPCWSTR
)IDC_ARROW
);
918 class.hbrBackground
= (HBRUSH
) COLOR_WINDOW
;
919 class.lpszClassName
= classname
;
921 if (!RegisterClassExW(&class))
923 WINE_ERR("Could not register SysTray window class\n");
927 tray_window
= CreateWindowExW( WS_EX_NOACTIVATE
, classname
, NULL
, WS_POPUP
,
928 0, GetSystemMetrics( SM_CYSCREEN
), 0, 0, 0, 0, 0, 0 );
931 WINE_ERR("Could not create tray window\n");
935 LoadStringW( NULL
, IDS_START_LABEL
, start_label
, ARRAY_SIZE( start_label
));
937 add_taskbar_button( 0 );
939 if (hide_systray
) do_hide_systray();
940 else if (enable_shell
) do_show_systray();