wineps: Copy GetTextExtentExPoint implementation to unixlib.
[wine.git] / dlls / winemac.drv / systray.c
blob8b3452e75402a4adc1c76b91621815daead7a620
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 #if 0
25 #pragma makedep unix
26 #endif
28 #include "config.h"
30 #include "macdrv.h"
32 #include "windef.h"
33 #include "winuser.h"
34 #include "shellapi.h"
36 #include "wine/list.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(systray);
42 /* an individual systray icon */
43 struct tray_icon
45 struct list entry;
46 HWND owner; /* the HWND passed in to the Shell_NotifyIcon call */
47 UINT id; /* the unique id given by the app */
48 UINT callback_message;
49 HICON image; /* the image to render */
50 WCHAR tiptext[128]; /* tooltip text */
51 DWORD state; /* state flags */
52 macdrv_status_item status_item;
53 UINT version;
56 static struct list icon_list = LIST_INIT(icon_list);
59 static BOOL delete_icon(struct tray_icon *icon);
62 /***********************************************************************
63 * cleanup_icons
65 * Delete all systray icons owned by a given window.
67 static void cleanup_icons(HWND hwnd)
69 struct tray_icon *icon, *next;
71 LIST_FOR_EACH_ENTRY_SAFE(icon, next, &icon_list, struct tray_icon, entry)
72 if (icon->owner == hwnd) delete_icon(icon);
76 /***********************************************************************
77 * get_icon
79 * Retrieves an icon record by owner window and ID.
81 static struct tray_icon *get_icon(HWND owner, UINT id)
83 struct tray_icon *this;
85 LIST_FOR_EACH_ENTRY(this, &icon_list, struct tray_icon, entry)
86 if ((this->id == id) && (this->owner == owner)) return this;
87 return NULL;
91 /***********************************************************************
92 * modify_icon
94 * Modifies an existing tray icon and updates its status item as needed.
96 static BOOL modify_icon(struct tray_icon *icon, NOTIFYICONDATAW *nid)
98 BOOL update_image = FALSE, update_tooltip = FALSE;
100 TRACE("hwnd %p id 0x%x flags %x\n", nid->hWnd, nid->uID, nid->uFlags);
102 if (nid->uFlags & NIF_STATE)
104 DWORD changed = (icon->state ^ nid->dwState) & nid->dwStateMask;
105 icon->state = (icon->state & ~nid->dwStateMask) | (nid->dwState & nid->dwStateMask);
106 if (changed & NIS_HIDDEN)
108 if (icon->state & NIS_HIDDEN)
110 if (icon->status_item)
112 TRACE("destroying status item %p\n", icon->status_item);
113 macdrv_destroy_status_item(icon->status_item);
114 icon->status_item = NULL;
117 else
119 if (!icon->status_item)
121 struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
123 icon->status_item = macdrv_create_status_item(thread_data->queue);
124 if (icon->status_item)
126 TRACE("created status item %p\n", icon->status_item);
128 if (icon->image)
129 update_image = TRUE;
130 if (*icon->tiptext)
131 update_tooltip = TRUE;
133 else
134 WARN("failed to create status item\n");
140 if (nid->uFlags & NIF_ICON)
142 if (icon->image) NtUserDestroyCursor(icon->image, 0);
143 icon->image = CopyImage(nid->hIcon, IMAGE_ICON, 0, 0, 0);
144 if (icon->status_item)
145 update_image = TRUE;
148 if (nid->uFlags & NIF_MESSAGE)
150 icon->callback_message = nid->uCallbackMessage;
152 if (nid->uFlags & NIF_TIP)
154 lstrcpynW(icon->tiptext, nid->szTip, ARRAY_SIZE(icon->tiptext));
155 if (icon->status_item)
156 update_tooltip = TRUE;
159 if (update_image)
161 CGImageRef cgimage = NULL;
162 if (icon->image)
163 cgimage = create_cgimage_from_icon(icon->image, 0, 0);
164 macdrv_set_status_item_image(icon->status_item, cgimage);
165 CGImageRelease(cgimage);
168 if (update_tooltip)
170 CFStringRef s;
172 TRACE("setting tooltip text for status item %p to %s\n", icon->status_item,
173 debugstr_w(icon->tiptext));
174 s = CFStringCreateWithCharacters(NULL, (UniChar*)icon->tiptext,
175 lstrlenW(icon->tiptext));
176 macdrv_set_status_item_tooltip(icon->status_item, s);
177 CFRelease(s);
180 return TRUE;
184 /***********************************************************************
185 * add_icon
187 * Creates a new tray icon structure and adds it to the list.
189 static BOOL add_icon(NOTIFYICONDATAW *nid)
191 NOTIFYICONDATAW new_nid;
192 struct tray_icon *icon;
194 TRACE("hwnd %p id 0x%x\n", nid->hWnd, nid->uID);
196 if ((icon = get_icon(nid->hWnd, nid->uID)))
198 WARN("duplicate tray icon add, buggy app?\n");
199 return FALSE;
202 if (!(icon = calloc(1, sizeof(*icon))))
204 ERR("out of memory\n");
205 return FALSE;
208 icon->id = nid->uID;
209 icon->owner = nid->hWnd;
210 icon->state = NIS_HIDDEN;
212 list_add_tail(&icon_list, &icon->entry);
214 if (!(nid->uFlags & NIF_STATE) || !(nid->dwStateMask & NIS_HIDDEN))
216 new_nid = *nid;
217 new_nid.uFlags |= NIF_STATE;
218 new_nid.dwState &= ~NIS_HIDDEN;
219 new_nid.dwStateMask |= NIS_HIDDEN;
220 nid = &new_nid;
222 return modify_icon(icon, nid);
226 /***********************************************************************
227 * delete_icon
229 * Destroy tray icon status item and delete structure.
231 static BOOL delete_icon(struct tray_icon *icon)
233 TRACE("hwnd %p id 0x%x\n", icon->owner, icon->id);
235 if (icon->status_item)
237 TRACE("destroying status item %p\n", icon->status_item);
238 macdrv_destroy_status_item(icon->status_item);
240 list_remove(&icon->entry);
241 NtUserDestroyCursor(icon->image, 0);
242 free(icon);
243 return TRUE;
247 /***********************************************************************
248 * wine_notify_icon (MACDRV.@)
250 * Driver-side implementation of Shell_NotifyIcon.
252 NTSTATUS macdrv_notify_icon(void *arg)
254 struct notify_icon_params *params = arg;
255 NOTIFYICONDATAW *data = params->data;
256 BOOL ret = FALSE;
257 struct tray_icon *icon;
259 switch (params->msg)
261 case NIM_ADD:
262 ret = add_icon(data);
263 break;
264 case NIM_DELETE:
265 if ((icon = get_icon(data->hWnd, data->uID))) ret = delete_icon(icon);
266 break;
267 case NIM_MODIFY:
268 if ((icon = get_icon(data->hWnd, data->uID))) ret = modify_icon(icon, data);
269 break;
270 case 0xdead: /* Wine extension: owner window has died */
271 cleanup_icons(data->hWnd);
272 break;
273 case NIM_SETVERSION:
274 if ((icon = get_icon(data->hWnd, data->uID)))
276 icon->version = data->uVersion;
277 ret = TRUE;
279 break;
280 default:
281 FIXME("unhandled tray message: %u\n", params->msg);
282 break;
284 return ret;
287 static BOOL notify_owner(struct tray_icon *icon, UINT msg, int x, int y)
289 WPARAM wp = icon->id;
290 LPARAM lp = msg;
292 if (icon->version >= NOTIFYICON_VERSION_4)
294 wp = MAKEWPARAM(x, y);
295 lp = MAKELPARAM(msg, icon->id);
298 TRACE("posting msg 0x%04x to hwnd %p id 0x%x\n", msg, icon->owner, icon->id);
299 if (!NtUserMessageCall(icon->owner, icon->callback_message, wp, lp,
300 0, NtUserSendNotifyMessage, FALSE) &&
301 (RtlGetLastWin32Error() == ERROR_INVALID_WINDOW_HANDLE))
303 WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
304 delete_icon(icon);
305 return FALSE;
307 return TRUE;
310 /***********************************************************************
311 * macdrv_status_item_mouse_button
313 * Handle STATUS_ITEM_MOUSE_BUTTON events.
315 void macdrv_status_item_mouse_button(const macdrv_event *event)
317 struct tray_icon *icon;
319 TRACE("item %p button %d down %d count %d pos %d,%d\n", event->status_item_mouse_button.item,
320 event->status_item_mouse_button.button, event->status_item_mouse_button.down,
321 event->status_item_mouse_button.count, event->status_item_mouse_button.x,
322 event->status_item_mouse_button.y);
324 LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry)
326 if (icon->status_item == event->status_item_mouse_button.item)
328 UINT msg;
330 switch (event->status_item_mouse_button.button)
332 case 0: msg = WM_LBUTTONDOWN; break;
333 case 1: msg = WM_RBUTTONDOWN; break;
334 case 2: msg = WM_MBUTTONDOWN; break;
335 default:
336 TRACE("ignoring button beyond the third\n");
337 return;
340 if (!event->status_item_mouse_button.down)
341 msg += WM_LBUTTONUP - WM_LBUTTONDOWN;
342 else if (event->status_item_mouse_button.count % 2 == 0)
343 msg += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
345 if (!send_message(icon->owner, WM_MACDRV_ACTIVATE_ON_FOLLOWING_FOCUS, 0, 0) &&
346 RtlGetLastWin32Error() == ERROR_INVALID_WINDOW_HANDLE)
348 WARN("window %p was destroyed, removing icon 0x%x\n", icon->owner, icon->id);
349 delete_icon(icon);
350 return;
353 if (!notify_owner(icon, msg, event->status_item_mouse_button.x, event->status_item_mouse_button.y))
354 return;
356 if (icon->version)
358 if (msg == WM_LBUTTONUP)
359 notify_owner(icon, NIN_SELECT, event->status_item_mouse_button.x, event->status_item_mouse_button.y);
360 else if (msg == WM_RBUTTONUP)
361 notify_owner(icon, WM_CONTEXTMENU, event->status_item_mouse_button.x, event->status_item_mouse_button.y);
364 break;
370 /***********************************************************************
371 * macdrv_status_item_mouse_move
373 * Handle STATUS_ITEM_MOUSE_MOVE events.
375 void macdrv_status_item_mouse_move(const macdrv_event *event)
377 struct tray_icon *icon;
379 TRACE("item %p pos %d,%d\n", event->status_item_mouse_move.item,
380 event->status_item_mouse_move.x, event->status_item_mouse_move.y);
382 LIST_FOR_EACH_ENTRY(icon, &icon_list, struct tray_icon, entry)
384 if (icon->status_item == event->status_item_mouse_move.item)
386 notify_owner(icon, WM_MOUSEMOVE, event->status_item_mouse_move.x, event->status_item_mouse_move.y);
387 break;