Port GArray and friends to gatomicrefcount
[glib.git] / gio / gcocoanotificationbackend.m
blobae4ad883363ecc967f338cb2747daf42aac56339
1 /*
2  * Copyright © 2015 Patrick Griffis
3  *
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.
8  *
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.
13  *
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/>.
16  *
17  * Authors: Patrick Griffis
18  */
20 #include "config.h"
22 #import <Cocoa/Cocoa.h>
23 #include "gnotificationbackend.h"
24 #include "gapplication.h"
25 #include "gaction.h"
26 #include "gactiongroup.h"
27 #include "giomodule-priv.h"
28 #include "gnotification-private.h"
29 #include "gthemedicon.h"
30 #include "gfileicon.h"
31 #include "gfile.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));
49 static NSString *
50 nsstring_from_cstr (const char *cstr)
52   if (!cstr)
53     return nil;
55   return [[NSString alloc] initWithUTF8String:cstr];
58 static NSImage*
59 nsimage_from_gicon (GIcon *icon)
61   if (G_IS_FILE_ICON (icon))
62     {
63       NSImage *image = nil;
64       GFile *file;
65       char *path;
67       file = g_file_icon_get_file (G_FILE_ICON (icon));
68       path = g_file_get_path (file);
69       if (path)
70         {
71           NSString *str_path = nsstring_from_cstr (path);
72           image = [[NSImage alloc] initByReferencingFile:str_path];
74           [str_path release];
75           g_free (path);
76         }
77       return image;
78     }
79   else
80     {
81       g_warning ("This icon type is not handled by this NotificationBackend");
82       return nil;
83     }
86 static void
87 activate_detailed_action (const char * action)
89   char *name;
90   GVariant *target;
92   if (!g_str_has_prefix (action, "app."))
93     {
94       g_warning ("Notification action does not have \"app.\" prefix");
95       return;
96     }
98   if (g_action_parse_detailed_name (action, &name, &target, NULL))
99     {
100       g_action_group_activate_action (G_ACTION_GROUP (g_application_get_default()), name + 4, target);
101       g_free (name);
102       if (target)
103         g_variant_unref (target);
104     }
107 @interface GNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> @end
108 @implementation GNotificationCenterDelegate
110 -(void) userNotificationCenter:(NSUserNotificationCenter*) center
111        didActivateNotification:(NSUserNotification*)       notification
113   if ([notification activationType] == NSUserNotificationActivationTypeContentsClicked)
114     {
115       const char *action = [[notification userInfo][@"default"] UTF8String];
116       if (action)
117         activate_detailed_action (action);
118       /* OSX Always activates the front window */
119     }
120   else if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked)
121     {
122       const char *action = [[notification userInfo][@"button0"] UTF8String];
123       if (action)
124         activate_detailed_action (action);
125     }
127   [center removeDeliveredNotification:notification];
130 @end
132 static GNotificationCenterDelegate *cocoa_notification_delegate;
134 static gboolean
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])
141     return FALSE;
143   return TRUE;
146 static void
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))
156     {
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);
167     }
169   if (n_buttons)
170     {
171       g_notification_get_button (notification, 0, &label, &action, &target);
172       if (label)
173         {
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);
178           if (!user_info)
179             user_info = [[NSMutableDictionary alloc] init];
181           user_info[@"button0"] = action_name;
182           userNotification.actionButtonTitle = str_label;
184           [str_label release];
185           [action_name release];
186           g_free (label);
187           g_free (action);
188           g_free (detailed_name);
189           g_clear_pointer (&target, g_variant_unref);
190         }
192       if (n_buttons > 1)
193         g_warning ("Only a single button is currently supported by this NotificationBackend");
194     }
196     userNotification.userInfo = user_info;
197     [user_info release];
200 static void
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;
207   const char *cstr;
208   GIcon *icon;
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);
216   if (cstr_id != NULL)
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];
237   [str_title release];
238   [str_text release];
239   [str_id release];
240   [content release];
241   [userNotification release];
244 static void
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)
253     {
254       if ([notification.identifier compare:str_id] == NSOrderedSame)
255         {
256           [center removeDeliveredNotification:notification];
257           break;
258         }
259     }
261   [notifications release];
262   [str_id release];
265 static void
266 g_cocoa_notification_backend_init (GCocoaNotificationBackend *backend)
270 static void
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;