2015-03-25 20:51:29 +01:00
|
|
|
/*
|
|
|
|
* Copyright © 2015 Patrick Griffis
|
|
|
|
*
|
2022-06-01 13:44:23 +02:00
|
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
*
|
2015-03-25 20:51:29 +01:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2017-05-27 18:21:30 +02:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2015-03-25 20:51:29 +01:00
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General
|
|
|
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Authors: Patrick Griffis
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#include "gnotificationbackend.h"
|
|
|
|
#include "gapplication.h"
|
|
|
|
#include "gaction.h"
|
|
|
|
#include "gactiongroup.h"
|
|
|
|
#include "giomodule-priv.h"
|
|
|
|
#include "gnotification-private.h"
|
|
|
|
#include "gthemedicon.h"
|
|
|
|
#include "gfileicon.h"
|
|
|
|
#include "gfile.h"
|
|
|
|
|
|
|
|
#define G_TYPE_COCOA_NOTIFICATION_BACKEND (g_cocoa_notification_backend_get_type ())
|
|
|
|
#define G_COCOA_NOTIFICATION_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COCOA_NOTIFICATION_BACKEND, GCocoaNotificationBackend))
|
|
|
|
|
|
|
|
typedef struct _GCocoaNotificationBackend GCocoaNotificationBackend;
|
|
|
|
typedef GNotificationBackendClass GCocoaNotificationBackendClass;
|
|
|
|
struct _GCocoaNotificationBackend
|
|
|
|
{
|
|
|
|
GNotificationBackend parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
GType g_cocoa_notification_backend_get_type (void);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GCocoaNotificationBackend, g_cocoa_notification_backend, G_TYPE_NOTIFICATION_BACKEND,
|
|
|
|
_g_io_modules_ensure_extension_points_registered ();
|
2019-03-26 10:20:25 +01:00
|
|
|
g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME, g_define_type_id, "cocoa", 200));
|
2015-03-25 20:51:29 +01:00
|
|
|
|
|
|
|
static NSString *
|
|
|
|
nsstring_from_cstr (const char *cstr)
|
|
|
|
{
|
|
|
|
if (!cstr)
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
return [[NSString alloc] initWithUTF8String:cstr];
|
|
|
|
}
|
|
|
|
|
|
|
|
static NSImage*
|
|
|
|
nsimage_from_gicon (GIcon *icon)
|
|
|
|
{
|
|
|
|
if (G_IS_FILE_ICON (icon))
|
|
|
|
{
|
|
|
|
NSImage *image = nil;
|
|
|
|
GFile *file;
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
file = g_file_icon_get_file (G_FILE_ICON (icon));
|
|
|
|
path = g_file_get_path (file);
|
|
|
|
if (path)
|
|
|
|
{
|
|
|
|
NSString *str_path = nsstring_from_cstr (path);
|
|
|
|
image = [[NSImage alloc] initByReferencingFile:str_path];
|
|
|
|
|
|
|
|
[str_path release];
|
|
|
|
g_free (path);
|
|
|
|
}
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_warning ("This icon type is not handled by this NotificationBackend");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
activate_detailed_action (const char * action)
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
GVariant *target;
|
|
|
|
|
|
|
|
if (!g_str_has_prefix (action, "app."))
|
|
|
|
{
|
|
|
|
g_warning ("Notification action does not have \"app.\" prefix");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_action_parse_detailed_name (action, &name, &target, NULL))
|
|
|
|
{
|
|
|
|
g_action_group_activate_action (G_ACTION_GROUP (g_application_get_default()), name + 4, target);
|
|
|
|
g_free (name);
|
|
|
|
if (target)
|
|
|
|
g_variant_unref (target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-19 19:05:50 +01:00
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
|
|
/* first deprecated in macOS 11.0 - All NSUserNotifications API should be
|
|
|
|
* replaced with UserNotifications.frameworks API
|
|
|
|
*/
|
|
|
|
|
2015-03-25 20:51:29 +01:00
|
|
|
@interface GNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> @end
|
|
|
|
@implementation GNotificationCenterDelegate
|
|
|
|
|
|
|
|
-(void) userNotificationCenter:(NSUserNotificationCenter*) center
|
|
|
|
didActivateNotification:(NSUserNotification*) notification
|
|
|
|
{
|
|
|
|
if ([notification activationType] == NSUserNotificationActivationTypeContentsClicked)
|
|
|
|
{
|
|
|
|
const char *action = [[notification userInfo][@"default"] UTF8String];
|
|
|
|
if (action)
|
|
|
|
activate_detailed_action (action);
|
|
|
|
/* OSX Always activates the front window */
|
|
|
|
}
|
|
|
|
else if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked)
|
|
|
|
{
|
|
|
|
const char *action = [[notification userInfo][@"button0"] UTF8String];
|
|
|
|
if (action)
|
|
|
|
activate_detailed_action (action);
|
|
|
|
}
|
|
|
|
|
|
|
|
[center removeDeliveredNotification:notification];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
static GNotificationCenterDelegate *cocoa_notification_delegate;
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
g_cocoa_notification_backend_is_supported (void)
|
|
|
|
{
|
|
|
|
NSBundle *bundle = [NSBundle mainBundle];
|
|
|
|
|
|
|
|
/* This is always actually supported, but without a bundle it does nothing */
|
|
|
|
if (![bundle bundleIdentifier])
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
add_actions_to_notification (NSUserNotification *userNotification,
|
|
|
|
GNotification *notification)
|
|
|
|
{
|
|
|
|
guint n_buttons = g_notification_get_n_buttons (notification);
|
|
|
|
char *action = NULL, *label = NULL;
|
|
|
|
GVariant *target = NULL;
|
|
|
|
NSMutableDictionary *user_info = nil;
|
|
|
|
|
|
|
|
if (g_notification_get_default_action (notification, &action, &target))
|
|
|
|
{
|
|
|
|
char *detailed_name = g_action_print_detailed_name (action, target);
|
|
|
|
NSString *action_name = nsstring_from_cstr (detailed_name);
|
|
|
|
user_info = [[NSMutableDictionary alloc] init];
|
|
|
|
|
|
|
|
user_info[@"default"] = action_name;
|
|
|
|
|
|
|
|
[action_name release];
|
|
|
|
g_free (detailed_name);
|
|
|
|
g_clear_pointer (&action, g_free);
|
|
|
|
g_clear_pointer (&target, g_variant_unref);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n_buttons)
|
|
|
|
{
|
|
|
|
g_notification_get_button (notification, 0, &label, &action, &target);
|
|
|
|
if (label)
|
|
|
|
{
|
|
|
|
NSString *str_label = nsstring_from_cstr (label);
|
|
|
|
char *detailed_name = g_action_print_detailed_name (action, target);
|
|
|
|
NSString *action_name = nsstring_from_cstr (detailed_name);
|
|
|
|
|
|
|
|
if (!user_info)
|
|
|
|
user_info = [[NSMutableDictionary alloc] init];
|
|
|
|
|
|
|
|
user_info[@"button0"] = action_name;
|
|
|
|
userNotification.actionButtonTitle = str_label;
|
|
|
|
|
|
|
|
[str_label release];
|
|
|
|
[action_name release];
|
|
|
|
g_free (label);
|
|
|
|
g_free (action);
|
|
|
|
g_free (detailed_name);
|
|
|
|
g_clear_pointer (&target, g_variant_unref);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n_buttons > 1)
|
|
|
|
g_warning ("Only a single button is currently supported by this NotificationBackend");
|
|
|
|
}
|
|
|
|
|
|
|
|
userNotification.userInfo = user_info;
|
|
|
|
[user_info release];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_cocoa_notification_backend_send_notification (GNotificationBackend *backend,
|
|
|
|
const gchar *cstr_id,
|
|
|
|
GNotification *notification)
|
|
|
|
{
|
|
|
|
NSString *str_title = nil, *str_text = nil, *str_id = nil;
|
|
|
|
NSImage *content = nil;
|
|
|
|
const char *cstr;
|
|
|
|
GIcon *icon;
|
|
|
|
NSUserNotification *userNotification;
|
|
|
|
NSUserNotificationCenter *center;
|
|
|
|
|
|
|
|
if ((cstr = g_notification_get_title (notification)))
|
|
|
|
str_title = nsstring_from_cstr (cstr);
|
|
|
|
if ((cstr = g_notification_get_body (notification)))
|
|
|
|
str_text = nsstring_from_cstr (cstr);
|
|
|
|
if (cstr_id != NULL)
|
|
|
|
str_id = nsstring_from_cstr (cstr_id);
|
|
|
|
if ((icon = g_notification_get_icon (notification)))
|
|
|
|
content = nsimage_from_gicon (icon);
|
|
|
|
/* NOTE: There is no priority */
|
|
|
|
|
|
|
|
userNotification = [NSUserNotification new];
|
|
|
|
userNotification.title = str_title;
|
|
|
|
userNotification.informativeText = str_text;
|
|
|
|
userNotification.identifier = str_id;
|
|
|
|
userNotification.contentImage = content;
|
|
|
|
/* NOTE: Buttons only show up if your bundle has NSUserNotificationAlertStyle set to "alerts" */
|
|
|
|
add_actions_to_notification (userNotification, notification);
|
|
|
|
|
|
|
|
if (!cocoa_notification_delegate)
|
|
|
|
cocoa_notification_delegate = [[GNotificationCenterDelegate alloc] init];
|
|
|
|
|
|
|
|
center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
|
|
|
center.delegate = cocoa_notification_delegate;
|
|
|
|
[center deliverNotification:userNotification];
|
|
|
|
|
|
|
|
[str_title release];
|
|
|
|
[str_text release];
|
|
|
|
[str_id release];
|
|
|
|
[content release];
|
|
|
|
[userNotification release];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_cocoa_notification_backend_withdraw_notification (GNotificationBackend *backend,
|
|
|
|
const gchar *cstr_id)
|
|
|
|
{
|
|
|
|
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
|
|
|
NSArray *notifications = [center deliveredNotifications];
|
|
|
|
NSString *str_id = nsstring_from_cstr (cstr_id);
|
|
|
|
|
|
|
|
for (NSUserNotification *notification in notifications)
|
|
|
|
{
|
|
|
|
if ([notification.identifier compare:str_id] == NSOrderedSame)
|
|
|
|
{
|
|
|
|
[center removeDeliveredNotification:notification];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[str_id release];
|
|
|
|
}
|
|
|
|
|
2024-02-19 19:05:50 +01:00
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
|
|
|
2015-03-25 20:51:29 +01:00
|
|
|
static void
|
|
|
|
g_cocoa_notification_backend_init (GCocoaNotificationBackend *backend)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_cocoa_notification_backend_class_init (GCocoaNotificationBackendClass *klass)
|
|
|
|
{
|
|
|
|
GNotificationBackendClass *backend_class = G_NOTIFICATION_BACKEND_CLASS (klass);
|
|
|
|
|
|
|
|
backend_class->is_supported = g_cocoa_notification_backend_is_supported;
|
|
|
|
backend_class->send_notification = g_cocoa_notification_backend_send_notification;
|
|
|
|
backend_class->withdraw_notification = g_cocoa_notification_backend_withdraw_notification;
|
|
|
|
}
|