forked from AuroraMiddleware/gtk
ea9f2abcc4
This one can be used in cases where data needs to be served in multiple formats that may not all be covered by content serializers.
513 lines
18 KiB
C
513 lines
18 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);
|
|
}
|
|
|
|
#define GDK_TYPE_CONTENT_PROVIDER_CALLBACK (gdk_content_provider_callback_get_type ())
|
|
#define GDK_CONTENT_PROVIDER_CALLBACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_CALLBACK, GdkContentProviderCallback))
|
|
|
|
typedef struct _GdkContentProviderCallback GdkContentProviderCallback;
|
|
typedef struct _GdkContentProviderCallbackClass GdkContentProviderCallbackClass;
|
|
|
|
struct _GdkContentProviderCallback
|
|
{
|
|
GdkContentProvider parent;
|
|
|
|
GType type;
|
|
GdkContentProviderGetValueFunc func;
|
|
gpointer data;
|
|
};
|
|
|
|
struct _GdkContentProviderCallbackClass
|
|
{
|
|
GdkContentProviderClass parent_class;
|
|
};
|
|
|
|
GType gdk_content_provider_callback_get_type (void) G_GNUC_CONST;
|
|
|
|
G_DEFINE_TYPE (GdkContentProviderCallback, gdk_content_provider_callback, GDK_TYPE_CONTENT_PROVIDER)
|
|
|
|
static GdkContentFormats *
|
|
gdk_content_provider_callback_ref_formats (GdkContentProvider *provider)
|
|
{
|
|
GdkContentProviderCallback *callback = GDK_CONTENT_PROVIDER_CALLBACK (provider);
|
|
|
|
return gdk_content_formats_new_for_gtype (callback->type);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_content_provider_callback_get_value (GdkContentProvider *provider,
|
|
GValue *value,
|
|
GError **error)
|
|
{
|
|
GdkContentProviderCallback *callback = GDK_CONTENT_PROVIDER_CALLBACK (provider);
|
|
|
|
if (G_VALUE_HOLDS (value, callback->type) && callback->func != NULL)
|
|
{
|
|
callback->func (value, callback->data);
|
|
return TRUE;
|
|
}
|
|
|
|
return GDK_CONTENT_PROVIDER_CLASS (gdk_content_provider_callback_parent_class)->get_value (provider, value, error);
|
|
}
|
|
|
|
static void
|
|
gdk_content_provider_callback_class_init (GdkContentProviderCallbackClass *class)
|
|
{
|
|
GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
|
|
|
|
provider_class->ref_formats = gdk_content_provider_callback_ref_formats;
|
|
provider_class->get_value = gdk_content_provider_callback_get_value;
|
|
}
|
|
|
|
static void
|
|
gdk_content_provider_callback_init (GdkContentProviderCallback *content)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* gdk_content_provider_new_for_callback:
|
|
* @type: the type that the callback provides
|
|
* @func: callback to populate a #GValue
|
|
* @data: data that gets passed to @func
|
|
*
|
|
* Create a content provider that provides data that is provided via a callback.
|
|
*
|
|
* Returns: a new #GdkContentProvider
|
|
**/
|
|
GdkContentProvider *
|
|
gdk_content_provider_new_with_callback (GType type,
|
|
GdkContentProviderGetValueFunc func,
|
|
gpointer data)
|
|
{
|
|
GdkContentProviderCallback *content;
|
|
|
|
content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_CALLBACK, NULL);
|
|
content->type = type;
|
|
content->func = func;
|
|
content->data = data;
|
|
|
|
return GDK_CONTENT_PROVIDER (content);
|
|
}
|
|
|
|
#define GDK_TYPE_CONTENT_PROVIDER_CALLBACK2 (gdk_content_provider_callback2_get_type ())
|
|
#define GDK_CONTENT_PROVIDER_CALLBACK2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_CALLBACK2, GdkContentProviderCallback2))
|
|
|
|
typedef struct _GdkContentProviderCallback2 GdkContentProviderCallback2;
|
|
typedef struct _GdkContentProviderCallback2Class GdkContentProviderCallback2Class;
|
|
|
|
struct _GdkContentProviderCallback2
|
|
{
|
|
GdkContentProvider parent;
|
|
|
|
GdkContentFormats *formats;
|
|
GdkContentProviderGetBytesFunc func;
|
|
gpointer data;
|
|
};
|
|
|
|
struct _GdkContentProviderCallback2Class
|
|
{
|
|
GdkContentProviderClass parent_class;
|
|
};
|
|
|
|
GType gdk_content_provider_callback2_get_type (void) G_GNUC_CONST;
|
|
|
|
G_DEFINE_TYPE (GdkContentProviderCallback2, gdk_content_provider_callback2, GDK_TYPE_CONTENT_PROVIDER)
|
|
|
|
static GdkContentFormats *
|
|
gdk_content_provider_callback2_ref_formats (GdkContentProvider *provider)
|
|
{
|
|
GdkContentProviderCallback2 *callback = GDK_CONTENT_PROVIDER_CALLBACK2 (provider);
|
|
|
|
return gdk_content_formats_ref (callback->formats);
|
|
}
|
|
|
|
static void
|
|
gdk_content_provider_callback2_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_callback2_write_mime_type_async (GdkContentProvider *provider,
|
|
const char *mime_type,
|
|
GOutputStream *stream,
|
|
int io_priority,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GdkContentProviderCallback2 *content = GDK_CONTENT_PROVIDER_CALLBACK2 (provider);
|
|
GTask *task;
|
|
GBytes *bytes;
|
|
|
|
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_callback2_write_mime_type_async);
|
|
|
|
if (!gdk_content_formats_contain_mime_type (content->formats, 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;
|
|
}
|
|
|
|
bytes = content->func (mime_type, content->data);
|
|
if (!bytes)
|
|
{
|
|
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
_("Failed to get contents as “%s”"), mime_type);
|
|
g_object_unref (task);
|
|
return;
|
|
}
|
|
|
|
g_object_set_data_full (G_OBJECT (task), "bytes", bytes, (GDestroyNotify)g_bytes_unref);
|
|
|
|
g_output_stream_write_all_async (stream,
|
|
g_bytes_get_data (bytes, NULL),
|
|
g_bytes_get_size (bytes),
|
|
io_priority,
|
|
cancellable,
|
|
gdk_content_provider_callback2_write_mime_type_done,
|
|
task);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_content_provider_callback2_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_callback2_write_mime_type_async, FALSE);
|
|
|
|
return g_task_propagate_boolean (G_TASK (result), error);
|
|
}
|
|
|
|
static void
|
|
gdk_content_provider_callback2_class_init (GdkContentProviderCallback2Class *class)
|
|
{
|
|
GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
|
|
|
|
provider_class->ref_formats = gdk_content_provider_callback2_ref_formats;
|
|
provider_class->write_mime_type_async = gdk_content_provider_callback2_write_mime_type_async;
|
|
provider_class->write_mime_type_finish = gdk_content_provider_callback2_write_mime_type_finish;
|
|
}
|
|
|
|
static void
|
|
gdk_content_provider_callback2_init (GdkContentProviderCallback2 *content)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* gdk_content_provider_new_with_formats:
|
|
* @formats: formats to advertise
|
|
* @func: callback to populate a #GValue
|
|
* @data: data that gets passed to @func
|
|
*
|
|
* Create a content provider that provides data that is provided via a callback.
|
|
*
|
|
* Returns: a new #GdkContentProvider
|
|
**/
|
|
GdkContentProvider *
|
|
gdk_content_provider_new_with_formats (GdkContentFormats *formats,
|
|
GdkContentProviderGetBytesFunc func,
|
|
gpointer data)
|
|
{
|
|
GdkContentProviderCallback2 *content;
|
|
|
|
content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_CALLBACK2, NULL);
|
|
content->formats = gdk_content_formats_ref (formats);
|
|
content->func = func;
|
|
content->data = data;
|
|
|
|
return GDK_CONTENT_PROVIDER (content);
|
|
}
|