winex11: Allow retrieving the window data structure from another thread, with appropr...
[wine] / dlls / winex11.drv / systray.c
1 /*
2  * X11 system tray management
3  *
4  * Copyright (C) 2004 Mike Hearn, for CodeWeavers
5  * Copyright (C) 2005 Robert Shearman
6  * Copyright (C) 2008 Alexandre Julliard
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32
33 #include <X11/Xlib.h>
34
35 #define NONAMELESSUNION
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "commctrl.h"
41 #include "shellapi.h"
42
43 #include "x11drv.h"
44 #include "wine/list.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(systray);
48
49 /* an individual systray icon */
50 struct tray_icon
51 {
52     struct list    entry;
53     HICON          image;    /* the image to render */
54     HWND           owner;    /* the HWND passed in to the Shell_NotifyIcon call */
55     HWND           window;   /* the adaptor window */
56     HWND           tooltip;  /* Icon tooltip */
57     UINT           state;    /* state flags */
58     UINT           id;       /* the unique id given by the app */
59     UINT           callback_message;
60     int            display;  /* display index, or -1 if hidden */
61     WCHAR          tiptext[128];    /* tooltip text */
62     WCHAR          info_text[256];  /* info balloon text */
63     WCHAR          info_title[64];  /* info balloon title */
64     UINT           info_flags;      /* flags for info balloon */
65     UINT           info_timeout;    /* timeout for info balloon */
66     HICON          info_icon;       /* info balloon icon */
67 };
68
69 static struct list icon_list = LIST_INIT( icon_list );
70
71 static const WCHAR icon_classname[] = {'_','_','w','i','n','e','x','1','1','_','t','r','a','y','_','i','c','o','n',0};
72 static const WCHAR tray_classname[] = {'_','_','w','i','n','e','x','1','1','_','s','t','a','n','d','a','l','o','n','e','_','t','r','a','y',0};
73
74 static BOOL show_icon( struct tray_icon *icon );
75 static BOOL hide_icon( struct tray_icon *icon );
76 static BOOL delete_icon( struct tray_icon *icon );
77
78 #define SYSTEM_TRAY_REQUEST_DOCK  0
79 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
80 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
81
82 Atom systray_atom = 0;
83
84 #define MIN_DISPLAYED 8
85 #define ICON_BORDER 2
86
87 #define VALID_WIN_TIMER      1
88 #define BALLOON_CREATE_TIMER 2
89 #define BALLOON_SHOW_TIMER   3
90
91 #define VALID_WIN_TIMEOUT        2000
92 #define BALLOON_CREATE_TIMEOUT   2000
93 #define BALLOON_SHOW_MIN_TIMEOUT 10000
94 #define BALLOON_SHOW_MAX_TIMEOUT 30000
95
96 static struct tray_icon *balloon_icon;
97 static HWND balloon_window;
98 static POINT balloon_pos;
99
100 /* stand-alone tray window */
101 static HWND standalone_tray;
102 static int icon_cx, icon_cy;
103 static unsigned int nb_displayed;
104
105 /* retrieves icon record by owner window and ID */
106 static struct tray_icon *get_icon(HWND owner, UINT id)
107 {
108     struct tray_icon *this;
109
110     LIST_FOR_EACH_ENTRY( this, &icon_list, struct tray_icon, entry )
111         if ((this->id == id) && (this->owner == owner)) return this;
112     return NULL;
113 }
114
115 static void init_common_controls(void)
116 {
117     static BOOL initialized = FALSE;
118
119     if (!initialized)
120     {
121         INITCOMMONCONTROLSEX init_tooltip;
122
123         init_tooltip.dwSize = sizeof(INITCOMMONCONTROLSEX);
124         init_tooltip.dwICC = ICC_TAB_CLASSES;
125
126         InitCommonControlsEx(&init_tooltip);
127         initialized = TRUE;
128     }
129 }
130
131 /* create tooltip window for icon */
132 static void create_tooltip(struct tray_icon *icon)
133 {
134     init_common_controls();
135     icon->tooltip = CreateWindowExW( WS_EX_TOPMOST, TOOLTIPS_CLASSW, NULL,
136                                      WS_POPUP | TTS_ALWAYSTIP,
137                                      CW_USEDEFAULT, CW_USEDEFAULT,
138                                      CW_USEDEFAULT, CW_USEDEFAULT,
139                                      icon->window, NULL, NULL, NULL);
140     if (icon->tooltip)
141     {
142         TTTOOLINFOW ti;
143         ZeroMemory(&ti, sizeof(ti));
144         ti.cbSize = sizeof(TTTOOLINFOW);
145         ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND;
146         ti.hwnd = icon->window;
147         ti.uId = (UINT_PTR)icon->window;
148         ti.lpszText = icon->tiptext;
149         SendMessageW(icon->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&ti);
150     }
151 }
152
153 void update_systray_balloon_position(void)
154 {
155     RECT rect;
156     POINT pos;
157
158     if (!balloon_icon) return;
159     GetWindowRect( balloon_icon->window, &rect );
160     pos.x = (rect.left + rect.right) / 2;
161     pos.y = (rect.top + rect.bottom) / 2;
162     if (pos.x == balloon_pos.x && pos.y == balloon_pos.y) return;  /* nothing changed */
163     balloon_pos = pos;
164     SendMessageW( balloon_window, TTM_TRACKPOSITION, 0, MAKELONG( pos.x, pos.y ));
165 }
166
167 static void balloon_create_timer( struct tray_icon *icon )
168 {
169     TTTOOLINFOW ti;
170
171     init_common_controls();
172     balloon_window = CreateWindowExW( WS_EX_TOPMOST, TOOLTIPS_CLASSW, NULL,
173                                       WS_POPUP | TTS_ALWAYSTIP | TTS_NOPREFIX | TTS_BALLOON | TTS_CLOSE,
174                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
175                                       icon->window, NULL, NULL, NULL);
176
177     memset( &ti, 0, sizeof(ti) );
178     ti.cbSize = sizeof(TTTOOLINFOW);
179     ti.hwnd = icon->window;
180     ti.uId = (UINT_PTR)icon->window;
181     ti.uFlags = TTF_TRACK | TTF_IDISHWND;
182     ti.lpszText = icon->info_text;
183     SendMessageW( balloon_window, TTM_ADDTOOLW, 0, (LPARAM)&ti );
184     if ((icon->info_flags & NIIF_ICONMASK) == NIIF_USER)
185         SendMessageW( balloon_window, TTM_SETTITLEW, (WPARAM)icon->info_icon, (LPARAM)icon->info_title );
186     else
187         SendMessageW( balloon_window, TTM_SETTITLEW, icon->info_flags, (LPARAM)icon->info_title );
188     balloon_icon = icon;
189     balloon_pos.x = balloon_pos.y = MAXLONG;
190     update_systray_balloon_position();
191     SendMessageW( balloon_window, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti );
192     KillTimer( icon->window, BALLOON_CREATE_TIMER );
193     SetTimer( icon->window, BALLOON_SHOW_TIMER, icon->info_timeout, NULL );
194 }
195
196 static BOOL show_balloon( struct tray_icon *icon )
197 {
198     if (standalone_tray && !show_systray) return FALSE;  /* no systray window */
199     if (!icon->window) return FALSE;  /* not displayed */
200     if (!icon->info_text[0]) return FALSE;  /* no balloon */
201     balloon_icon = icon;
202     SetTimer( icon->window, BALLOON_CREATE_TIMER, BALLOON_CREATE_TIMEOUT, NULL );
203     return TRUE;
204 }
205
206 static void hide_balloon(void)
207 {
208     if (!balloon_icon) return;
209     if (balloon_window)
210     {
211         KillTimer( balloon_icon->window, BALLOON_SHOW_TIMER );
212         DestroyWindow( balloon_window );
213         balloon_window = 0;
214     }
215     else KillTimer( balloon_icon->window, BALLOON_CREATE_TIMER );
216     balloon_icon = NULL;
217 }
218
219 static void show_next_balloon(void)
220 {
221     struct tray_icon *icon;
222
223     LIST_FOR_EACH_ENTRY( icon, &icon_list, struct tray_icon, entry )
224         if (show_balloon( icon )) break;
225 }
226
227 static void update_balloon( struct tray_icon *icon )
228 {
229     if (balloon_icon == icon)
230     {
231         hide_balloon();
232         show_balloon( icon );
233     }
234     else if (!balloon_icon)
235     {
236         if (!show_balloon( icon )) return;
237     }
238     if (!balloon_icon) show_next_balloon();
239 }
240
241 static void balloon_timer(void)
242 {
243     if (balloon_icon) balloon_icon->info_text[0] = 0;  /* clear text now that balloon has been shown */
244     hide_balloon();
245     show_next_balloon();
246 }
247
248 /* synchronize tooltip text with tooltip window */
249 static void update_tooltip_text(struct tray_icon *icon)
250 {
251     TTTOOLINFOW ti;
252
253     ZeroMemory(&ti, sizeof(ti));
254     ti.cbSize = sizeof(TTTOOLINFOW);
255     ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND;
256     ti.hwnd = icon->window;
257     ti.uId = (UINT_PTR)icon->window;
258     ti.lpszText = icon->tiptext;
259
260     SendMessageW(icon->tooltip, TTM_UPDATETIPTEXTW, 0, (LPARAM)&ti);
261 }
262
263 /* get the size of the stand-alone tray window */
264 static SIZE get_window_size(void)
265 {
266     SIZE size;
267     RECT rect;
268
269     rect.left = 0;
270     rect.top = 0;
271     rect.right = icon_cx * max( nb_displayed, MIN_DISPLAYED );
272     rect.bottom = icon_cy;
273     AdjustWindowRect( &rect, WS_CAPTION, FALSE );
274     size.cx = rect.right - rect.left;
275     size.cy = rect.bottom - rect.top;
276     return size;
277 }
278
279 /* get the position of an icon in the stand-alone tray */
280 static POINT get_icon_pos( struct tray_icon *icon )
281 {
282     POINT pos;
283
284     pos.x = icon_cx * icon->display;
285     pos.y = 0;
286     return pos;
287 }
288
289 /* window procedure for the standalone tray window */
290 static LRESULT WINAPI standalone_tray_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
291 {
292     switch (msg)
293     {
294     case WM_MOVE:
295         update_systray_balloon_position();
296         break;
297     case WM_CLOSE:
298         ShowWindow( hwnd, SW_HIDE );
299         hide_balloon();
300         show_systray = FALSE;
301         return 0;
302     case WM_DESTROY:
303         standalone_tray = 0;
304         break;
305     }
306     return DefWindowProcW( hwnd, msg, wparam, lparam );
307 }
308
309 /* add an icon to the standalone tray window */
310 static void add_to_standalone_tray( struct tray_icon *icon )
311 {
312     SIZE size;
313     POINT pos;
314
315     if (!standalone_tray)
316     {
317         static const WCHAR winname[] = {'W','i','n','e',' ','S','y','s','t','e','m',' ','T','r','a','y',0};
318
319         size = get_window_size();
320         standalone_tray = CreateWindowExW( 0, tray_classname, winname, WS_CAPTION | WS_SYSMENU,
321                                            CW_USEDEFAULT, CW_USEDEFAULT, size.cx, size.cy, 0, 0, 0, 0 );
322         if (!standalone_tray) return;
323     }
324
325     icon->display = nb_displayed;
326     pos = get_icon_pos( icon );
327     icon->window = CreateWindowW( icon_classname, NULL, WS_CHILD | WS_VISIBLE,
328                                   pos.x, pos.y, icon_cx, icon_cy, standalone_tray, NULL, NULL, icon );
329     if (!icon->window)
330     {
331         icon->display = -1;
332         return;
333     }
334     create_tooltip( icon );
335
336     nb_displayed++;
337     size = get_window_size();
338     SetWindowPos( standalone_tray, 0, 0, 0, size.cx, size.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
339     if (nb_displayed == 1 && show_systray) ShowWindow( standalone_tray, SW_SHOWNA );
340     TRACE( "added %u now %d icons\n", icon->id, nb_displayed );
341 }
342
343 /* remove an icon from the stand-alone tray */
344 static void remove_from_standalone_tray( struct tray_icon *icon )
345 {
346     struct tray_icon *ptr;
347     POINT pos;
348
349     if (icon->display == -1) return;
350
351     LIST_FOR_EACH_ENTRY( ptr, &icon_list, struct tray_icon, entry )
352     {
353         if (ptr == icon) continue;
354         if (ptr->display < icon->display) continue;
355         ptr->display--;
356         pos = get_icon_pos( ptr );
357         SetWindowPos( ptr->window, 0, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER );
358     }
359     icon->display = -1;
360     if (!--nb_displayed) ShowWindow( standalone_tray, SW_HIDE );
361     TRACE( "removed %u now %d icons\n", icon->id, nb_displayed );
362 }
363
364 /* window procedure for the individual tray icon window */
365 static LRESULT WINAPI tray_icon_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
366 {
367     struct tray_icon *icon = NULL;
368     BOOL ret;
369
370     TRACE("hwnd=%p, msg=0x%x\n", hwnd, msg);
371
372     /* set the icon data for the window from the data passed into CreateWindow */
373     if (msg == WM_NCCREATE)
374         SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LPARAM)((const CREATESTRUCTW *)lparam)->lpCreateParams);
375
376     icon = (struct tray_icon *) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
377
378     switch (msg)
379     {
380     case WM_CREATE:
381         SetTimer( hwnd, VALID_WIN_TIMER, VALID_WIN_TIMEOUT, NULL );
382         break;
383
384     case WM_PAINT:
385         {
386             PAINTSTRUCT ps;
387             RECT rc;
388             HDC hdc;
389             int cx = GetSystemMetrics( SM_CXSMICON );
390             int cy = GetSystemMetrics( SM_CYSMICON );
391
392             hdc = BeginPaint(hwnd, &ps);
393             GetClientRect(hwnd, &rc);
394             TRACE("painting rect %s\n", wine_dbgstr_rect(&rc));
395             DrawIconEx( hdc, (rc.left + rc.right - cx) / 2, (rc.top + rc.bottom - cy) / 2,
396                         icon->image, cx, cy, 0, 0, DI_DEFAULTSIZE|DI_NORMAL );
397             EndPaint(hwnd, &ps);
398             return 0;
399         }
400
401     case WM_MOUSEMOVE:
402     case WM_LBUTTONDOWN:
403     case WM_LBUTTONUP:
404     case WM_RBUTTONDOWN:
405     case WM_RBUTTONUP:
406     case WM_MBUTTONDOWN:
407     case WM_MBUTTONUP:
408     case WM_LBUTTONDBLCLK:
409     case WM_RBUTTONDBLCLK:
410     case WM_MBUTTONDBLCLK:
411         /* notify the owner hwnd of the message */
412         TRACE("relaying 0x%x\n", msg);
413         ret = PostMessageW(icon->owner, icon->callback_message, icon->id, msg);
414         if (!ret && (GetLastError() == ERROR_INVALID_WINDOW_HANDLE))
415         {
416             WARN( "application window was destroyed, removing icon %u\n", icon->id );
417             delete_icon( icon );
418         }
419         return 0;
420
421     case WM_WINDOWPOSCHANGED:
422         update_systray_balloon_position();
423         break;
424
425     case WM_TIMER:
426         switch (wparam)
427         {
428         case VALID_WIN_TIMER:
429             if (!IsWindow( icon->owner )) delete_icon( icon );
430             break;
431         case BALLOON_CREATE_TIMER:
432             balloon_create_timer( icon );
433             break;
434         case BALLOON_SHOW_TIMER:
435             balloon_timer();
436             break;
437         }
438         return 0;
439
440     case WM_CLOSE:
441         if (icon->display == -1)
442         {
443             TRACE( "icon %u no longer embedded\n", icon->id );
444             hide_icon( icon );
445             add_to_standalone_tray( icon );
446         }
447         return 0;
448     }
449     return DefWindowProcW( hwnd, msg, wparam, lparam );
450 }
451
452 /* find the X11 window owner the system tray selection */
453 static Window get_systray_selection_owner( Display *display )
454 {
455     return XGetSelectionOwner( display, systray_atom );
456 }
457
458 static BOOL init_systray(void)
459 {
460     static BOOL init_done;
461     WNDCLASSEXW class;
462     Display *display;
463
464     if (root_window != DefaultRootWindow( gdi_display )) return FALSE;
465     if (init_done) return TRUE;
466
467     icon_cx = GetSystemMetrics( SM_CXSMICON ) + 2 * ICON_BORDER;
468     icon_cy = GetSystemMetrics( SM_CYSMICON ) + 2 * ICON_BORDER;
469
470     memset( &class, 0, sizeof(class) );
471     class.cbSize        = sizeof(class);
472     class.lpfnWndProc   = tray_icon_wndproc;
473     class.hIcon         = LoadIconW(0, (LPCWSTR)IDI_WINLOGO);
474     class.hCursor       = LoadCursorW( 0, (LPCWSTR)IDC_ARROW );
475     class.lpszClassName = icon_classname;
476     class.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
477
478     if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
479     {
480         ERR( "Could not register icon tray window class\n" );
481         return FALSE;
482     }
483
484     class.lpfnWndProc   = standalone_tray_wndproc;
485     class.hbrBackground = (HBRUSH)COLOR_WINDOW;
486     class.lpszClassName = tray_classname;
487     class.style         = CS_DBLCLKS;
488
489     if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
490     {
491         ERR( "Could not register standalone tray window class\n" );
492         return FALSE;
493     }
494
495     display = thread_init_display();
496     if (DefaultScreen( display ) == 0)
497         systray_atom = x11drv_atom(_NET_SYSTEM_TRAY_S0);
498     else
499     {
500         char systray_buffer[29]; /* strlen(_NET_SYSTEM_TRAY_S4294967295)+1 */
501         sprintf( systray_buffer, "_NET_SYSTEM_TRAY_S%u", DefaultScreen( display ) );
502         systray_atom = XInternAtom( display, systray_buffer, False );
503     }
504     XSelectInput( display, root_window, StructureNotifyMask );
505
506     init_done = TRUE;
507     return TRUE;
508 }
509
510 /* dock the given icon with the NETWM system tray */
511 static void dock_systray_icon( Display *display, struct tray_icon *icon, Window systray_window )
512 {
513     Window window;
514     XEvent ev;
515     XSetWindowAttributes attr;
516
517     icon->window = CreateWindowW( icon_classname, NULL, WS_CLIPSIBLINGS | WS_POPUP,
518                                   CW_USEDEFAULT, CW_USEDEFAULT, icon_cx, icon_cy,
519                                   NULL, NULL, NULL, icon );
520     make_window_embedded( icon->window );
521     create_tooltip( icon );
522     ShowWindow( icon->window, SW_SHOWNA );
523
524     if (!(window = X11DRV_get_whole_window( icon->window ))) return;
525     TRACE( "icon window %p/%lx\n", icon->window, window );
526
527     /* send the docking request message */
528     ev.xclient.type = ClientMessage;
529     ev.xclient.window = systray_window;
530     ev.xclient.message_type = x11drv_atom( _NET_SYSTEM_TRAY_OPCODE );
531     ev.xclient.format = 32;
532     ev.xclient.data.l[0] = CurrentTime;
533     ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
534     ev.xclient.data.l[2] = window;
535     ev.xclient.data.l[3] = 0;
536     ev.xclient.data.l[4] = 0;
537     XSendEvent( display, systray_window, False, NoEventMask, &ev );
538     attr.background_pixmap = ParentRelative;
539     attr.bit_gravity = ForgetGravity;
540     XChangeWindowAttributes( display, window, CWBackPixmap | CWBitGravity, &attr );
541 }
542
543 /* dock systray windows again with the new owner */
544 void change_systray_owner( Display *display, Window systray_window )
545 {
546     struct tray_icon *icon;
547
548     TRACE( "new owner %lx\n", systray_window );
549     LIST_FOR_EACH_ENTRY( icon, &icon_list, struct tray_icon, entry )
550     {
551         if (icon->display == -1) continue;
552         hide_icon( icon );
553         dock_systray_icon( display, icon, systray_window );
554     }
555 }
556
557 /* hide a tray icon */
558 static BOOL hide_icon( struct tray_icon *icon )
559 {
560     struct x11drv_win_data *data;
561
562     TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner );
563
564     if (!icon->window) return TRUE;  /* already hidden */
565
566     /* make sure we don't try to unmap it, it confuses some systray docks */
567     if ((data = X11DRV_get_win_data( icon->window )) && data->embedded) data->mapped = FALSE;
568
569     DestroyWindow(icon->window);
570     DestroyWindow(icon->tooltip);
571     icon->window = 0;
572     icon->tooltip = 0;
573     remove_from_standalone_tray( icon );
574     update_balloon( icon );
575     return TRUE;
576 }
577
578 /* make the icon visible */
579 static BOOL show_icon( struct tray_icon *icon )
580 {
581     Window systray_window;
582     Display *display = thread_init_display();
583
584     TRACE( "id=0x%x, hwnd=%p\n", icon->id, icon->owner );
585
586     if (icon->window) return TRUE;  /* already shown */
587
588     if ((systray_window = get_systray_selection_owner( display )))
589         dock_systray_icon( display, icon, systray_window );
590     else
591         add_to_standalone_tray( icon );
592
593     update_balloon( icon );
594     return TRUE;
595 }
596
597 /* Modifies an existing icon record */
598 static BOOL modify_icon( struct tray_icon *icon, NOTIFYICONDATAW *nid )
599 {
600     TRACE( "id=0x%x hwnd=%p flags=%x\n", nid->uID, nid->hWnd, nid->uFlags );
601
602     if (nid->uFlags & NIF_STATE)
603     {
604         icon->state = (icon->state & ~nid->dwStateMask) | (nid->dwState & nid->dwStateMask);
605     }
606
607     if (nid->uFlags & NIF_ICON)
608     {
609         if (icon->image) DestroyIcon(icon->image);
610         icon->image = CopyIcon(nid->hIcon);
611         if (icon->window)
612         {
613             if (icon->display != -1) InvalidateRect( icon->window, NULL, TRUE );
614             else
615             {
616                 struct x11drv_win_data *data = X11DRV_get_win_data( icon->window );
617                 if (data) XClearArea( gdi_display, data->whole_window, 0, 0, 0, 0, True );
618             }
619         }
620     }
621
622     if (nid->uFlags & NIF_MESSAGE)
623     {
624         icon->callback_message = nid->uCallbackMessage;
625     }
626     if (nid->uFlags & NIF_TIP)
627     {
628         lstrcpynW(icon->tiptext, nid->szTip, sizeof(icon->tiptext)/sizeof(WCHAR));
629         if (icon->tooltip) update_tooltip_text(icon);
630     }
631     if (nid->uFlags & NIF_INFO && nid->cbSize >= NOTIFYICONDATAA_V2_SIZE)
632     {
633         lstrcpynW( icon->info_text, nid->szInfo, sizeof(icon->info_text)/sizeof(WCHAR) );
634         lstrcpynW( icon->info_title, nid->szInfoTitle, sizeof(icon->info_title)/sizeof(WCHAR) );
635         icon->info_flags = nid->dwInfoFlags;
636         icon->info_timeout = max(min(nid->u.uTimeout, BALLOON_SHOW_MAX_TIMEOUT), BALLOON_SHOW_MIN_TIMEOUT);
637         icon->info_icon = nid->hBalloonIcon;
638         update_balloon( icon );
639     }
640     if (icon->state & NIS_HIDDEN) hide_icon( icon );
641     else show_icon( icon );
642     return TRUE;
643 }
644
645 /* Adds a new icon record to the list */
646 static BOOL add_icon(NOTIFYICONDATAW *nid)
647 {
648     struct tray_icon  *icon;
649
650     TRACE("id=0x%x, hwnd=%p\n", nid->uID, nid->hWnd);
651
652     if ((icon = get_icon(nid->hWnd, nid->uID)))
653     {
654         WARN("duplicate tray icon add, buggy app?\n");
655         return FALSE;
656     }
657
658     if (!(icon = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*icon))))
659     {
660         ERR("out of memory\n");
661         return FALSE;
662     }
663
664     ZeroMemory(icon, sizeof(struct tray_icon));
665     icon->id     = nid->uID;
666     icon->owner  = nid->hWnd;
667     icon->display = -1;
668
669     list_add_tail(&icon_list, &icon->entry);
670
671     return modify_icon( icon, nid );
672 }
673
674 /* delete tray icon window and icon structure */
675 static BOOL delete_icon( struct tray_icon *icon )
676 {
677     hide_icon( icon );
678     list_remove( &icon->entry );
679     DestroyIcon( icon->image );
680     HeapFree( GetProcessHeap(), 0, icon );
681     return TRUE;
682 }
683
684
685 /***********************************************************************
686  *              wine_notify_icon   (X11DRV.@)
687  *
688  * Driver-side implementation of Shell_NotifyIcon.
689  */
690 int CDECL wine_notify_icon( DWORD msg, NOTIFYICONDATAW *data )
691 {
692     BOOL ret = FALSE;
693     struct tray_icon *icon;
694
695     switch (msg)
696     {
697     case NIM_ADD:
698         if (!init_systray()) return -1;  /* fall back to default handling */
699         ret = add_icon( data );
700         break;
701     case NIM_DELETE:
702         if ((icon = get_icon( data->hWnd, data->uID ))) ret = delete_icon( icon );
703         break;
704     case NIM_MODIFY:
705         if ((icon = get_icon( data->hWnd, data->uID ))) ret = modify_icon( icon, data );
706         break;
707     default:
708         FIXME( "unhandled tray message: %u\n", msg );
709         break;
710     }
711     return ret;
712 }