user32/tests: Fix a spelling error in a scroll comment.
[wine.git] / dlls / winemac.drv / systray.c
blob16d2e6c0fa8fcda9867a55eca545f82f1a2d853a
1 /*
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
24 #include "config.h"
26 #include "macdrv.h"
28 #include "windef.h"
29 #include "winuser.h"
30 #include "shellapi.h"
32 #include "wine/list.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(systray);
38 /* an individual systray icon */
39 struct tray_icon
41 struct list entry;
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;
51 static struct list icon_list = LIST_INIT(icon_list);
54 static BOOL delete_icon(struct tray_icon *icon);
57 /***********************************************************************
58 * cleanup_icons
60 * Delete all systray icons owned by a given window.
62 static void cleanup_icons(HWND hwnd)
64 struct tray_icon *icon, *next;
66 LIST_FOR_EACH_ENTRY_SAFE(icon, next, &icon_list, struct tray_icon, entry)
67 if (icon->owner == hwnd) delete_icon(icon);
71 /***********************************************************************
72 * get_icon
74 * Retrieves an icon record by owner window and ID.
76 static struct tray_icon *get_icon(HWND owner, UINT id)
78 struct tray_icon *this;
80 LIST_FOR_EACH_ENTRY(this, &icon_list, struct tray_icon, entry)
81 if ((this->id == id) && (this->owner == owner)) return this;
82 return NULL;
86 /***********************************************************************
87 * modify_icon
89 * Modifies an existing tray icon and updates its status item as needed.
91 static BOOL modify_icon(struct tray_icon *icon, NOTIFYICONDATAW *nid)
93 BOOL update_image = FALSE, update_tooltip = FALSE;
95 TRACE("hwnd %p id 0x%x flags %x\n", nid->hWnd, nid->uID, nid->uFlags);
97 if (nid->uFlags & NIF_STATE)
99 DWORD changed = (icon->state ^ nid->dwState) & nid->dwStateMask;
100 icon->state = (icon->state & ~nid->dwStateMask) | (nid->dwState & nid->dwStateMask);
101 if (changed & NIS_HIDDEN)
103 if (icon->state & NIS_HIDDEN)
105 if (icon->status_item)
107 TRACE("destroying status item %p\n", icon->status_item);
108 macdrv_destroy_status_item(icon->status_item);
109 icon->status_item = NULL;
112 else
114 if (!icon->status_item)
116 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
118 icon->status_item = macdrv_create_status_item(thread_data->queue);
119 if (icon->status_item)
121 TRACE("created status item %p\n", icon->status_item);
123 if (icon->image)
124 update_image = TRUE;
125 if (lstrlenW(icon->tiptext))
126 update_tooltip = TRUE;
128 else
129 WARN("failed to create status item\n");
135 if (nid->uFlags & NIF_ICON)
137 if (icon->image) DestroyIcon(icon->image);
138 icon->image = CopyIcon(nid->hIcon);
139 if (icon->status_item)
140 update_image = TRUE;
143 if (nid->uFlags & NIF_MESSAGE)
145 icon->callback_message = nid->uCallbackMessage;
147 if (nid->uFlags & NIF_TIP)
149 lstrcpynW(icon->tiptext, nid->szTip, sizeof(icon->tiptext)/sizeof(WCHAR));
150 if (icon->status_item)
151 update_tooltip = TRUE;
154 if (update_image)
156 CGImageRef cgimage = NULL;
157 if (icon->image)
158 cgimage = create_cgimage_from_icon(icon->image, 0, 0);
159 macdrv_set_status_item_image(icon->status_item, cgimage);
160 CGImageRelease(cgimage);
163 if (update_tooltip)
165 CFStringRef s;
167 TRACE("setting tooltip text for status item %p to %s\n", icon->status_item,
168 debugstr_w(icon->tiptext));
169 s = CFStringCreateWithCharacters(NULL, (UniChar*)icon->tiptext,
170 lstrlenW(icon->tiptext));
171 macdrv_set_status_item_tooltip(icon->status_item, s);
172 CFRelease(s);
175 return TRUE;
179 /***********************************************************************
180 * add_icon
182 * Creates a new tray icon structure and adds it to the list.
184 static BOOL add_icon(NOTIFYICONDATAW *nid)
186 NOTIFYICONDATAW new_nid;
187 struct tray_icon *icon;
189 TRACE("hwnd %p id 0x%x\n", nid->hWnd, nid->uID);
191 if ((icon = get_icon(nid->hWnd, nid->uID)))
193 WARN("duplicate tray icon add, buggy app?\n");
194 return FALSE;
197 if (!(icon = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*icon))))
199 ERR("out of memory\n");
200 return FALSE;
203 icon->id = nid->uID;
204 icon->owner = nid->hWnd;
205 icon->state = NIS_HIDDEN;
207 list_add_tail(&icon_list, &icon->entry);
209 if (!(nid->uFlags & NIF_STATE) || !(nid->dwStateMask & NIS_HIDDEN))
211 new_nid = *nid;
212 new_nid.uFlags |= NIF_STATE;
213 new_nid.dwState &= ~NIS_HIDDEN;
214 new_nid.dwStateMask |= NIS_HIDDEN;
215 nid = &new_nid;
217 return modify_icon(icon, nid);
221 /***********************************************************************
222 * delete_icon
224 * Destroy tray icon status item and delete structure.
226 static BOOL delete_icon(struct tray_icon *icon)
228 TRACE("hwnd %p id 0x%x\n", icon->owner, icon->id);
230 if (icon->status_item)
232 TRACE("destroying status item %p\n", icon->status_item);
233 macdrv_destroy_status_item(icon->status_item);
235 list_remove(&icon->entry);
236 DestroyIcon(icon->image);
237 HeapFree(GetProcessHeap(), 0, icon);
238 return TRUE;
242 /***********************************************************************
243 * wine_notify_icon (MACDRV.@)
245 * Driver-side implementation of Shell_NotifyIcon.
247 int CDECL wine_notify_icon(DWORD msg, NOTIFYICONDATAW *data)
249 BOOL ret = FALSE;
250 struct tray_icon *icon;
252 switch (msg)
254 case NIM_ADD:
255 ret = add_icon(data);
256 break;
257 case NIM_DELETE:
258 if ((icon = get_icon(data->hWnd, data->uID))) ret = delete_icon(icon);
259 break;
260 case NIM_MODIFY:
261 if ((icon = get_icon(data->hWnd, data->uID))) ret = modify_icon(icon, data);
262 break;
263 case 0xdead: /* Wine extension: owner window has died */
264 cleanup_icons(data->hWnd);
265 break;
266 default:
267 FIXME("unhandled tray message: %u\n", msg);
268 break;
270 return ret;
274 /***********************************************************************
275 * macdrv_status_item_mouse_button
277 * Handle STATUS_ITEM_MOUSE_BUTTON events.
279 void macdrv_status_item_mouse_button(const macdrv_event *event)
281 struct tray_icon *icon;
283 TRACE("item %p button %d down %d count %d\n", event->status_item_mouse_button.item,
284 event->status_item_mouse_button.button, event->status_item_mouse_button.down,
285 event->status_item_mouse_button.count);
287 LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry)
289 if (icon->status_item == event->status_item_mouse_button.item)
291 UINT msg;
293 switch (event->status_item_mouse_button.button)
295 case 0: msg = WM_LBUTTONDOWN; break;
296 case 1: msg = WM_RBUTTONDOWN; break;
297 case 2: msg = WM_MBUTTONDOWN; break;
298 default:
299 TRACE("ignoring button beyond the third\n");
300 return;
303 if (!event->status_item_mouse_button.down)
304 msg += WM_LBUTTONUP - WM_LBUTTONDOWN;
305 else if (event->status_item_mouse_button.count % 2 == 0)
306 msg += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
308 if (!SendMessageW(icon->owner, WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS, 0, 0) &&
309 GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
311 WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
312 delete_icon(icon);
313 return;
316 TRACE("posting msg 0x%04x to hwnd %p id 0x%x\n", msg, icon->owner, icon->id);
317 if (!PostMessageW(icon->owner, icon->callback_message, icon->id, msg) &&
318 GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
320 WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
321 delete_icon(icon);
322 return;
325 break;
331 /***********************************************************************
332 * macdrv_status_item_mouse_move
334 * Handle STATUS_ITEM_MOUSE_MOVE events.
336 void macdrv_status_item_mouse_move(const macdrv_event *event)
338 struct tray_icon *icon;
340 TRACE("item %p\n", event->status_item_mouse_move.item);
342 LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry)
344 if (icon->status_item == event->status_item_mouse_move.item)
346 if (!PostMessageW(icon->owner, icon->callback_message, icon->id, WM_MOUSEMOVE) &&
347 GetLastError() == ERROR_INVALID_WINDOW_HANDLE)
349 WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
350 delete_icon(icon);
351 return;
354 break;