2 * Mac driver system tray management
4 * Copyright (C) 2004 Mike Hearn, for CodeWeavers
5 * Copyright (C) 2005 Robert Shearman
6 * Copyright (C) 2008 Alexandre Julliard
7 * Copyright (C) 2012, 2013 Ken Thomases for CodeWeavers Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/list.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(systray
);
38 /* an individual systray icon */
42 HWND owner
; /* the HWND passed in to the Shell_NotifyIcon call */
43 UINT id
; /* the unique id given by the app */
44 UINT callback_message
;
45 HICON image
; /* the image to render */
46 WCHAR tiptext
[128]; /* tooltip text */
47 DWORD state
; /* state flags */
48 macdrv_status_item status_item
;
52 static struct list icon_list
= LIST_INIT(icon_list
);
55 static BOOL
delete_icon(struct tray_icon
*icon
);
58 /***********************************************************************
61 * Delete all systray icons owned by a given window.
63 static void cleanup_icons(HWND hwnd
)
65 struct tray_icon
*icon
, *next
;
67 LIST_FOR_EACH_ENTRY_SAFE(icon
, next
, &icon_list
, struct tray_icon
, entry
)
68 if (icon
->owner
== hwnd
) delete_icon(icon
);
72 /***********************************************************************
75 * Retrieves an icon record by owner window and ID.
77 static struct tray_icon
*get_icon(HWND owner
, UINT id
)
79 struct tray_icon
*this;
81 LIST_FOR_EACH_ENTRY(this, &icon_list
, struct tray_icon
, entry
)
82 if ((this->id
== id
) && (this->owner
== owner
)) return this;
87 /***********************************************************************
90 * Modifies an existing tray icon and updates its status item as needed.
92 static BOOL
modify_icon(struct tray_icon
*icon
, NOTIFYICONDATAW
*nid
)
94 BOOL update_image
= FALSE
, update_tooltip
= FALSE
;
96 TRACE("hwnd %p id 0x%x flags %x\n", nid
->hWnd
, nid
->uID
, nid
->uFlags
);
98 if (nid
->uFlags
& NIF_STATE
)
100 DWORD changed
= (icon
->state
^ nid
->dwState
) & nid
->dwStateMask
;
101 icon
->state
= (icon
->state
& ~nid
->dwStateMask
) | (nid
->dwState
& nid
->dwStateMask
);
102 if (changed
& NIS_HIDDEN
)
104 if (icon
->state
& NIS_HIDDEN
)
106 if (icon
->status_item
)
108 TRACE("destroying status item %p\n", icon
->status_item
);
109 macdrv_destroy_status_item(icon
->status_item
);
110 icon
->status_item
= NULL
;
115 if (!icon
->status_item
)
117 struct macdrv_thread_data
*thread_data
= macdrv_init_thread_data();
119 icon
->status_item
= macdrv_create_status_item(thread_data
->queue
);
120 if (icon
->status_item
)
122 TRACE("created status item %p\n", icon
->status_item
);
126 if (lstrlenW(icon
->tiptext
))
127 update_tooltip
= TRUE
;
130 WARN("failed to create status item\n");
136 if (nid
->uFlags
& NIF_ICON
)
138 if (icon
->image
) DestroyIcon(icon
->image
);
139 icon
->image
= CopyIcon(nid
->hIcon
);
140 if (icon
->status_item
)
144 if (nid
->uFlags
& NIF_MESSAGE
)
146 icon
->callback_message
= nid
->uCallbackMessage
;
148 if (nid
->uFlags
& NIF_TIP
)
150 lstrcpynW(icon
->tiptext
, nid
->szTip
, sizeof(icon
->tiptext
)/sizeof(WCHAR
));
151 if (icon
->status_item
)
152 update_tooltip
= TRUE
;
157 CGImageRef cgimage
= NULL
;
159 cgimage
= create_cgimage_from_icon(icon
->image
, 0, 0);
160 macdrv_set_status_item_image(icon
->status_item
, cgimage
);
161 CGImageRelease(cgimage
);
168 TRACE("setting tooltip text for status item %p to %s\n", icon
->status_item
,
169 debugstr_w(icon
->tiptext
));
170 s
= CFStringCreateWithCharacters(NULL
, (UniChar
*)icon
->tiptext
,
171 lstrlenW(icon
->tiptext
));
172 macdrv_set_status_item_tooltip(icon
->status_item
, s
);
180 /***********************************************************************
183 * Creates a new tray icon structure and adds it to the list.
185 static BOOL
add_icon(NOTIFYICONDATAW
*nid
)
187 NOTIFYICONDATAW new_nid
;
188 struct tray_icon
*icon
;
190 TRACE("hwnd %p id 0x%x\n", nid
->hWnd
, nid
->uID
);
192 if ((icon
= get_icon(nid
->hWnd
, nid
->uID
)))
194 WARN("duplicate tray icon add, buggy app?\n");
198 if (!(icon
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*icon
))))
200 ERR("out of memory\n");
205 icon
->owner
= nid
->hWnd
;
206 icon
->state
= NIS_HIDDEN
;
208 list_add_tail(&icon_list
, &icon
->entry
);
210 if (!(nid
->uFlags
& NIF_STATE
) || !(nid
->dwStateMask
& NIS_HIDDEN
))
213 new_nid
.uFlags
|= NIF_STATE
;
214 new_nid
.dwState
&= ~NIS_HIDDEN
;
215 new_nid
.dwStateMask
|= NIS_HIDDEN
;
218 return modify_icon(icon
, nid
);
222 /***********************************************************************
225 * Destroy tray icon status item and delete structure.
227 static BOOL
delete_icon(struct tray_icon
*icon
)
229 TRACE("hwnd %p id 0x%x\n", icon
->owner
, icon
->id
);
231 if (icon
->status_item
)
233 TRACE("destroying status item %p\n", icon
->status_item
);
234 macdrv_destroy_status_item(icon
->status_item
);
236 list_remove(&icon
->entry
);
237 DestroyIcon(icon
->image
);
238 HeapFree(GetProcessHeap(), 0, icon
);
243 /***********************************************************************
244 * wine_notify_icon (MACDRV.@)
246 * Driver-side implementation of Shell_NotifyIcon.
248 int CDECL
wine_notify_icon(DWORD msg
, NOTIFYICONDATAW
*data
)
251 struct tray_icon
*icon
;
256 ret
= add_icon(data
);
259 if ((icon
= get_icon(data
->hWnd
, data
->uID
))) ret
= delete_icon(icon
);
262 if ((icon
= get_icon(data
->hWnd
, data
->uID
))) ret
= modify_icon(icon
, data
);
264 case 0xdead: /* Wine extension: owner window has died */
265 cleanup_icons(data
->hWnd
);
268 if ((icon
= get_icon(data
->hWnd
, data
->uID
)))
270 icon
->version
= data
->uVersion
;
275 FIXME("unhandled tray message: %u\n", msg
);
281 static BOOL
notify_owner(struct tray_icon
*icon
, UINT msg
, int x
, int y
)
283 WPARAM wp
= icon
->id
;
286 if (icon
->version
>= NOTIFY_VERSION_4
)
288 wp
= MAKEWPARAM(x
, y
);
289 lp
= MAKELPARAM(msg
, icon
->id
);
292 TRACE("posting msg 0x%04x to hwnd %p id 0x%x\n", msg
, icon
->owner
, icon
->id
);
293 if (!PostMessageW(icon
->owner
, icon
->callback_message
, wp
, lp
) &&
294 (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
))
296 WARN("window %p was destroyed, removing icon 0x%x\n", icon
->owner
, icon
->id
);
303 /***********************************************************************
304 * macdrv_status_item_mouse_button
306 * Handle STATUS_ITEM_MOUSE_BUTTON events.
308 void macdrv_status_item_mouse_button(const macdrv_event
*event
)
310 struct tray_icon
*icon
;
312 TRACE("item %p button %d down %d count %d pos %d,%d\n", event
->status_item_mouse_button
.item
,
313 event
->status_item_mouse_button
.button
, event
->status_item_mouse_button
.down
,
314 event
->status_item_mouse_button
.count
, event
->status_item_mouse_button
.x
,
315 event
->status_item_mouse_button
.y
);
317 LIST_FOR_EACH_ENTRY(icon
, &icon_list
, struct tray_icon
, entry
)
319 if (icon
->status_item
== event
->status_item_mouse_button
.item
)
323 switch (event
->status_item_mouse_button
.button
)
325 case 0: msg
= WM_LBUTTONDOWN
; break;
326 case 1: msg
= WM_RBUTTONDOWN
; break;
327 case 2: msg
= WM_MBUTTONDOWN
; break;
329 TRACE("ignoring button beyond the third\n");
333 if (!event
->status_item_mouse_button
.down
)
334 msg
+= WM_LBUTTONUP
- WM_LBUTTONDOWN
;
335 else if (event
->status_item_mouse_button
.count
% 2 == 0)
336 msg
+= WM_LBUTTONDBLCLK
- WM_LBUTTONDOWN
;
338 if (!SendMessageW(icon
->owner
, WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS
, 0, 0) &&
339 GetLastError() == ERROR_INVALID_WINDOW_HANDLE
)
341 WARN("window %p was destroyed, removing icon 0x%x\n", icon
->owner
, icon
->id
);
346 if (!notify_owner(icon
, msg
, event
->status_item_mouse_button
.x
, event
->status_item_mouse_button
.y
))
351 if (msg
== WM_LBUTTONUP
)
352 notify_owner(icon
, NIN_SELECT
, event
->status_item_mouse_button
.x
, event
->status_item_mouse_button
.y
);
353 else if (msg
== WM_RBUTTONUP
)
354 notify_owner(icon
, WM_CONTEXTMENU
, event
->status_item_mouse_button
.x
, event
->status_item_mouse_button
.y
);
363 /***********************************************************************
364 * macdrv_status_item_mouse_move
366 * Handle STATUS_ITEM_MOUSE_MOVE events.
368 void macdrv_status_item_mouse_move(const macdrv_event
*event
)
370 struct tray_icon
*icon
;
372 TRACE("item %p pos %d,%d\n", event
->status_item_mouse_move
.item
,
373 event
->status_item_mouse_move
.x
, event
->status_item_mouse_move
.y
);
375 LIST_FOR_EACH_ENTRY(icon
, &icon_list
, struct tray_icon
, entry
)
377 if (icon
->status_item
== event
->status_item_mouse_move
.item
)
379 notify_owner(icon
, WM_MOUSEMOVE
, event
->status_item_mouse_move
.x
, event
->status_item_mouse_move
.y
);