gtk/gdk/gdkcontentproviderimpl.c
Emmanuele Bassi 2cbe094b91 Allow binding GdkContentFormatsBuilder
GdkContentFormatsBuilder is currently not introspectable, as it does not
have a GType. We can turn it into a boxed type, but we need to implement
memory management for it.

The current gdk_content_formats_builder_free() function returns a newly
constructed value, so we cannot use it as a GBoxedFreeFunc; additionally
copying a GdkContentFormatsBuilder contents would make it a bit odd, as
you could get multiple identical GdkContentFormats out of the copies.

A simple approach is to model the GdkContentFormatsBuilder API to follow
the GBytes one: use reference counting for memory management, and have
a function to release a reference, return a GdkContentFormats, and reset
the GdkContentFormatsBuilder state.

For language bindings, we can provide a get_formats() function that
returns the GdkContentFormats instance and resets the builder instance,
leaving the reference count untouched.

For C convenience we can keep gdk_content_formats_builder_free(), and
make it a wrapper around gdk_content_formats_builder_get_formats(), with
the guarantee that it'll free the builder instance regardless of its
current reference count.

https://bugzilla.gnome.org/show_bug.cgi?id=793097
https://blogs.gnome.org/otte/2018/02/03/builders/
2018-02-03 16:24:13 +01:00

283 lines
10 KiB
C

/* GDK - The GIMP Drawing Kit
*
* Copyright (C) 2017 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 "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);
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);
}
#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);
}