| /* GDK - The GIMP Drawing Kit |
| * |
| * Copyright (C) 2017,2020 Benjamin Otte <otte@gnome.org> |
| * |
| * 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 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/>. |
| */ |
| |
| #include "config.h" |
| |
| #include "gdkcontentproviderprivate.h" |
| |
| #include <gobject/gvaluecollector.h> |
| |
| #include "gdkcontentformats.h" |
| #include "gdkcontentserializer.h" |
| #include "gdkintl.h" |
| #include "gdkcontentproviderimpl.h" |
| |
| #define GDK_TYPE_CONTENT_PROVIDER_VALUE (gdk_content_provider_value_get_type ()) |
| #define GDK_CONTENT_PROVIDER_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_VALUE, GdkContentProviderValue)) |
| #define GDK_IS_CONTENT_PROVIDER_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER_VALUE)) |
| #define GDK_CONTENT_PROVIDER_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER_VALUE, GdkContentProviderValueClass)) |
| #define GDK_IS_CONTENT_PROVIDER_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER_VALUE)) |
| #define GDK_CONTENT_PROVIDER_VALUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER_VALUE, GdkContentProviderValueClass)) |
| |
| typedef struct _GdkContentProviderValue GdkContentProviderValue; |
| typedef struct _GdkContentProviderValueClass GdkContentProviderValueClass; |
| |
| struct _GdkContentProviderValue |
| { |
| GdkContentProvider parent; |
| |
| GValue value; |
| }; |
| |
| struct _GdkContentProviderValueClass |
| { |
| GdkContentProviderClass parent_class; |
| }; |
| |
| GType gdk_content_provider_value_get_type (void) G_GNUC_CONST; |
| |
| G_DEFINE_TYPE (GdkContentProviderValue, gdk_content_provider_value, GDK_TYPE_CONTENT_PROVIDER) |
| |
| static void |
| gdk_content_provider_value_finalize (GObject *object) |
| { |
| GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (object); |
| |
| g_value_unset (&content->value); |
| |
| G_OBJECT_CLASS (gdk_content_provider_value_parent_class)->finalize (object); |
| } |
| |
| static GdkContentFormats * |
| gdk_content_provider_value_ref_formats (GdkContentProvider *provider) |
| { |
| GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (provider); |
| |
| return gdk_content_formats_new_for_gtype (G_VALUE_TYPE (&content->value)); |
| } |
| |
| static gboolean |
| gdk_content_provider_value_get_value (GdkContentProvider *provider, |
| GValue *value, |
| GError **error) |
| { |
| GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (provider); |
| |
| if (G_VALUE_HOLDS (value, G_VALUE_TYPE (&content->value))) |
| { |
| g_value_copy (&content->value, value); |
| return TRUE; |
| } |
| |
| return GDK_CONTENT_PROVIDER_CLASS (gdk_content_provider_value_parent_class)->get_value (provider, value, error); |
| } |
| |
| static void |
| gdk_content_provider_value_class_init (GdkContentProviderValueClass *class) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (class); |
| GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class); |
| |
| object_class->finalize = gdk_content_provider_value_finalize; |
| |
| provider_class->ref_formats = gdk_content_provider_value_ref_formats; |
| provider_class->get_value = gdk_content_provider_value_get_value; |
| } |
| |
| static void |
| gdk_content_provider_value_init (GdkContentProviderValue *content) |
| { |
| } |
| |
| /** |
| * gdk_content_provider_new_for_value: |
| * @value: a #GValue |
| * |
| * Create a content provider that provides the given @value. |
| * |
| * Returns: a new #GdkContentProvider |
| **/ |
| GdkContentProvider * |
| gdk_content_provider_new_for_value (const GValue *value) |
| { |
| GdkContentProviderValue *content; |
| |
| g_return_val_if_fail (G_IS_VALUE (value), NULL); |
| |
| content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_VALUE, NULL); |
| g_value_init (&content->value, G_VALUE_TYPE (value)); |
| g_value_copy (value, &content->value); |
| |
| return GDK_CONTENT_PROVIDER (content); |
| } |
| |
| /** |
| * gdk_content_provider_new_typed: |
| * @type: Type of value to follow |
| * @...: value |
| * |
| * Create a content provider that provides the value of the given |
| * @type. |
| * |
| * The value is provided using G_VALUE_COLLECT(), so the same rules |
| * apply as when calling g_object_new() or g_object_set(). |
| * |
| * Returns: a new #GdkContentProvider |
| **/ |
| GdkContentProvider * |
| gdk_content_provider_new_typed (GType type, |
| ...) |
| { |
| GdkContentProviderValue *content; |
| va_list args; |
| char *error; |
| |
| content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_VALUE, NULL); |
| |
| va_start (args, type); |
| G_VALUE_COLLECT_INIT (&content->value, type, args, 0, &error); |
| if (error) |
| { |
| g_warning ("%s: %s", G_STRLOC, error); |
| g_free (error); |
| /* we purposely leak the value here, it might not be |
| * in a sane state if an error condition occoured |
| */ |
| } |
| va_end (args); |
| |
| return GDK_CONTENT_PROVIDER (content); |
| } |
| |
| #define GDK_TYPE_CONTENT_PROVIDER_UNION (gdk_content_provider_union_get_type ()) |
| #define GDK_CONTENT_PROVIDER_UNION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_UNION, GdkContentProviderUnion)) |
| #define GDK_IS_CONTENT_PROVIDER_UNION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER_UNION)) |
| #define GDK_CONTENT_PROVIDER_UNION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER_UNION, GdkContentProviderUnionClass)) |
| #define GDK_IS_CONTENT_PROVIDER_UNION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER_UNION)) |
| #define GDK_CONTENT_PROVIDER_UNION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER_UNION, GdkContentProviderUnionClass)) |
| |
| typedef struct _GdkContentProviderUnion GdkContentProviderUnion; |
| typedef struct _GdkContentProviderUnionClass GdkContentProviderUnionClass; |
| |
| struct _GdkContentProviderUnion |
| { |
| GdkContentProvider parent; |
| |
| GdkContentProvider **providers; |
| gsize n_providers; |
| }; |
| |
| struct _GdkContentProviderUnionClass |
| { |
| GdkContentProviderClass parent_class; |
| }; |
| |
| GType gdk_content_provider_union_get_type (void) G_GNUC_CONST; |
| |
| G_DEFINE_TYPE (GdkContentProviderUnion, gdk_content_provider_union, GDK_TYPE_CONTENT_PROVIDER) |
| |
| static void |
| gdk_content_provider_union_attach_clipboard (GdkContentProvider *provider, |
| GdkClipboard *clipboard) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider); |
| gsize i; |
| |
| for (i = 0; i < self->n_providers; i++) |
| gdk_content_provider_attach_clipboard (self->providers[i], clipboard); |
| } |
| |
| static void |
| gdk_content_provider_union_detach_clipboard (GdkContentProvider *provider, |
| GdkClipboard *clipboard) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider); |
| gsize i; |
| |
| for (i = 0; i < self->n_providers; i++) |
| gdk_content_provider_detach_clipboard (self->providers[i], clipboard); |
| } |
| |
| static GdkContentFormats * |
| gdk_content_provider_union_ref_formats (GdkContentProvider *provider) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider); |
| GdkContentFormatsBuilder *builder; |
| gsize i; |
| |
| builder = gdk_content_formats_builder_new (); |
| |
| for (i = 0; i < self->n_providers; i++) |
| { |
| GdkContentFormats *formats = gdk_content_provider_ref_formats (self->providers[i]); |
| gdk_content_formats_builder_add_formats (builder, formats); |
| gdk_content_formats_unref (formats); |
| } |
| |
| return gdk_content_formats_builder_free_to_formats (builder); |
| } |
| |
| static GdkContentFormats * |
| gdk_content_provider_union_ref_storable_formats (GdkContentProvider *provider) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider); |
| GdkContentFormatsBuilder *builder; |
| gsize i; |
| |
| builder = gdk_content_formats_builder_new (); |
| |
| for (i = 0; i < self->n_providers; i++) |
| { |
| GdkContentFormats *formats = gdk_content_provider_ref_storable_formats (self->providers[i]); |
| gdk_content_formats_builder_add_formats (builder, formats); |
| gdk_content_formats_unref (formats); |
| } |
| |
| return gdk_content_formats_builder_free_to_formats (builder); |
| } |
| |
| static void |
| gdk_content_provider_union_write_mime_type_done (GObject *source_object, |
| GAsyncResult *res, |
| gpointer data) |
| { |
| GTask *task = data; |
| GError *error = NULL; |
| |
| if (!gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (source_object), res, &error)) |
| { |
| g_task_return_error (task, error); |
| } |
| else |
| { |
| g_task_return_boolean (task, TRUE); |
| } |
| |
| g_object_unref (task); |
| } |
| |
| static void |
| gdk_content_provider_union_write_mime_type_async (GdkContentProvider *provider, |
| const char *mime_type, |
| GOutputStream *stream, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider); |
| GTask *task; |
| gsize i; |
| |
| task = g_task_new (self, cancellable, callback, user_data); |
| g_task_set_priority (task, io_priority); |
| g_task_set_source_tag (task, gdk_content_provider_union_write_mime_type_async); |
| |
| for (i = 0; i < self->n_providers; i++) |
| { |
| GdkContentFormats *formats = gdk_content_provider_ref_formats (self->providers[i]); |
| |
| if (gdk_content_formats_contain_mime_type (formats, mime_type)) |
| { |
| gdk_content_provider_write_mime_type_async (self->providers[i], |
| mime_type, |
| stream, |
| io_priority, |
| cancellable, |
| gdk_content_provider_union_write_mime_type_done, |
| task); |
| gdk_content_formats_unref (formats); |
| return; |
| } |
| gdk_content_formats_unref (formats); |
| } |
| |
| g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, |
| _("Cannot provide contents as ā%sā"), mime_type); |
| g_object_unref (task); |
| } |
| |
| static gboolean |
| gdk_content_provider_union_write_mime_type_finish (GdkContentProvider *provider, |
| GAsyncResult *result, |
| GError **error) |
| { |
| g_return_val_if_fail (g_task_is_valid (result, provider), FALSE); |
| g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_content_provider_union_write_mime_type_async, FALSE); |
| |
| return g_task_propagate_boolean (G_TASK (result), error); |
| } |
| |
| static gboolean |
| gdk_content_provider_union_get_value (GdkContentProvider *provider, |
| GValue *value, |
| GError **error) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider); |
| gsize i; |
| |
| for (i = 0; i < self->n_providers; i++) |
| { |
| GError *provider_error = NULL; |
| |
| if (gdk_content_provider_get_value (self->providers[i], value, &provider_error)) |
| return TRUE; |
| |
| if (!g_error_matches (provider_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) |
| { |
| g_propagate_error (error, provider_error); |
| return FALSE; |
| } |
| |
| g_clear_error (&provider_error); |
| } |
| |
| return FALSE; |
| } |
| |
| static void |
| gdk_content_provider_union_finalize (GObject *object) |
| { |
| GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (object); |
| gsize i; |
| |
| for (i = 0; i < self->n_providers; i++) |
| { |
| g_signal_handlers_disconnect_by_func (self->providers[i], gdk_content_provider_content_changed, self); |
| g_object_unref (self->providers[i]); |
| } |
| |
| g_free (self->providers); |
| |
| G_OBJECT_CLASS (gdk_content_provider_union_parent_class)->finalize (object); |
| } |
| |
| static void |
| gdk_content_provider_union_class_init (GdkContentProviderUnionClass *class) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (class); |
| GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class); |
| |
| object_class->finalize = gdk_content_provider_union_finalize; |
| |
| provider_class->attach_clipboard = gdk_content_provider_union_attach_clipboard; |
| provider_class->detach_clipboard = gdk_content_provider_union_detach_clipboard; |
| provider_class->ref_formats = gdk_content_provider_union_ref_formats; |
| provider_class->ref_storable_formats = gdk_content_provider_union_ref_storable_formats; |
| provider_class->write_mime_type_async = gdk_content_provider_union_write_mime_type_async; |
| provider_class->write_mime_type_finish = gdk_content_provider_union_write_mime_type_finish; |
| provider_class->get_value = gdk_content_provider_union_get_value; |
| } |
| |
| static void |
| gdk_content_provider_union_init (GdkContentProviderUnion *self) |
| { |
| } |
| |
| /** |
| * gdk_content_provider_new_union: |
| * @providers: (nullable) (array length=n_providers) (transfer full): |
| * The #GdkContentProviders to present the union of |
| * @n_providers: the number of providers |
| * |
| * Creates a content provider that represents all the given @providers. |
| * |
| * Whenever data needs to be written, the union provider will try the given |
| * @providers in the given order and the first one supporting a format will |
| * be chosen to provide it. |
| * |
| * This allows an easy way to support providing data in different formats. |
| * For example, an image may be provided by its file and by the iamge |
| * contents with a call such as |
| * |[<!-- language="C" --> |
| * gdk_content_provider_new_union ((GdkContentProvider *[2]) { |
| * gdk_content_provider_new_typed (G_TYPE_FILE, file), |
| * gdk_content_provider_new_typed (G_TYPE_TEXTURE, texture) |
| * }, 2); |
| * ]| |
| * |
| * |
| * Returns: a new #GdkContentProvider |
| **/ |
| GdkContentProvider * |
| gdk_content_provider_new_union (GdkContentProvider **providers, |
| gsize n_providers) |
| { |
| GdkContentProviderUnion *result; |
| gsize i; |
| |
| g_return_val_if_fail (providers != NULL || n_providers == 0, NULL); |
| |
| result = g_object_new (GDK_TYPE_CONTENT_PROVIDER_UNION, NULL); |
| |
| result->n_providers = n_providers; |
| result->providers = g_memdup (providers, sizeof (GdkContentProvider *) * n_providers); |
| |
| for (i = 0; i < n_providers; i++) |
| { |
| g_signal_connect_swapped (result->providers[i], |
| "content-changed", |
| G_CALLBACK (gdk_content_provider_content_changed), |
| result); |
| } |
| |
| return GDK_CONTENT_PROVIDER (result); |
| } |
| |
| #define GDK_TYPE_CONTENT_PROVIDER_BYTES (gdk_content_provider_bytes_get_type ()) |
| #define GDK_CONTENT_PROVIDER_BYTES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_BYTES, GdkContentProviderBytes)) |
| #define GDK_IS_CONTENT_PROVIDER_BYTES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER_BYTES)) |
| #define GDK_CONTENT_PROVIDER_BYTES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER_BYTES, GdkContentProviderBytesClass)) |
| #define GDK_IS_CONTENT_PROVIDER_BYTES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER_BYTES)) |
| #define GDK_CONTENT_PROVIDER_BYTES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER_BYTES, GdkContentProviderBytesClass)) |
| |
| typedef struct _GdkContentProviderBytes GdkContentProviderBytes; |
| typedef struct _GdkContentProviderBytesClass GdkContentProviderBytesClass; |
| |
| struct _GdkContentProviderBytes |
| { |
| GdkContentProvider parent; |
| |
| /* interned */const char *mime_type; |
| GBytes *bytes; |
| }; |
| |
| struct _GdkContentProviderBytesClass |
| { |
| GdkContentProviderClass parent_class; |
| }; |
| |
| GType gdk_content_provider_bytes_get_type (void) G_GNUC_CONST; |
| |
| G_DEFINE_TYPE (GdkContentProviderBytes, gdk_content_provider_bytes, GDK_TYPE_CONTENT_PROVIDER) |
| |
| static void |
| gdk_content_provider_bytes_finalize (GObject *object) |
| { |
| GdkContentProviderBytes *content = GDK_CONTENT_PROVIDER_BYTES (object); |
| |
| g_bytes_unref (content->bytes); |
| |
| G_OBJECT_CLASS (gdk_content_provider_bytes_parent_class)->finalize (object); |
| } |
| |
| static GdkContentFormats * |
| gdk_content_provider_bytes_ref_formats (GdkContentProvider *provider) |
| { |
| GdkContentProviderBytes *content = GDK_CONTENT_PROVIDER_BYTES (provider); |
| GdkContentFormatsBuilder *builder; |
| |
| builder = gdk_content_formats_builder_new (); |
| gdk_content_formats_builder_add_mime_type (builder, content->mime_type); |
| return gdk_content_formats_builder_free_to_formats (builder); |
| } |
| |
| static void |
| gdk_content_provider_bytes_write_mime_type_done (GObject *stream, |
| GAsyncResult *result, |
| gpointer task) |
| { |
| GError *error = NULL; |
| |
| if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), |
| result, |
| NULL, |
| &error)) |
| { |
| g_task_return_error (task, error); |
| } |
| else |
| { |
| g_task_return_boolean (task, TRUE); |
| } |
| |
| g_object_unref (task); |
| } |
| |
| static void |
| gdk_content_provider_bytes_write_mime_type_async (GdkContentProvider *provider, |
| const char *mime_type, |
| GOutputStream *stream, |
| int io_priority, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GdkContentProviderBytes *content = GDK_CONTENT_PROVIDER_BYTES (provider); |
| GTask *task; |
| |
| task = g_task_new (content, cancellable, callback, user_data); |
| g_task_set_priority (task, io_priority); |
| g_task_set_source_tag (task, gdk_content_provider_bytes_write_mime_type_async); |
| |
| if (mime_type != content->mime_type) |
| { |
| g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, |
| _("Cannot provide contents as ā%sā"), mime_type); |
| g_object_unref (task); |
| return; |
| } |
| |
| g_output_stream_write_all_async (stream, |
| g_bytes_get_data (content->bytes, NULL), |
| g_bytes_get_size (content->bytes), |
| io_priority, |
| cancellable, |
| gdk_content_provider_bytes_write_mime_type_done, |
| task); |
| } |
| |
| static gboolean |
| gdk_content_provider_bytes_write_mime_type_finish (GdkContentProvider *provider, |
| GAsyncResult *result, |
| GError **error) |
| { |
| g_return_val_if_fail (g_task_is_valid (result, provider), FALSE); |
| g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_content_provider_bytes_write_mime_type_async, FALSE); |
| |
| return g_task_propagate_boolean (G_TASK (result), error); |
| } |
| |
| static void |
| gdk_content_provider_bytes_class_init (GdkContentProviderBytesClass *class) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (class); |
| GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class); |
| |
| object_class->finalize = gdk_content_provider_bytes_finalize; |
| |
| provider_class->ref_formats = gdk_content_provider_bytes_ref_formats; |
| provider_class->write_mime_type_async = gdk_content_provider_bytes_write_mime_type_async; |
| provider_class->write_mime_type_finish = gdk_content_provider_bytes_write_mime_type_finish; |
| } |
| |
| static void |
| gdk_content_provider_bytes_init (GdkContentProviderBytes *content) |
| { |
| } |
| |
| /** |
| * gdk_content_provider_new_for_bytes: |
| * @mime_type: the mime type |
| * @bytes: (transfer none): a #GBytes with the data for @mime_type |
| * |
| * Create a content provider that provides the given @bytes as data for |
| * the given @mime_type. |
| * |
| * Returns: a new #GdkContentProvider |
| **/ |
| GdkContentProvider * |
| gdk_content_provider_new_for_bytes (const char *mime_type, |
| GBytes *bytes) |
| { |
| GdkContentProviderBytes *content; |
| |
| g_return_val_if_fail (mime_type != NULL, NULL); |
| g_return_val_if_fail (bytes != NULL, NULL); |
| |
| content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_BYTES, NULL); |
| content->mime_type = g_intern_string (mime_type); |
| content->bytes = g_bytes_ref (bytes); |
| |
| return GDK_CONTENT_PROVIDER (content); |
| } |
| |