2 * Copyright © 2015 Patrick Griffis
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 * Authors: Patrick Griffis
22 #import <Cocoa/Cocoa.h>
23 #include "gnotificationbackend.h"
24 #include "gapplication.h"
26 #include "gactiongroup.h"
27 #include "giomodule-priv.h"
28 #include "gnotification-private.h"
29 #include "gthemedicon.h"
30 #include "gfileicon.h"
33 #define G_TYPE_COCOA_NOTIFICATION_BACKEND (g_cocoa_notification_backend_get_type ())
34 #define G_COCOA_NOTIFICATION_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COCOA_NOTIFICATION_BACKEND, GCocoaNotificationBackend))
36 typedef struct _GCocoaNotificationBackend GCocoaNotificationBackend;
37 typedef GNotificationBackendClass GCocoaNotificationBackendClass;
38 struct _GCocoaNotificationBackend
40 GNotificationBackend parent;
43 GType g_cocoa_notification_backend_get_type (void);
45 G_DEFINE_TYPE_WITH_CODE (GCocoaNotificationBackend, g_cocoa_notification_backend, G_TYPE_NOTIFICATION_BACKEND,
46 _g_io_modules_ensure_extension_points_registered ();
47 g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME, g_define_type_id, "cocoa", 0));
50 nsstring_from_cstr (const char *cstr)
55 return [[NSString alloc] initWithUTF8String:cstr];
59 nsimage_from_gicon (GIcon *icon)
61 if (G_IS_FILE_ICON (icon))
67 file = g_file_icon_get_file (G_FILE_ICON (icon));
68 path = g_file_get_path (file);
71 NSString *str_path = nsstring_from_cstr (path);
72 image = [[NSImage alloc] initByReferencingFile:str_path];
81 g_warning ("This icon type is not handled by this NotificationBackend");
87 activate_detailed_action (const char * action)
92 if (!g_str_has_prefix (action, "app."))
94 g_warning ("Notification action does not have \"app.\" prefix");
98 if (g_action_parse_detailed_name (action, &name, &target, NULL))
100 g_action_group_activate_action (G_ACTION_GROUP (g_application_get_default()), name + 4, target);
103 g_variant_unref (target);
107 @interface GNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> @end
108 @implementation GNotificationCenterDelegate
110 -(void) userNotificationCenter:(NSUserNotificationCenter*) center
111 didActivateNotification:(NSUserNotification*) notification
113 if ([notification activationType] == NSUserNotificationActivationTypeContentsClicked)
115 const char *action = [[notification userInfo][@"default"] UTF8String];
117 activate_detailed_action (action);
118 /* OSX Always activates the front window */
120 else if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked)
122 const char *action = [[notification userInfo][@"button0"] UTF8String];
124 activate_detailed_action (action);
127 [center removeDeliveredNotification:notification];
132 static GNotificationCenterDelegate *cocoa_notification_delegate;
135 g_cocoa_notification_backend_is_supported (void)
137 NSBundle *bundle = [NSBundle mainBundle];
139 /* This is always actually supported, but without a bundle it does nothing */
140 if (![bundle bundleIdentifier])
147 add_actions_to_notification (NSUserNotification *userNotification,
148 GNotification *notification)
150 guint n_buttons = g_notification_get_n_buttons (notification);
151 char *action = NULL, *label = NULL;
152 GVariant *target = NULL;
153 NSMutableDictionary *user_info = nil;
155 if (g_notification_get_default_action (notification, &action, &target))
157 char *detailed_name = g_action_print_detailed_name (action, target);
158 NSString *action_name = nsstring_from_cstr (detailed_name);
159 user_info = [[NSMutableDictionary alloc] init];
161 user_info[@"default"] = action_name;
163 [action_name release];
164 g_free (detailed_name);
165 g_clear_pointer (&action, g_free);
166 g_clear_pointer (&target, g_variant_unref);
171 g_notification_get_button (notification, 0, &label, &action, &target);
174 NSString *str_label = nsstring_from_cstr (label);
175 char *detailed_name = g_action_print_detailed_name (action, target);
176 NSString *action_name = nsstring_from_cstr (detailed_name);
179 user_info = [[NSMutableDictionary alloc] init];
181 user_info[@"button0"] = action_name;
182 userNotification.actionButtonTitle = str_label;
185 [action_name release];
188 g_free (detailed_name);
189 g_clear_pointer (&target, g_variant_unref);
193 g_warning ("Only a single button is currently supported by this NotificationBackend");
196 userNotification.userInfo = user_info;
201 g_cocoa_notification_backend_send_notification (GNotificationBackend *backend,
202 const gchar *cstr_id,
203 GNotification *notification)
205 NSString *str_title = nil, *str_text = nil, *str_id = nil;
206 NSImage *content = nil;
209 NSUserNotification *userNotification;
210 NSUserNotificationCenter *center;
212 if ((cstr = g_notification_get_title (notification)))
213 str_title = nsstring_from_cstr (cstr);
214 if ((cstr = g_notification_get_body (notification)))
215 str_text = nsstring_from_cstr (cstr);
217 str_id = nsstring_from_cstr (cstr_id);
218 if ((icon = g_notification_get_icon (notification)))
219 content = nsimage_from_gicon (icon);
220 /* NOTE: There is no priority */
222 userNotification = [NSUserNotification new];
223 userNotification.title = str_title;
224 userNotification.informativeText = str_text;
225 userNotification.identifier = str_id;
226 userNotification.contentImage = content;
227 /* NOTE: Buttons only show up if your bundle has NSUserNotificationAlertStyle set to "alerts" */
228 add_actions_to_notification (userNotification, notification);
230 if (!cocoa_notification_delegate)
231 cocoa_notification_delegate = [[GNotificationCenterDelegate alloc] init];
233 center = [NSUserNotificationCenter defaultUserNotificationCenter];
234 center.delegate = cocoa_notification_delegate;
235 [center deliverNotification:userNotification];
241 [userNotification release];
245 g_cocoa_notification_backend_withdraw_notification (GNotificationBackend *backend,
246 const gchar *cstr_id)
248 NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
249 NSArray *notifications = [center deliveredNotifications];
250 NSString *str_id = nsstring_from_cstr (cstr_id);
252 for (NSUserNotification *notification in notifications)
254 if ([notification.identifier compare:str_id] == NSOrderedSame)
256 [center removeDeliveredNotification:notification];
261 [notifications release];
266 g_cocoa_notification_backend_init (GCocoaNotificationBackend *backend)
271 g_cocoa_notification_backend_class_init (GCocoaNotificationBackendClass *klass)
273 GNotificationBackendClass *backend_class = G_NOTIFICATION_BACKEND_CLASS (klass);
275 backend_class->is_supported = g_cocoa_notification_backend_is_supported;
276 backend_class->send_notification = g_cocoa_notification_backend_send_notification;
277 backend_class->withdraw_notification = g_cocoa_notification_backend_withdraw_notification;