diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index 36964391b3..7680e6d8b0 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -27,10 +27,10 @@ #import "GdkMacosView.h" #import "GdkMacosWindow.h" -#include "gdkmacosclipboard-private.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosmonitor-private.h" +#include "gdkmacospasteboard-private.h" #include "gdkmacossurface-private.h" #include "gdkmacospopupsurface-private.h" #include "gdkmacostoplevelsurface-private.h" @@ -246,7 +246,7 @@ typedef NSString *CALayerContentsGravity; [view release]; /* TODO: We might want to make this more extensible at some point */ - _gdk_macos_clipboard_register_drag_types (self); + _gdk_macos_pasteboard_register_drag_types (self); return self; } diff --git a/gdk/macos/gdkmacosclipboard-private.h b/gdk/macos/gdkmacosclipboard-private.h index ba0b52bf0a..a1fa83f7ca 100644 --- a/gdk/macos/gdkmacosclipboard-private.h +++ b/gdk/macos/gdkmacosclipboard-private.h @@ -24,6 +24,7 @@ #include "gdkclipboardprivate.h" #include "gdkmacosdisplay-private.h" +#include "gdkmacospasteboard-private.h" G_BEGIN_DECLS @@ -41,28 +42,13 @@ NSPasteboardType _gdk_macos_clipboard_to_ns_type (const char NSPasteboardType *alternate); const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type); void _gdk_macos_clipboard_register_drag_types (NSWindow *window); -GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard); -void _gdk_macos_pasteboard_read_async (GObject *object, - NSPasteboard *pasteboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object, - GAsyncResult *result, - const char **out_mime_type, - GError **error); -@interface GdkMacosClipboardDataProvider : NSObject +@interface GdkMacosClipboardDataProvider : GdkMacosPasteboardDataProvider { - GCancellable *cancellable; - GdkClipboard *clipboard; - char **mimeTypes; + GdkClipboard *clipboard; } --(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; --(NSArray *)types; +-(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; @end diff --git a/gdk/macos/gdkmacosclipboard.c b/gdk/macos/gdkmacosclipboard.c index 3a2667c0df..e647950364 100644 --- a/gdk/macos/gdkmacosclipboard.c +++ b/gdk/macos/gdkmacosclipboard.c @@ -22,6 +22,7 @@ #include #include "gdkmacosclipboard-private.h" +#include "gdkmacospasteboard-private.h" #include "gdkmacosutils-private.h" #include "gdkprivate.h" @@ -41,56 +42,8 @@ typedef struct guint done : 1; } WriteRequest; -enum { - TYPE_STRING, - TYPE_PBOARD, - TYPE_URL, - TYPE_FILE_URL, - TYPE_COLOR, - TYPE_TIFF, - TYPE_PNG, - TYPE_LAST -}; - -#define PTYPE(k) (get_pasteboard_type(TYPE_##k)) - -static NSPasteboardType pasteboard_types[TYPE_LAST]; - G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD) -static NSPasteboardType -get_pasteboard_type (int type) -{ - static gsize initialized = FALSE; - - g_assert (type >= 0); - g_assert (type < TYPE_LAST); - - if (g_once_init_enter (&initialized)) - { - pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG; - pasteboard_types[TYPE_STRING] = NSPasteboardTypeString; - pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF; - pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor; - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS - pasteboard_types[TYPE_PBOARD] = NSStringPboardType; - G_GNUC_END_IGNORE_DEPRECATIONS - -#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER - pasteboard_types[TYPE_URL] = NSPasteboardTypeURL; - pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL; -#else - pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"]; - pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; -#endif - - g_once_init_leave (&initialized, TRUE); - } - - return pasteboard_types[type]; -} - static void write_request_free (WriteRequest *wr) { @@ -100,95 +53,6 @@ write_request_free (WriteRequest *wr) g_slice_free (WriteRequest, wr); } -const char * -_gdk_macos_clipboard_from_ns_type (NSPasteboardType type) -{ - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if ([type isEqualToString:PTYPE(STRING)] || - [type isEqualToString:PTYPE(PBOARD)]) - return g_intern_string ("text/plain;charset=utf-8"); - else if ([type isEqualToString:PTYPE(URL)] || - [type isEqualToString:PTYPE(FILE_URL)]) - return g_intern_string ("text/uri-list"); - else if ([type isEqualToString:PTYPE(COLOR)]) - return g_intern_string ("application/x-color"); - else if ([type isEqualToString:PTYPE(TIFF)]) - return g_intern_string ("image/tiff"); - else if ([type isEqualToString:PTYPE(PNG)]) - return g_intern_string ("image/png"); - - G_GNUC_END_IGNORE_DEPRECATIONS; - - return NULL; -} - -NSPasteboardType -_gdk_macos_clipboard_to_ns_type (const char *mime_type, - NSPasteboardType *alternate) -{ - if (alternate) - *alternate = NULL; - - if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0) - { - return PTYPE(STRING); - } - else if (g_strcmp0 (mime_type, "text/uri-list") == 0) - { - if (alternate) - *alternate = PTYPE(URL); - return PTYPE(FILE_URL); - } - else if (g_strcmp0 (mime_type, "application/x-color") == 0) - { - return PTYPE(COLOR); - } - else if (g_strcmp0 (mime_type, "image/tiff") == 0) - { - return PTYPE(TIFF); - } - else if (g_strcmp0 (mime_type, "image/png") == 0) - { - return PTYPE(PNG); - } - - return nil; -} - -static void -populate_content_formats (GdkContentFormatsBuilder *builder, - NSPasteboardType type) -{ - const char *mime_type; - - g_return_if_fail (builder != NULL); - g_return_if_fail (type != NULL); - - mime_type = _gdk_macos_clipboard_from_ns_type (type); - - if (mime_type != NULL) - gdk_content_formats_builder_add_mime_type (builder, mime_type); -} - -static GdkContentFormats * -load_offer_formats (NSPasteboard *pasteboard) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkContentFormatsBuilder *builder; - GdkContentFormats *formats; - - builder = gdk_content_formats_builder_new (); - for (NSPasteboardType type in [pasteboard types]) - populate_content_formats (builder, type); - formats = gdk_content_formats_builder_free_to_formats (builder); - - GDK_END_MACOS_ALLOC_POOL; - - return g_steal_pointer (&formats); -} - static void _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) { @@ -199,22 +63,13 @@ _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self) change_count = [self->pasteboard changeCount]; - formats = load_offer_formats (self->pasteboard); + formats = _gdk_macos_pasteboard_load_formats (self->pasteboard); gdk_clipboard_claim_remote (GDK_CLIPBOARD (self), formats); gdk_content_formats_unref (formats); self->last_change_count = change_count; } -static GInputStream * -create_stream_from_nsdata (NSData *data) -{ - const guint8 *bytes = [data bytes]; - gsize len = [data length]; - - return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free); -} - static void _gdk_macos_clipboard_read_async (GdkClipboard *clipboard, GdkContentFormats *formats, @@ -245,35 +100,25 @@ static void _gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self, GdkContentProvider *content) { - GDK_BEGIN_MACOS_ALLOC_POOL; - GdkMacosClipboardDataProvider *dataProvider; GdkContentFormats *serializable; - NSPasteboardItem *item; const char * const *mime_types; gsize n_mime_types; - g_return_if_fail (GDK_IS_MACOS_CLIPBOARD (self)); - g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); + g_assert (GDK_IS_MACOS_CLIPBOARD (self)); + g_assert (GDK_IS_CONTENT_PROVIDER (content)); serializable = gdk_content_provider_ref_storable_formats (content); serializable = gdk_content_formats_union_serialize_mime_types (serializable); mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types); - dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:GDK_CLIPBOARD (self) - mimetypes:mime_types]; - item = [[NSPasteboardItem alloc] init]; - [item setDataProvider:dataProvider forTypes:[dataProvider types]]; - - [self->pasteboard clearContents]; - if ([self->pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) - g_warning ("Failed to write object to pasteboard"); + dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:self mimetypes:mime_types]; + _gdk_macos_pasteboard_send_content (self->pasteboard, content, dataProvider); + [dataProvider release]; self->last_change_count = [self->pasteboard changeCount]; g_clear_pointer (&serializable, gdk_content_formats_unref); - - GDK_END_MACOS_ALLOC_POOL; } static gboolean @@ -368,23 +213,18 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) @implementation GdkMacosClipboardDataProvider --(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; +-(id)initClipboard:(GdkMacosClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types; { - [super init]; + [super initPasteboard:gdkClipboard->pasteboard mimetypes:mime_types]; - self->mimeTypes = g_strdupv ((char **)mime_types); - self->clipboard = g_object_ref (gdkClipboard); + self->clipboard = g_object_ref (GDK_CLIPBOARD (gdkClipboard)); return self; } -(void)dealloc { - g_cancellable_cancel (self->cancellable); - - g_clear_pointer (&self->mimeTypes, g_strfreev); g_clear_object (&self->clipboard); - g_clear_object (&self->cancellable); [super dealloc]; } @@ -394,27 +234,6 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self) g_clear_object (&self->clipboard); } --(NSArray *)types -{ - NSMutableArray *ret = [[NSMutableArray alloc] init]; - - for (guint i = 0; self->mimeTypes[i]; i++) - { - const char *mime_type = self->mimeTypes[i]; - NSPasteboardType type; - NSPasteboardType alternate = nil; - - if ((type = _gdk_macos_clipboard_to_ns_type (mime_type, &alternate))) - { - [ret addObject:type]; - if (alternate) - [ret addObject:alternate]; - } - } - - return g_steal_pointer (&ret); -} - static void on_data_ready_cb (GObject *object, GAsyncResult *result, @@ -460,11 +279,9 @@ on_data_ready_cb (GObject *object, GDK_END_MACOS_ALLOC_POOL; } --(void) pasteboard:(NSPasteboard *)pasteboard - item:(NSPasteboardItem *)item - provideDataForType:(NSPasteboardType)type +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type { - const char *mime_type = _gdk_macos_clipboard_from_ns_type (type); + const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type); GMainContext *main_context = g_main_context_default (); WriteRequest *wr; @@ -500,170 +317,4 @@ on_data_ready_cb (GObject *object, write_request_free (wr); } -void -_gdk_macos_clipboard_register_drag_types (NSWindow *window) -{ - [window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING), - PTYPE(PBOARD), - PTYPE(URL), - PTYPE(FILE_URL), - PTYPE(COLOR), - PTYPE(TIFF), - PTYPE(PNG), - nil]]; -} - @end - -GdkContentFormats * -_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) -{ - return load_offer_formats (pasteboard); -} - -void -_gdk_macos_pasteboard_read_async (GObject *object, - NSPasteboard *pasteboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GDK_BEGIN_MACOS_ALLOC_POOL; - - GdkContentFormats *offer_formats = NULL; - const char *mime_type; - GInputStream *stream = NULL; - GTask *task = NULL; - - g_assert (G_IS_OBJECT (object)); - g_assert (pasteboard != NULL); - g_assert (formats != NULL); - - task = g_task_new (object, cancellable, callback, user_data); - g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async); - g_task_set_priority (task, io_priority); - - offer_formats = load_offer_formats (pasteboard); - mime_type = gdk_content_formats_match_mime_type (formats, offer_formats); - - if (mime_type == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "%s", - _("No compatible transfer format found")); - goto cleanup; - } - - if (strcmp (mime_type, "text/plain;charset=utf-8") == 0) - { - NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString]; - - if (nsstr != NULL) - { - const char *str = [nsstr UTF8String]; - stream = g_memory_input_stream_new_from_data (g_strdup (str), - strlen (str) + 1, - g_free); - } - } - else if (strcmp (mime_type, "text/uri-list") == 0) - { - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if ([[pasteboard types] containsObject:PTYPE(FILE_URL)]) - { - GString *str = g_string_new (NULL); - NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; - gsize n_files = [files count]; - char *data; - guint len; - - for (gsize i = 0; i < n_files; ++i) - { - NSString* uriString = [files objectAtIndex:i]; - uriString = [@"file://" stringByAppendingString:uriString]; - uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - - g_string_append_printf (str, - "%s\r\n", - [uriString cStringUsingEncoding:NSUTF8StringEncoding]); - } - - len = str->len; - data = g_string_free (str, FALSE); - stream = g_memory_input_stream_new_from_data (data, len, g_free); - } - - G_GNUC_END_IGNORE_DEPRECATIONS; - } - else if (strcmp (mime_type, "application/x-color") == 0) - { - NSColorSpace *colorspace; - NSColor *nscolor; - guint16 color[4]; - - colorspace = [NSColorSpace genericRGBColorSpace]; - nscolor = [[NSColor colorFromPasteboard:pasteboard] - colorUsingColorSpace:colorspace]; - - color[0] = 0xffff * [nscolor redComponent]; - color[1] = 0xffff * [nscolor greenComponent]; - color[2] = 0xffff * [nscolor blueComponent]; - color[3] = 0xffff * [nscolor alphaComponent]; - - stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color), - sizeof color, - g_free); - } - else if (strcmp (mime_type, "image/tiff") == 0) - { - NSData *data = [pasteboard dataForType:PTYPE(TIFF)]; - stream = create_stream_from_nsdata (data); - } - else if (strcmp (mime_type, "image/png") == 0) - { - NSData *data = [pasteboard dataForType:PTYPE(PNG)]; - stream = create_stream_from_nsdata (data); - } - - if (stream != NULL) - { - g_task_set_task_data (task, g_strdup (mime_type), g_free); - g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref); - } - else - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Failed to decode contents with mime-type of '%s'"), - mime_type); - } - -cleanup: - g_clear_object (&task); - g_clear_pointer (&offer_formats, gdk_content_formats_unref); - - GDK_END_MACOS_ALLOC_POOL; -} - -GInputStream * -_gdk_macos_pasteboard_read_finish (GObject *object, - GAsyncResult *result, - const char **out_mime_type, - GError **error) -{ - GTask *task = (GTask *)result; - - g_assert (G_IS_OBJECT (object)); - g_assert (G_IS_TASK (task)); - - if (out_mime_type != NULL) - *out_mime_type = g_strdup (g_task_get_task_data (task)); - - return g_task_propagate_pointer (task, error); -} diff --git a/gdk/macos/gdkmacospasteboard-private.h b/gdk/macos/gdkmacospasteboard-private.h new file mode 100644 index 0000000000..c135d82d58 --- /dev/null +++ b/gdk/macos/gdkmacospasteboard-private.h @@ -0,0 +1,64 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * 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 . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef __GDK_MACOS_PASTEBOARD_PRIVATE_H__ +#define __GDK_MACOS_PASTEBOARD_PRIVATE_H__ + +#include +#include + +#include "gdkclipboardprivate.h" + +G_BEGIN_DECLS + +@interface GdkMacosPasteboardDataProvider : NSObject +{ + NSPasteboard *pasteboard; + GCancellable *cancellable; + char **mimeTypes; +} + +-(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; +-(NSArray *)types; + +@end + +NSPasteboardType _gdk_macos_pasteboard_to_ns_type (const char *mime_type, + NSPasteboardType *alternate); +const char *_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type); +GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard); +void _gdk_macos_pasteboard_register_drag_types (NSWindow *window); +void _gdk_macos_pasteboard_read_async (GObject *object, + NSPasteboard *pasteboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object, + GAsyncResult *result, + const char **out_mime_type, + GError **error); +void _gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, + GdkContentProvider *content, + GdkMacosPasteboardDataProvider *dataProvider); + +G_END_DECLS + +#endif /* __GDK_MACOS_PASTEBOARD_PRIVATE_H__ */ diff --git a/gdk/macos/gdkmacospasteboard.c b/gdk/macos/gdkmacospasteboard.c new file mode 100644 index 0000000000..73e6af07cd --- /dev/null +++ b/gdk/macos/gdkmacospasteboard.c @@ -0,0 +1,416 @@ +/* + * Copyright © 2021 Red Hat, Inc. + * + * 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 . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "gdkmacospasteboard-private.h" +#include "gdkmacosutils-private.h" + +enum { + TYPE_STRING, + TYPE_PBOARD, + TYPE_URL, + TYPE_FILE_URL, + TYPE_COLOR, + TYPE_TIFF, + TYPE_PNG, + TYPE_LAST +}; + +#define PTYPE(k) (get_pasteboard_type(TYPE_##k)) + +static NSPasteboardType pasteboard_types[TYPE_LAST]; + +static NSPasteboardType +get_pasteboard_type (int type) +{ + static gsize initialized = FALSE; + + g_assert (type >= 0); + g_assert (type < TYPE_LAST); + + if (g_once_init_enter (&initialized)) + { + pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG; + pasteboard_types[TYPE_STRING] = NSPasteboardTypeString; + pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF; + pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor; + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + pasteboard_types[TYPE_PBOARD] = NSStringPboardType; + G_GNUC_END_IGNORE_DEPRECATIONS + +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER + pasteboard_types[TYPE_URL] = NSPasteboardTypeURL; + pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL; +#else + pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"]; + pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"]; +#endif + + g_once_init_leave (&initialized, TRUE); + } + + return pasteboard_types[type]; +} + +const char * +_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type) +{ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + + if ([type isEqualToString:PTYPE(STRING)] || + [type isEqualToString:PTYPE(PBOARD)]) + return g_intern_string ("text/plain;charset=utf-8"); + else if ([type isEqualToString:PTYPE(URL)] || + [type isEqualToString:PTYPE(FILE_URL)]) + return g_intern_string ("text/uri-list"); + else if ([type isEqualToString:PTYPE(COLOR)]) + return g_intern_string ("application/x-color"); + else if ([type isEqualToString:PTYPE(TIFF)]) + return g_intern_string ("image/tiff"); + else if ([type isEqualToString:PTYPE(PNG)]) + return g_intern_string ("image/png"); + + G_GNUC_END_IGNORE_DEPRECATIONS; + + return NULL; +} + +NSPasteboardType +_gdk_macos_pasteboard_to_ns_type (const char *mime_type, + NSPasteboardType *alternate) +{ + if (alternate) + *alternate = NULL; + + if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0) + { + return PTYPE(STRING); + } + else if (g_strcmp0 (mime_type, "text/uri-list") == 0) + { + if (alternate) + *alternate = PTYPE(URL); + return PTYPE(FILE_URL); + } + else if (g_strcmp0 (mime_type, "application/x-color") == 0) + { + return PTYPE(COLOR); + } + else if (g_strcmp0 (mime_type, "image/tiff") == 0) + { + return PTYPE(TIFF); + } + else if (g_strcmp0 (mime_type, "image/png") == 0) + { + return PTYPE(PNG); + } + + return nil; +} + +static void +populate_content_formats (GdkContentFormatsBuilder *builder, + NSPasteboardType type) +{ + const char *mime_type; + + g_assert (builder != NULL); + g_assert (type != NULL); + + if ((mime_type = _gdk_macos_pasteboard_from_ns_type (type))) + gdk_content_formats_builder_add_mime_type (builder, mime_type); +} + +static GdkContentFormats * +load_offer_formats (NSPasteboard *pasteboard) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + GdkContentFormatsBuilder *builder; + GdkContentFormats *formats; + + builder = gdk_content_formats_builder_new (); + for (NSPasteboardType type in [pasteboard types]) + populate_content_formats (builder, type); + formats = gdk_content_formats_builder_free_to_formats (builder); + + GDK_END_MACOS_ALLOC_POOL; + + return g_steal_pointer (&formats); +} + +GdkContentFormats * +_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard) +{ + return load_offer_formats (pasteboard); +} + +void +_gdk_macos_pasteboard_send_content (NSPasteboard *pasteboard, + GdkContentProvider *content, + GdkMacosPasteboardDataProvider *dataProvider) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + NSPasteboardItem *item; + + g_return_if_fail (pasteboard != NULL); + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content)); + + item = [[NSPasteboardItem alloc] init]; + [item setDataProvider:dataProvider forTypes:[dataProvider types]]; + + [pasteboard clearContents]; + if ([pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO) + g_warning ("Failed to write object to pasteboard"); + + GDK_END_MACOS_ALLOC_POOL; +} + +@implementation GdkMacosPasteboardDataProvider + +-(id)initPasteboard:(NSPasteboard *)pasteBoard mimetypes:(const char * const *)mime_types; +{ + [super init]; + + self->mimeTypes = g_strdupv ((char **)mime_types); + self->pasteboard = [pasteBoard retain]; + + return self; +} + +-(void)dealloc +{ + g_cancellable_cancel (self->cancellable); + + if (self->pasteboard) + { + [self->pasteboard release]; + self->pasteboard = nil; + } + + g_clear_pointer (&self->mimeTypes, g_strfreev); + g_clear_object (&self->cancellable); + + [super dealloc]; +} + +-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard +{ +} + +-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type +{ +} + +-(NSArray *)types +{ + NSMutableArray *ret = [[NSMutableArray alloc] init]; + + for (guint i = 0; self->mimeTypes[i]; i++) + { + const char *mime_type = self->mimeTypes[i]; + NSPasteboardType type; + NSPasteboardType alternate = nil; + + if ((type = _gdk_macos_pasteboard_to_ns_type (mime_type, &alternate))) + { + [ret addObject:type]; + if (alternate) + [ret addObject:alternate]; + } + } + + return g_steal_pointer (&ret); +} + +@end + +static GInputStream * +create_stream_from_nsdata (NSData *data) +{ + const guint8 *bytes = [data bytes]; + gsize len = [data length]; + + return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free); +} + +void +_gdk_macos_pasteboard_read_async (GObject *object, + NSPasteboard *pasteboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDK_BEGIN_MACOS_ALLOC_POOL; + + GdkContentFormats *offer_formats = NULL; + const char *mime_type; + GInputStream *stream = NULL; + GTask *task = NULL; + + g_assert (G_IS_OBJECT (object)); + g_assert (pasteboard != NULL); + g_assert (formats != NULL); + + task = g_task_new (object, cancellable, callback, user_data); + g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async); + g_task_set_priority (task, io_priority); + + offer_formats = load_offer_formats (pasteboard); + mime_type = gdk_content_formats_match_mime_type (formats, offer_formats); + + if (mime_type == NULL) + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "%s", + _("No compatible transfer format found")); + goto cleanup; + } + + if (strcmp (mime_type, "text/plain;charset=utf-8") == 0) + { + NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString]; + + if (nsstr != NULL) + { + const char *str = [nsstr UTF8String]; + stream = g_memory_input_stream_new_from_data (g_strdup (str), + strlen (str) + 1, + g_free); + } + } + else if (strcmp (mime_type, "text/uri-list") == 0) + { + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + + if ([[pasteboard types] containsObject:PTYPE(FILE_URL)]) + { + GString *str = g_string_new (NULL); + NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; + gsize n_files = [files count]; + char *data; + guint len; + + for (gsize i = 0; i < n_files; ++i) + { + NSString* uriString = [files objectAtIndex:i]; + uriString = [@"file://" stringByAppendingString:uriString]; + uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + g_string_append_printf (str, + "%s\r\n", + [uriString cStringUsingEncoding:NSUTF8StringEncoding]); + } + + len = str->len; + data = g_string_free (str, FALSE); + stream = g_memory_input_stream_new_from_data (data, len, g_free); + } + + G_GNUC_END_IGNORE_DEPRECATIONS; + } + else if (strcmp (mime_type, "application/x-color") == 0) + { + NSColorSpace *colorspace; + NSColor *nscolor; + guint16 color[4]; + + colorspace = [NSColorSpace genericRGBColorSpace]; + nscolor = [[NSColor colorFromPasteboard:pasteboard] + colorUsingColorSpace:colorspace]; + + color[0] = 0xffff * [nscolor redComponent]; + color[1] = 0xffff * [nscolor greenComponent]; + color[2] = 0xffff * [nscolor blueComponent]; + color[3] = 0xffff * [nscolor alphaComponent]; + + stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color), + sizeof color, + g_free); + } + else if (strcmp (mime_type, "image/tiff") == 0) + { + NSData *data = [pasteboard dataForType:PTYPE(TIFF)]; + stream = create_stream_from_nsdata (data); + } + else if (strcmp (mime_type, "image/png") == 0) + { + NSData *data = [pasteboard dataForType:PTYPE(PNG)]; + stream = create_stream_from_nsdata (data); + } + + if (stream != NULL) + { + g_task_set_task_data (task, g_strdup (mime_type), g_free); + g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref); + } + else + { + g_task_return_new_error (task, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Failed to decode contents with mime-type of '%s'"), + mime_type); + } + +cleanup: + g_clear_object (&task); + g_clear_pointer (&offer_formats, gdk_content_formats_unref); + + GDK_END_MACOS_ALLOC_POOL; +} + +GInputStream * +_gdk_macos_pasteboard_read_finish (GObject *object, + GAsyncResult *result, + const char **out_mime_type, + GError **error) +{ + GTask *task = (GTask *)result; + + g_assert (G_IS_OBJECT (object)); + g_assert (G_IS_TASK (task)); + + if (out_mime_type != NULL) + *out_mime_type = g_strdup (g_task_get_task_data (task)); + + return g_task_propagate_pointer (task, error); +} + +void +_gdk_macos_pasteboard_register_drag_types (NSWindow *window) +{ + [window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING), + PTYPE(PBOARD), + PTYPE(URL), + PTYPE(FILE_URL), + PTYPE(COLOR), + PTYPE(TIFF), + PTYPE(PNG), + nil]]; +} diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index bd7bbb5324..b3baefb898 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -19,6 +19,7 @@ gdk_macos_sources = files([ 'gdkmacoseventsource.c', 'gdkmacoskeymap.c', 'gdkmacosmonitor.c', + 'gdkmacospasteboard.c', 'gdkmacospopupsurface.c', 'gdkmacosseat.c', 'gdkmacossurface.c',