Blame gio/gcocoanotificationbackend.c

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