mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-03 17:48:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			304 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright © 2015 Patrick Griffis
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: LGPL-2.1-or-later
 | 
						|
 *
 | 
						|
 * 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
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * 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 "gbytesicon.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 ();
 | 
						|
  g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME, g_define_type_id, "cocoa", 200));
 | 
						|
 | 
						|
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 if (G_IS_BYTES_ICON (icon))
 | 
						|
    {
 | 
						|
      NSImage *image = nil;
 | 
						|
      GBytes *bytes;
 | 
						|
      gconstpointer bytes_data;
 | 
						|
      gsize bytes_size;
 | 
						|
      NSData *data;
 | 
						|
 | 
						|
      bytes = g_bytes_icon_get_bytes (G_BYTES_ICON (icon));
 | 
						|
      bytes_data = g_bytes_get_data (bytes, &bytes_size);
 | 
						|
      data = [[NSData alloc] initWithBytes:bytes_data
 | 
						|
                                    length:bytes_size];
 | 
						|
 | 
						|
      image = [[NSImage alloc] initWithData:data];
 | 
						|
      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);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
 | 
						|
/* first deprecated in macOS 11.0 - All NSUserNotifications API should be
 | 
						|
 * replaced with UserNotifications.frameworks API
 | 
						|
 */
 | 
						|
 | 
						|
@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];
 | 
						|
}
 | 
						|
 | 
						|
G_GNUC_END_IGNORE_DEPRECATIONS
 | 
						|
 | 
						|
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;
 | 
						|
}
 |