From 888e5257e074c33f316d83213b57f2f2a77d6150 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 24 Nov 2017 06:17:37 +0100 Subject: [PATCH] clipboard: Introduce GdkContentProvider GdkContentProvider is the object that represents local data in the clipboard. This patch only introduces the object and adds the clipboard properties, it does not yet provide a way for the actual implementations to access it. The only access that is implemented is the local shortcut GValue access. --- gdk/gdk.h | 2 + gdk/gdkclipboard.c | 364 ++++++++++++++++++++++++++++---- gdk/gdkclipboard.h | 14 ++ gdk/gdkcontentprovider.c | 328 ++++++++++++++++++++++++++++ gdk/gdkcontentprovider.h | 115 ++++++++++ gdk/gdkcontentproviderimpl.c | 285 +++++++++++++++++++++++++ gdk/gdkcontentproviderimpl.h | 41 ++++ gdk/gdkcontentproviderprivate.h | 33 +++ gdk/gdktypes.h | 1 + gdk/meson.build | 4 + tests/testclipboard2.c | 69 +++++- 11 files changed, 1214 insertions(+), 42 deletions(-) create mode 100644 gdk/gdkcontentprovider.c create mode 100644 gdk/gdkcontentprovider.h create mode 100644 gdk/gdkcontentproviderimpl.c create mode 100644 gdk/gdkcontentproviderimpl.h create mode 100644 gdk/gdkcontentproviderprivate.h diff --git a/gdk/gdk.h b/gdk/gdk.h index 80dc657e4e..89f13d2373 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/gdk/gdkclipboard.c b/gdk/gdkclipboard.c index bd213625ff..7ba9632991 100644 --- a/gdk/gdkclipboard.c +++ b/gdk/gdkclipboard.c @@ -22,7 +22,10 @@ #include "gdkcontentdeserializer.h" #include "gdkcontentformats.h" +#include "gdkcontentproviderimpl.h" +#include "gdkcontentproviderprivate.h" #include "gdkdisplay.h" +#include "gdkintl.h" typedef struct _GdkClipboardPrivate GdkClipboardPrivate; @@ -30,6 +33,7 @@ struct _GdkClipboardPrivate { GdkDisplay *display; GdkContentFormats *formats; + GdkContentProvider *content; guint local : 1; }; @@ -39,6 +43,7 @@ enum { PROP_DISPLAY, PROP_FORMATS, PROP_LOCAL, + PROP_CONTENT, N_PROPERTIES }; @@ -93,6 +98,10 @@ gdk_clipboard_get_property (GObject *gobject, g_value_set_boxed (value, priv->formats); break; + case PROP_CONTENT: + g_value_set_object (value, priv->content); + break; + case PROP_LOCAL: g_value_set_boolean (value, priv->local); break; @@ -115,32 +124,48 @@ gdk_clipboard_finalize (GObject *object) } static void -gdk_clipboard_real_read_async (GdkClipboard *clipboard, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +gdk_clipboard_read_local_async (GdkClipboard *clipboard, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); GTask *task; task = g_task_new (clipboard, cancellable, callback, user_data); g_task_set_priority (task, io_priority); - g_task_set_source_tag (task, gdk_clipboard_read_async); + g_task_set_source_tag (task, gdk_clipboard_read_local_async); g_task_set_task_data (task, gdk_content_formats_ref (formats), (GDestroyNotify) gdk_content_formats_unref); - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Reading local content not supported yet."); + if (priv->content == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Cannot read from empty clipboard.")); + } + else + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Reading local content via streams not supported yet.")); + } + g_object_unref (task); } static GInputStream * -gdk_clipboard_real_read_finish (GdkClipboard *clipboard, - const char **out_mime_type, - GAsyncResult *result, - GError **error) +gdk_clipboard_read_local_finish (GdkClipboard *clipboard, + const char **out_mime_type, + GAsyncResult *result, + GError **error) { - /* whoop whooop */ - return g_memory_input_stream_new (); + g_return_val_if_fail (g_task_is_valid (result, clipboard), NULL); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_read_local_async, NULL); + + if (out_mime_type) + *out_mime_type = NULL; + + return g_task_propagate_pointer (G_TASK (result), error); } static void @@ -152,8 +177,8 @@ gdk_clipboard_class_init (GdkClipboardClass *class) object_class->set_property = gdk_clipboard_set_property; object_class->finalize = gdk_clipboard_finalize; - class->read_async = gdk_clipboard_real_read_async; - class->read_finish = gdk_clipboard_real_read_finish; + class->read_async = gdk_clipboard_read_local_async; + class->read_finish = gdk_clipboard_read_local_finish; /** * GdkClipboard:display: @@ -204,6 +229,23 @@ gdk_clipboard_class_init (GdkClipboardClass *class) G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + /** + * GdkClipboard:content: + * + * The #GdkContentProvider or %NULL if the clipboard is empty or contents are + * provided otherwise. + * + * Since: 3.94 + */ + properties[PROP_CONTENT] = + g_param_spec_object ("content", + "Content", + "Provider of the clipboard's content", + GDK_TYPE_CONTENT_PROVIDER, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (class), @@ -260,6 +302,49 @@ gdk_clipboard_get_formats (GdkClipboard *clipboard) return priv->formats; } +/** + * gdk_clipboard_is_local: + * @clipboard: a #GdkClipboard + * + * Returns if the clipboard is local. A clipboard is consideredlocal if it was + * last claimed by the running application. + * + * Note that gdk_clipboard_get_content() may return %NULL even on a local + * clipboard. In this case the clipboard is empty. + * + * Returns: %TRUE if the clipboard is local + **/ +gboolean +gdk_clipboard_is_local (GdkClipboard *clipboard) +{ + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); + + g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), FALSE); + + return priv->local; +} + +/** + * gdk_clipboard_get_content: + * @clipboard: a #GdkClipboard + * + * Returns the #GdkContentProvider currently set on @clipboard. If the + * @clipboard is empty or its contents are not owned by the current process, + * %NULL will be returned. + * + * Returns: (transfer none) (nullable): The content of a clipboard or %NULL + * if the clipboard does not maintain any content. + **/ +GdkContentProvider * +gdk_clipboard_get_content (GdkClipboard *clipboard) +{ + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); + + g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL); + + return priv->content; +} + static void gdk_clipboard_read_internal (GdkClipboard *clipboard, GdkContentFormats *formats, @@ -268,12 +353,26 @@ gdk_clipboard_read_internal (GdkClipboard *clipboard, GAsyncReadyCallback callback, gpointer user_data) { - return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_async (clipboard, - formats, - io_priority, - cancellable, - callback, - user_data); + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); + + if (priv->local) + { + return gdk_clipboard_read_local_async (clipboard, + formats, + io_priority, + cancellable, + callback, + user_data); + } + else + { + return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_async (clipboard, + formats, + io_priority, + cancellable, + callback, + user_data); + } } /** @@ -337,7 +436,16 @@ gdk_clipboard_read_finish (GdkClipboard *clipboard, g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_finish (clipboard, out_mime_type, result, error); + /* don't check priv->local here because it might have changed while the + * read was ongoing */ + if (g_async_result_is_tagged (result, gdk_clipboard_read_local_async)) + { + return gdk_clipboard_read_local_finish (clipboard, out_mime_type, result, error); + } + else + { + return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_finish (clipboard, out_mime_type, result, error); + } } static void @@ -402,16 +510,12 @@ gdk_clipboard_read_value_internal (GdkClipboard *clipboard, GAsyncReadyCallback callback, gpointer user_data) { + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); GdkContentFormatsBuilder *builder; GdkContentFormats *formats; GValue *value; GTask *task; - - builder = gdk_content_formats_builder_new (); - gdk_content_formats_builder_add_gtype (builder, type); - formats = gdk_content_formats_builder_free (builder); - formats = gdk_content_formats_union_deserialize_mime_types (formats); - + task = g_task_new (clipboard, cancellable, callback, user_data); g_task_set_priority (task, io_priority); g_task_set_source_tag (task, source_tag); @@ -419,6 +523,42 @@ gdk_clipboard_read_value_internal (GdkClipboard *clipboard, g_value_init (value, type); g_task_set_task_data (task, value, free_value); + if (priv->local) + { + GError *error = NULL; + + if (priv->content == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Cannot read from empty clipboard.")); + g_object_unref (task); + return; + } + + if (gdk_content_provider_get_value (priv->content, value, &error)) + { + g_task_return_pointer (task, value, NULL); + g_object_unref (task); + return; + } + else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + else + { + /* fall through to regular stream transfer */ + g_clear_error (&error); + } + } + + builder = gdk_content_formats_builder_new (); + gdk_content_formats_builder_add_gtype (builder, type); + formats = gdk_content_formats_builder_free (builder); + formats = gdk_content_formats_union_deserialize_mime_types (formats); + gdk_clipboard_read_internal (clipboard, formats, io_priority, @@ -629,25 +769,177 @@ gdk_clipboard_new (GdkDisplay *display) NULL); } -void -gdk_clipboard_claim_remote (GdkClipboard *clipboard, - GdkContentFormats *formats) +static void +gdk_clipboard_content_changed_cb (GdkContentProvider *provider, + GdkClipboard *clipboard); + +static void +gdk_clipboard_claim (GdkClipboard *clipboard, + GdkContentFormats *formats, + gboolean local, + GdkContentProvider *content) { GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); - g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); - g_return_if_fail (formats != NULL); + g_object_freeze_notify (G_OBJECT (clipboard)); gdk_content_formats_unref (priv->formats); gdk_content_formats_ref (formats); formats = gdk_content_formats_union_deserialize_gtypes (formats); priv->formats = formats; g_object_notify_by_pspec (G_OBJECT (clipboard), properties[PROP_FORMATS]); - if (priv->local) + if (priv->local != local) { - priv->local = FALSE; + priv->local = local; g_object_notify_by_pspec (G_OBJECT (clipboard), properties[PROP_LOCAL]); } + if (priv->content != content) + { + GdkContentProvider *old_content = priv->content; + + priv->content = g_object_ref (content); + + if (old_content) + { + g_signal_handlers_disconnect_by_func (old_content, + gdk_clipboard_content_changed_cb, + clipboard); + gdk_content_provider_detach_clipboard (old_content, clipboard); + g_object_unref (old_content); + } + if (content) + { + gdk_content_provider_attach_clipboard (content, clipboard); + g_signal_connect (content, + "content-changed", + G_CALLBACK (gdk_clipboard_content_changed_cb), + clipboard); + } + + g_object_notify_by_pspec (G_OBJECT (clipboard), properties[PROP_CONTENT]); + } + + g_object_thaw_notify (G_OBJECT (clipboard)); + g_signal_emit (clipboard, signals[CHANGED], 0); } + +static void +gdk_clipboard_content_changed_cb (GdkContentProvider *provider, + GdkClipboard *clipboard) +{ + GdkContentFormats *formats; + + formats = gdk_content_provider_ref_formats (provider); + + gdk_clipboard_claim (clipboard, formats, TRUE, provider); + + gdk_content_formats_unref (formats); +} + +void +gdk_clipboard_claim_remote (GdkClipboard *clipboard, + GdkContentFormats *formats) +{ + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + g_return_if_fail (formats != NULL); + + gdk_clipboard_claim (clipboard, formats, FALSE, NULL); +} + +/** + * gdk_clipboard_set_content: + * @clipboard: a #GdkClipboard + * @provider: (transfer none) (allow-none): the new contents of @clipboard or + * %NULL to clear the clipboard + * + * Sets a new content provider on @clipboard. The clipboard will claim the + * #GdkDisplay's resources and advertise these new contents to other + * applications. + * + * If the contents are read by either an external application or the + * @clipboard's read functions, @clipboard will select the best format to + * transfer the contents and then request that format from @provider. + **/ +void +gdk_clipboard_set_content (GdkClipboard *clipboard, + GdkContentProvider *provider) +{ + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); + GdkContentFormats *formats; + + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + g_return_if_fail (provider == NULL || GDK_IS_CONTENT_PROVIDER (provider)); + + if (provider) + { + if (priv->content == provider) + return; + + formats = gdk_content_provider_ref_formats (provider); + } + else + { + if (priv->content == NULL && priv->local) + return; + + formats = gdk_content_formats_new (NULL, 0); + } + + gdk_clipboard_claim (clipboard, formats, TRUE, provider); + + gdk_content_formats_unref (formats); +} + +/** + * gdk_clipboard_set_text: + * @clipboard: a #GdkClipboard + * @text: Text to put into the clipboard + * + * Puts the given @text into the clipboard. + **/ +void +gdk_clipboard_set_text (GdkClipboard *clipboard, + const char *text) +{ + GdkContentProvider *provider; + GValue value = G_VALUE_INIT; + + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + provider = gdk_content_provider_new_for_value (&value); + g_value_unset (&value); + + gdk_clipboard_set_content (clipboard, provider); + g_object_unref (provider); +} + +/** + * gdk_clipboard_set_pixbuf: + * @clipboard: a #GdkClipboard + * @pixbuf: a #GdkPixbuf to put into the clipboard + * + * Puts the given @pixbuf into the clipboard. + **/ +void +gdk_clipboard_set_pixbuf (GdkClipboard *clipboard, + GdkPixbuf *pixbuf) +{ + GdkContentProvider *provider; + GValue value = G_VALUE_INIT; + + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + g_return_if_fail (GDK_IS_PIXBUF (pixbuf)); + + g_value_init (&value, GDK_TYPE_PIXBUF); + g_value_set_object (&value, pixbuf); + provider = gdk_content_provider_new_for_value (&value); + g_value_unset (&value); + + gdk_clipboard_set_content (clipboard, provider); + g_object_unref (provider); +} + diff --git a/gdk/gdkclipboard.h b/gdk/gdkclipboard.h index 9fce851040..4f1ec56736 100644 --- a/gdk/gdkclipboard.h +++ b/gdk/gdkclipboard.h @@ -41,6 +41,10 @@ GDK_AVAILABLE_IN_3_94 GdkDisplay * gdk_clipboard_get_display (GdkClipboard *clipboard); GDK_AVAILABLE_IN_3_94 GdkContentFormats * gdk_clipboard_get_formats (GdkClipboard *clipboard); +GDK_AVAILABLE_IN_3_94 +gboolean gdk_clipboard_is_local (GdkClipboard *clipboard); +GDK_AVAILABLE_IN_3_94 +GdkContentProvider * gdk_clipboard_get_content (GdkClipboard *clipboard); GDK_AVAILABLE_IN_3_94 void gdk_clipboard_read_async (GdkClipboard *clipboard, @@ -84,6 +88,16 @@ char * gdk_clipboard_read_text_finish (GdkClipboard * GAsyncResult *res, GError **error); +GDK_AVAILABLE_IN_3_94 +void gdk_clipboard_set_content (GdkClipboard *clipboard, + GdkContentProvider *provider); +GDK_AVAILABLE_IN_3_94 +void gdk_clipboard_set_text (GdkClipboard *clipboard, + const char *text); +GDK_AVAILABLE_IN_3_94 +void gdk_clipboard_set_pixbuf (GdkClipboard *clipboard, + GdkPixbuf *pixbuf); + G_END_DECLS #endif /* __GDK_CLIPBOARD_H__ */ diff --git a/gdk/gdkcontentprovider.c b/gdk/gdkcontentprovider.c new file mode 100644 index 0000000000..525a4eb12f --- /dev/null +++ b/gdk/gdkcontentprovider.c @@ -0,0 +1,328 @@ +/* GDK - The GIMP Drawing Kit + * + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#include "config.h" + +#include "gdkcontentproviderprivate.h" + +#include "gdkclipboard.h" +#include "gdkcontentformats.h" +#include "gdkintl.h" + +typedef struct _GdkContentProviderPrivate GdkContentProviderPrivate; + +struct _GdkContentProviderPrivate +{ + GdkContentFormats *formats; +}; + +enum { + PROP_0, + PROP_FORMATS, + N_PROPERTIES +}; + +enum { + CONTENT_CHANGED, + N_SIGNALS +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; +static guint signals[N_SIGNALS] = { 0 }; + +G_DEFINE_TYPE_WITH_PRIVATE (GdkContentProvider, gdk_content_provider, G_TYPE_OBJECT) + +static void +gdk_content_provider_real_attach_clipboard (GdkContentProvider *provider, + GdkClipboard *clipboard) +{ +} + +static void +gdk_content_provider_real_detach_clipboard (GdkContentProvider *provider, + GdkClipboard *clipboard) +{ +} + +static GdkContentFormats * +gdk_content_provider_real_ref_formats (GdkContentProvider *provider) +{ + return gdk_content_formats_new (NULL, 0); +} + +static void +gdk_content_provider_real_write_mime_type_async (GdkContentProvider *provider, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (provider, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, gdk_content_provider_real_write_mime_type_async); + + 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_real_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_real_write_mime_type_async, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static gboolean +gdk_content_provider_real_get_value (GdkContentProvider *provider, + GValue *value, + GError **error) +{ + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Cannot provide contents as %s"), G_VALUE_TYPE_NAME (value)); + + return FALSE; +} + +static void +gdk_content_provider_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdkContentProvider *provider = GDK_CONTENT_PROVIDER (gobject); + + switch (prop_id) + { + case PROP_FORMATS: + g_value_take_boxed (value, gdk_content_provider_ref_formats (provider)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +gdk_content_provider_class_init (GdkContentProviderClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = gdk_content_provider_get_property; + + class->attach_clipboard = gdk_content_provider_real_attach_clipboard; + class->detach_clipboard = gdk_content_provider_real_detach_clipboard; + class->ref_formats = gdk_content_provider_real_ref_formats; + class->write_mime_type_async = gdk_content_provider_real_write_mime_type_async; + class->write_mime_type_finish = gdk_content_provider_real_write_mime_type_finish; + class->get_value = gdk_content_provider_real_get_value; + + /** + * GdkContentProvider:formats: + * + * The possible formats that the provider can provide its data in. + * + * Since: 3.94 + */ + properties[PROP_FORMATS] = + g_param_spec_boxed ("formats", + "Formats", + "The possible formats for data", + GDK_TYPE_CONTENT_FORMATS, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + + /** + * GdkContentProvider:content-changed: + * + * Emitted whenever the content provided by this provider has changed. + * + * Since: 3.94 + */ + signals[CONTENT_CHANGED] = + g_signal_new ("content-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdkContentProviderClass, content_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); +} + +static void +gdk_content_provider_init (GdkContentProvider *provider) +{ +} + +/** + * gdk_content_provider_ref_formats: + * @provider: a #GdkContentProvider + * + * Gets the formats that the provider can provide its current contents in. + * + * Returns: (transfer full): The formats of the provider + **/ +GdkContentFormats * +gdk_content_provider_ref_formats (GdkContentProvider *provider) +{ + g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), NULL); + + return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->ref_formats (provider); +} + +void +gdk_content_provider_content_changed (GdkContentProvider *provider) +{ + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); + + g_signal_emit (provider, signals[CONTENT_CHANGED], 0); + + g_object_notify_by_pspec (G_OBJECT (provider), properties[PROP_FORMATS]); +} + +/** + * gdk_content_provider_write_mime_type_async: + * @provider: a #GdkContentProvider + * @type: the #GType to provide the data in + * @stream: the #GOutputStream to write to + * @io_priority: the [I/O priority][io-priority] + * of the request. + * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. + * @callback: (scope async): callback to call when the request is satisfied + * @user_data: (closure): the data to pass to callback function + * + * Asynchronously writes the contents of @provider to @stream in the given + * @mime_type. When the operation is finished @callback will be called. You + * can then call gdk_content_provider_write_mime_type_finish() to get the + * result of the operation. + * + * The given mime type does not need to be listed in the formats returned by + * gdk_content_provider_ref_formats(). However, if the given #GType is not + * supported, #G_IO_ERROR_NOT_SUPPORTED will be reported. + * + * The given @stream will not be closed. + **/ +void +gdk_content_provider_write_mime_type_async (GdkContentProvider *provider, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); + g_return_if_fail (mime_type != NULL); + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + + GDK_CONTENT_PROVIDER_GET_CLASS (provider)->write_mime_type_async (provider, + g_intern_string (mime_type), + stream, + io_priority, + cancellable, + callback, + user_data); +} + +/** + * gdk_content_provider_write_mime_type_finish: + * @provider: a #GdkContentProvider + * @result: a #GAsyncResult + * @error: a #GError location to store the error occurring, or %NULL to + * ignore. + * + * Finishes an asynchronous write operation started with + * gdk_content_provider_write_mime_type_async(). + * + * Returns: %TRUE if the operation was completed successfully. Otherwise + * @error will be set to describe the failure. + **/ +gboolean +gdk_content_provider_write_mime_type_finish (GdkContentProvider *provider, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->write_mime_type_finish (provider, result, error); +} + +/** + * gdk_content_provider_get_value: + * @provider: a #GdkContentProvider + * @value: the #GValue to fill + * @error: a #GError location to store the error occurring, or %NULL to + * ignore. + * + * Gets the convtents of @provider stored in @value. + * + * The @value will have been initialized to the #GType the value should be + * provided in. This given #GType does not need to be listed in the formats + * returned by gdk_content_provider_ref_formats(). However, if the given + * #GType is not supported, this operation can fail and + * #G_IO_ERROR_NOT_SUPPORTED will be reported. + * + * Returns: %TRUE if the value was set successfully. Otherwise + * @error will be set to describe the failure. + **/ +gboolean +gdk_content_provider_get_value (GdkContentProvider *provider, + GValue *value, + GError **error) +{ + g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), FALSE); + g_return_val_if_fail (G_IS_VALUE (value), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->get_value (provider, + value, + error); +} + +void +gdk_content_provider_attach_clipboard (GdkContentProvider *provider, + GdkClipboard *clipboard) +{ + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + + return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->attach_clipboard (provider, clipboard); +} + +void +gdk_content_provider_detach_clipboard (GdkContentProvider *provider, + GdkClipboard *clipboard) +{ + g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + + return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->detach_clipboard (provider, clipboard); +} diff --git a/gdk/gdkcontentprovider.h b/gdk/gdkcontentprovider.h new file mode 100644 index 0000000000..90b9eae88b --- /dev/null +++ b/gdk/gdkcontentprovider.h @@ -0,0 +1,115 @@ +/* GDK - The GIMP Drawing Kit + * + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#ifndef __GDK_CONTENT_PROVIDER_H__ +#define __GDK_CONTENT_PROVIDER_H__ + +#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + + +G_BEGIN_DECLS + +#define GDK_TYPE_CONTENT_PROVIDER (gdk_content_provider_get_type ()) +#define GDK_CONTENT_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER, GdkContentProvider)) +#define GDK_IS_CONTENT_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER)) +#define GDK_CONTENT_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER, GdkContentProviderClass)) +#define GDK_IS_CONTENT_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER)) +#define GDK_CONTENT_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER, GdkContentProviderClass)) + +typedef struct _GdkContentProviderClass GdkContentProviderClass; + +struct _GdkContentProvider +{ + GObject parent; +}; + +struct _GdkContentProviderClass +{ + GObjectClass parent_class; + + /* signals */ + void (* content_changed) (GdkContentProvider *provider); + + /* vfuncs */ + void (* attach_clipboard) (GdkContentProvider *provider, + GdkClipboard *clipboard); + void (* detach_clipboard) (GdkContentProvider *provider, + GdkClipboard *clipboard); + + GdkContentFormats * (* ref_formats) (GdkContentProvider *provider); + void (* write_mime_type_async) (GdkContentProvider *provider, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* write_mime_type_finish) (GdkContentProvider *provider, + GAsyncResult *result, + GError **error); + gboolean (* get_value) (GdkContentProvider *provider, + GValue *value, + GError **error); + + /*< private >*/ + /* Padding for future expansion */ + void (*_gdk_reserved1) (void); + void (*_gdk_reserved2) (void); + void (*_gdk_reserved3) (void); + void (*_gdk_reserved4) (void); + void (*_gdk_reserved5) (void); + void (*_gdk_reserved6) (void); + void (*_gdk_reserved7) (void); + void (*_gdk_reserved8) (void); +}; + + +GDK_AVAILABLE_IN_3_94 +GType gdk_content_provider_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_94 +GdkContentFormats * gdk_content_provider_ref_formats (GdkContentProvider *provider); + +GDK_AVAILABLE_IN_3_94 +void gdk_content_provider_content_changed (GdkContentProvider *provider); + +GDK_AVAILABLE_IN_3_94 +void gdk_content_provider_write_mime_type_async (GdkContentProvider *provider, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDK_AVAILABLE_IN_3_94 +gboolean gdk_content_provider_write_mime_type_finish (GdkContentProvider *provider, + GAsyncResult *result, + GError **error); +GDK_AVAILABLE_IN_3_94 +gboolean gdk_content_provider_get_value (GdkContentProvider *provider, + GValue *value, + GError **error); +G_END_DECLS + +#endif /* __GDK_CONTENT_PROVIDER_H__ */ diff --git a/gdk/gdkcontentproviderimpl.c b/gdk/gdkcontentproviderimpl.c new file mode 100644 index 0000000000..77e4d8d3e0 --- /dev/null +++ b/gdk/gdkcontentproviderimpl.c @@ -0,0 +1,285 @@ +/* GDK - The GIMP Drawing Kit + * + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#include "config.h" + +#include "gdkcontentprovider.h" + +#include "gdkcontentformats.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); + GdkContentFormatsBuilder *builder; + + builder = gdk_content_formats_builder_new (); + gdk_content_formats_builder_add_gtype (builder, G_VALUE_TYPE (&content->value)); + return gdk_content_formats_builder_free (builder); +} + +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); +} + +#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 (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); +} diff --git a/gdk/gdkcontentproviderimpl.h b/gdk/gdkcontentproviderimpl.h new file mode 100644 index 0000000000..aae5c658a5 --- /dev/null +++ b/gdk/gdkcontentproviderimpl.h @@ -0,0 +1,41 @@ +/* GDK - The GIMP Drawing Kit + * + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#ifndef __GDK_CONTENT_PROVIDER_IMPL_H__ +#define __GDK_CONTENT_PROVIDER_IMPL_H__ + +#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + + +GDK_AVAILABLE_IN_3_94 +GdkContentProvider * gdk_content_provider_new_for_value (const GValue *value); +GDK_AVAILABLE_IN_3_94 +GdkContentProvider * gdk_content_provider_new_for_bytes (const char *mime_type, + GBytes *bytes); + + +G_END_DECLS + +#endif /* __GDK_CONTENT_PROVIDER_IMPL_H__ */ diff --git a/gdk/gdkcontentproviderprivate.h b/gdk/gdkcontentproviderprivate.h new file mode 100644 index 0000000000..5d4d1c94fe --- /dev/null +++ b/gdk/gdkcontentproviderprivate.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#ifndef __GDK_CONTENT_PROVIDER_PRIVATE_H__ +#define __GDK_CONTENT_PROVIDER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + + +void gdk_content_provider_attach_clipboard (GdkContentProvider *provider, + GdkClipboard *clipboard); +void gdk_content_provider_detach_clipboard (GdkContentProvider *provider, + GdkClipboard *clipboard); + +G_END_DECLS + +#endif /* __GDK_CONTENT_PROVIDER_PRIVATE_H__ */ diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h index 32dba8794c..c362634037 100644 --- a/gdk/gdktypes.h +++ b/gdk/gdktypes.h @@ -121,6 +121,7 @@ typedef const char *GdkAtom; /* Forward declarations of commonly used types */ typedef struct _GdkRGBA GdkRGBA; typedef struct _GdkContentFormats GdkContentFormats; +typedef struct _GdkContentProvider GdkContentProvider; typedef struct _GdkCursor GdkCursor; typedef struct _GdkTexture GdkTexture; typedef struct _GdkDevice GdkDevice; diff --git a/gdk/meson.build b/gdk/meson.build index 88045c38c6..968dd5d14f 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -5,6 +5,8 @@ gdk_public_sources = files([ 'gdkclipboard.c', 'gdkcontentdeserializer.c', 'gdkcontentformats.c', + 'gdkcontentprovider.c', + 'gdkcontentproviderimpl.c', 'gdkcursor.c', 'gdkdevice.c', 'gdkdevicepad.c', @@ -46,6 +48,8 @@ gdk_public_headers = files([ 'gdkclipboard.h', 'gdkcontentdeserializer.h', 'gdkcontentformats.h', + 'gdkcontentprovider.h', + 'gdkcontentproviderimpl.h', 'gdkcursor.h', 'gdkdevice.h', 'gdkdevicepad.h', diff --git a/tests/testclipboard2.c b/tests/testclipboard2.c index 23eddfe6d9..4ea833dbe2 100644 --- a/tests/testclipboard2.c +++ b/tests/testclipboard2.c @@ -142,21 +142,78 @@ get_contents_widget (GdkClipboard *clipboard) return stack; } +static void +provider_button_clicked_cb (GtkWidget *button, + GdkClipboard *clipboard) +{ + gdk_clipboard_set_content (clipboard, + g_object_get_data (G_OBJECT (button), "provider")); +} + +static void +add_provider_button (GtkWidget *box, + GdkContentProvider *provider, + GdkClipboard *clipboard, + const char *name) +{ + GtkWidget *button; + + button = gtk_button_new_with_label (name); + g_signal_connect (button, "clicked", G_CALLBACK (provider_button_clicked_cb), clipboard); + g_object_set_data_full (G_OBJECT (button), "provider", provider, g_object_unref); + + gtk_container_add (GTK_CONTAINER (box), button); +} + +static GtkWidget * +get_button_list (GdkClipboard *clipboard) +{ + GtkWidget *box; + GValue value = G_VALUE_INIT; + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + gtk_container_add (GTK_CONTAINER (box), gtk_label_new ("Set Clipboard:")); + + g_value_init (&value, GDK_TYPE_PIXBUF); + g_value_take_object (&value, gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + "utilities-terminal", + 48, 0, NULL)); + add_provider_button (box, + gdk_content_provider_new_for_value (&value), + clipboard, + "Icon"); + g_value_unset (&value); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, "Hello Clipboard ☺"); + add_provider_button (box, + gdk_content_provider_new_for_value (&value), + clipboard, + "Text"); + g_value_unset (&value); + + return box; +} + static GtkWidget * get_clipboard_widget (GdkClipboard *clipboard, const char *name) { - GtkWidget *box, *stack, *switcher; + GtkWidget *vbox, *hbox, *stack, *switcher; - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add (GTK_CONTAINER (box), gtk_label_new (name)); + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (hbox), vbox); + gtk_container_add (GTK_CONTAINER (vbox), gtk_label_new (name)); switcher = gtk_stack_switcher_new (); - gtk_container_add (GTK_CONTAINER (box), switcher); + gtk_container_add (GTK_CONTAINER (vbox), switcher); stack = get_contents_widget (clipboard); - gtk_container_add (GTK_CONTAINER (box), stack); + gtk_container_add (GTK_CONTAINER (vbox), stack); gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack)); + gtk_container_add (GTK_CONTAINER (hbox), get_button_list (clipboard)); - return box; + return hbox; } static GtkWidget *