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 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
;