1/* GDK - The GIMP Drawing Kit
2 *
3 * Copyright (C) 2017,2020 Benjamin Otte <otte@gnome.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "config.h"
20
21#include "gdkcontentproviderprivate.h"
22
23#include <gobject/gvaluecollector.h>
24
25#include "gdkcontentformats.h"
26#include "gdkcontentserializer.h"
27#include "gdkintl.h"
28#include "gdkcontentproviderimpl.h"
29
30#include "gdk-private.h"
31
32#define GDK_TYPE_CONTENT_PROVIDER_VALUE (gdk_content_provider_value_get_type ())
33#define GDK_CONTENT_PROVIDER_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_VALUE, GdkContentProviderValue))
34#define GDK_IS_CONTENT_PROVIDER_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER_VALUE))
35#define GDK_CONTENT_PROVIDER_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER_VALUE, GdkContentProviderValueClass))
36#define GDK_IS_CONTENT_PROVIDER_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER_VALUE))
37#define GDK_CONTENT_PROVIDER_VALUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER_VALUE, GdkContentProviderValueClass))
38
39typedef struct _GdkContentProviderValue GdkContentProviderValue;
40typedef struct _GdkContentProviderValueClass GdkContentProviderValueClass;
41
42struct _GdkContentProviderValue
43{
44 GdkContentProvider parent;
45
46 GValue value;
47};
48
49struct _GdkContentProviderValueClass
50{
51 GdkContentProviderClass parent_class;
52};
53
54GType gdk_content_provider_value_get_type (void) G_GNUC_CONST;
55
56G_DEFINE_TYPE (GdkContentProviderValue, gdk_content_provider_value, GDK_TYPE_CONTENT_PROVIDER)
57
58static void
59gdk_content_provider_value_finalize (GObject *object)
60{
61 GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (object);
62
63 g_value_unset (value: &content->value);
64
65 G_OBJECT_CLASS (gdk_content_provider_value_parent_class)->finalize (object);
66}
67
68static GdkContentFormats *
69gdk_content_provider_value_ref_formats (GdkContentProvider *provider)
70{
71 GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (provider);
72
73 return gdk_content_formats_new_for_gtype (G_VALUE_TYPE (&content->value));
74}
75
76static gboolean
77gdk_content_provider_value_get_value (GdkContentProvider *provider,
78 GValue *value,
79 GError **error)
80{
81 GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (provider);
82
83 if (G_VALUE_HOLDS (&content->value, G_VALUE_TYPE (value)))
84 {
85 g_value_copy (src_value: &content->value, dest_value: value);
86 return TRUE;
87 }
88
89 return GDK_CONTENT_PROVIDER_CLASS (gdk_content_provider_value_parent_class)->get_value (provider, value, error);
90}
91
92static void
93gdk_content_provider_value_class_init (GdkContentProviderValueClass *class)
94{
95 GObjectClass *object_class = G_OBJECT_CLASS (class);
96 GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
97
98 object_class->finalize = gdk_content_provider_value_finalize;
99
100 provider_class->ref_formats = gdk_content_provider_value_ref_formats;
101 provider_class->get_value = gdk_content_provider_value_get_value;
102}
103
104static void
105gdk_content_provider_value_init (GdkContentProviderValue *content)
106{
107}
108
109/**
110 * gdk_content_provider_new_for_value:
111 * @value: a `GValue`
112 *
113 * Create a content provider that provides the given @value.
114 *
115 * Returns: a new `GdkContentProvider`
116 */
117GdkContentProvider *
118gdk_content_provider_new_for_value (const GValue *value)
119{
120 GdkContentProviderValue *content;
121
122 g_return_val_if_fail (G_IS_VALUE (value), NULL);
123
124 content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_VALUE, NULL);
125 g_value_init (value: &content->value, G_VALUE_TYPE (value));
126 g_value_copy (src_value: value, dest_value: &content->value);
127
128 return GDK_CONTENT_PROVIDER (content);
129}
130
131/**
132 * gdk_content_provider_new_typed:
133 * @type: Type of value to follow
134 * @...: value
135 *
136 * Create a content provider that provides the value of the given
137 * @type.
138 *
139 * The value is provided using G_VALUE_COLLECT(), so the same rules
140 * apply as when calling g_object_new() or g_object_set().
141 *
142 * Returns: a new `GdkContentProvider`
143 */
144GdkContentProvider *
145gdk_content_provider_new_typed (GType type,
146 ...)
147{
148 GdkContentProviderValue *content;
149 va_list args;
150 char *error;
151
152 content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_VALUE, NULL);
153
154 va_start (args, type);
155 G_VALUE_COLLECT_INIT (&content->value, type, args, 0, &error);
156 if (error)
157 {
158 g_warning ("%s: %s", G_STRLOC, error);
159 g_free (mem: error);
160 /* we purposely leak the value here, it might not be
161 * in a sane state if an error condition occurred
162 */
163 }
164 va_end (args);
165
166 return GDK_CONTENT_PROVIDER (content);
167}
168
169#define GDK_TYPE_CONTENT_PROVIDER_UNION (gdk_content_provider_union_get_type ())
170#define GDK_CONTENT_PROVIDER_UNION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_UNION, GdkContentProviderUnion))
171#define GDK_IS_CONTENT_PROVIDER_UNION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER_UNION))
172#define GDK_CONTENT_PROVIDER_UNION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER_UNION, GdkContentProviderUnionClass))
173#define GDK_IS_CONTENT_PROVIDER_UNION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER_UNION))
174#define GDK_CONTENT_PROVIDER_UNION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER_UNION, GdkContentProviderUnionClass))
175
176typedef struct _GdkContentProviderUnion GdkContentProviderUnion;
177typedef struct _GdkContentProviderUnionClass GdkContentProviderUnionClass;
178
179struct _GdkContentProviderUnion
180{
181 GdkContentProvider parent;
182
183 GdkContentProvider **providers;
184 gsize n_providers;
185};
186
187struct _GdkContentProviderUnionClass
188{
189 GdkContentProviderClass parent_class;
190};
191
192GType gdk_content_provider_union_get_type (void) G_GNUC_CONST;
193
194G_DEFINE_TYPE (GdkContentProviderUnion, gdk_content_provider_union, GDK_TYPE_CONTENT_PROVIDER)
195
196static void
197gdk_content_provider_union_attach_clipboard (GdkContentProvider *provider,
198 GdkClipboard *clipboard)
199{
200 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider);
201 gsize i;
202
203 for (i = 0; i < self->n_providers; i++)
204 gdk_content_provider_attach_clipboard (provider: self->providers[i], clipboard);
205}
206
207static void
208gdk_content_provider_union_detach_clipboard (GdkContentProvider *provider,
209 GdkClipboard *clipboard)
210{
211 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider);
212 gsize i;
213
214 for (i = 0; i < self->n_providers; i++)
215 gdk_content_provider_detach_clipboard (provider: self->providers[i], clipboard);
216}
217
218static GdkContentFormats *
219gdk_content_provider_union_ref_formats (GdkContentProvider *provider)
220{
221 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider);
222 GdkContentFormatsBuilder *builder;
223 gsize i;
224
225 builder = gdk_content_formats_builder_new ();
226
227 for (i = 0; i < self->n_providers; i++)
228 {
229 GdkContentFormats *formats = gdk_content_provider_ref_formats (provider: self->providers[i]);
230 gdk_content_formats_builder_add_formats (builder, formats);
231 gdk_content_formats_unref (formats);
232 }
233
234 return gdk_content_formats_builder_free_to_formats (builder);
235}
236
237static GdkContentFormats *
238gdk_content_provider_union_ref_storable_formats (GdkContentProvider *provider)
239{
240 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider);
241 GdkContentFormatsBuilder *builder;
242 gsize i;
243
244 builder = gdk_content_formats_builder_new ();
245
246 for (i = 0; i < self->n_providers; i++)
247 {
248 GdkContentFormats *formats = gdk_content_provider_ref_storable_formats (provider: self->providers[i]);
249 gdk_content_formats_builder_add_formats (builder, formats);
250 gdk_content_formats_unref (formats);
251 }
252
253 return gdk_content_formats_builder_free_to_formats (builder);
254}
255
256static void
257gdk_content_provider_union_write_mime_type_done (GObject *source_object,
258 GAsyncResult *res,
259 gpointer data)
260{
261 GTask *task = data;
262 GError *error = NULL;
263
264 if (!gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (source_object), result: res, error: &error))
265 {
266 g_task_return_error (task, error);
267 }
268 else
269 {
270 g_task_return_boolean (task, TRUE);
271 }
272
273 g_object_unref (object: task);
274}
275
276static void
277gdk_content_provider_union_write_mime_type_async (GdkContentProvider *provider,
278 const char *mime_type,
279 GOutputStream *stream,
280 int io_priority,
281 GCancellable *cancellable,
282 GAsyncReadyCallback callback,
283 gpointer user_data)
284{
285 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider);
286 GTask *task;
287 gsize i;
288
289 task = g_task_new (source_object: self, cancellable, callback, callback_data: user_data);
290 g_task_set_priority (task, priority: io_priority);
291 g_task_set_source_tag (task, gdk_content_provider_union_write_mime_type_async);
292
293 for (i = 0; i < self->n_providers; i++)
294 {
295 GdkContentFormats *formats = gdk_content_provider_ref_formats (provider: self->providers[i]);
296
297 if (gdk_content_formats_contain_mime_type (formats, mime_type))
298 {
299 gdk_content_provider_write_mime_type_async (provider: self->providers[i],
300 mime_type,
301 stream,
302 io_priority,
303 cancellable,
304 callback: gdk_content_provider_union_write_mime_type_done,
305 user_data: task);
306 gdk_content_formats_unref (formats);
307 return;
308 }
309 gdk_content_formats_unref (formats);
310 }
311
312 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
313 _("Cannot provide contents as ā€œ%sā€"), mime_type);
314 g_object_unref (object: task);
315}
316
317static gboolean
318gdk_content_provider_union_write_mime_type_finish (GdkContentProvider *provider,
319 GAsyncResult *result,
320 GError **error)
321{
322 g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
323 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_content_provider_union_write_mime_type_async, FALSE);
324
325 return g_task_propagate_boolean (G_TASK (result), error);
326}
327
328static gboolean
329gdk_content_provider_union_get_value (GdkContentProvider *provider,
330 GValue *value,
331 GError **error)
332{
333 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (provider);
334 gsize i;
335
336 for (i = 0; i < self->n_providers; i++)
337 {
338 GError *provider_error = NULL;
339
340 if (gdk_content_provider_get_value (provider: self->providers[i], value, error: &provider_error))
341 return TRUE;
342
343 if (!g_error_matches (error: provider_error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED))
344 {
345 g_propagate_error (dest: error, src: provider_error);
346 return FALSE;
347 }
348
349 g_clear_error (err: &provider_error);
350 }
351
352 return GDK_CONTENT_PROVIDER_CLASS (gdk_content_provider_union_parent_class)->get_value (provider, value, error);
353}
354
355static void
356gdk_content_provider_union_finalize (GObject *object)
357{
358 GdkContentProviderUnion *self = GDK_CONTENT_PROVIDER_UNION (object);
359 gsize i;
360
361 for (i = 0; i < self->n_providers; i++)
362 {
363 g_signal_handlers_disconnect_by_func (self->providers[i], gdk_content_provider_content_changed, self);
364 g_object_unref (object: self->providers[i]);
365 }
366
367 g_free (mem: self->providers);
368
369 G_OBJECT_CLASS (gdk_content_provider_union_parent_class)->finalize (object);
370}
371
372static void
373gdk_content_provider_union_class_init (GdkContentProviderUnionClass *class)
374{
375 GObjectClass *object_class = G_OBJECT_CLASS (class);
376 GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
377
378 object_class->finalize = gdk_content_provider_union_finalize;
379
380 provider_class->attach_clipboard = gdk_content_provider_union_attach_clipboard;
381 provider_class->detach_clipboard = gdk_content_provider_union_detach_clipboard;
382 provider_class->ref_formats = gdk_content_provider_union_ref_formats;
383 provider_class->ref_storable_formats = gdk_content_provider_union_ref_storable_formats;
384 provider_class->write_mime_type_async = gdk_content_provider_union_write_mime_type_async;
385 provider_class->write_mime_type_finish = gdk_content_provider_union_write_mime_type_finish;
386 provider_class->get_value = gdk_content_provider_union_get_value;
387}
388
389static void
390gdk_content_provider_union_init (GdkContentProviderUnion *self)
391{
392}
393
394/**
395 * gdk_content_provider_new_union:
396 * @providers: (nullable) (array length=n_providers) (transfer full):
397 * The `GdkContentProvider`s to present the union of
398 * @n_providers: the number of providers
399 *
400 * Creates a content provider that represents all the given @providers.
401 *
402 * Whenever data needs to be written, the union provider will try the given
403 * @providers in the given order and the first one supporting a format will
404 * be chosen to provide it.
405 *
406 * This allows an easy way to support providing data in different formats.
407 * For example, an image may be provided by its file and by the image
408 * contents with a call such as
409 * ```c
410 * gdk_content_provider_new_union ((GdkContentProvider *[2]) {
411 * gdk_content_provider_new_typed (G_TYPE_FILE, file),
412 * gdk_content_provider_new_typed (G_TYPE_TEXTURE, texture)
413 * }, 2);
414 * ```
415 *
416 * Returns: a new `GdkContentProvider`
417 */
418GdkContentProvider *
419gdk_content_provider_new_union (GdkContentProvider **providers,
420 gsize n_providers)
421{
422 GdkContentProviderUnion *result;
423 gsize i;
424
425 g_return_val_if_fail (providers != NULL || n_providers == 0, NULL);
426
427 result = g_object_new (GDK_TYPE_CONTENT_PROVIDER_UNION, NULL);
428
429 result->n_providers = n_providers;
430 result->providers = g_memdup2 (mem: providers, byte_size: sizeof (GdkContentProvider *) * n_providers);
431
432 for (i = 0; i < n_providers; i++)
433 {
434 g_signal_connect_swapped (result->providers[i],
435 "content-changed",
436 G_CALLBACK (gdk_content_provider_content_changed),
437 result);
438 }
439
440 return GDK_CONTENT_PROVIDER (result);
441}
442
443#define GDK_TYPE_CONTENT_PROVIDER_BYTES (gdk_content_provider_bytes_get_type ())
444#define GDK_CONTENT_PROVIDER_BYTES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_BYTES, GdkContentProviderBytes))
445#define GDK_IS_CONTENT_PROVIDER_BYTES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CONTENT_PROVIDER_BYTES))
446#define GDK_CONTENT_PROVIDER_BYTES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CONTENT_PROVIDER_BYTES, GdkContentProviderBytesClass))
447#define GDK_IS_CONTENT_PROVIDER_BYTES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CONTENT_PROVIDER_BYTES))
448#define GDK_CONTENT_PROVIDER_BYTES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CONTENT_PROVIDER_BYTES, GdkContentProviderBytesClass))
449
450typedef struct _GdkContentProviderBytes GdkContentProviderBytes;
451typedef struct _GdkContentProviderBytesClass GdkContentProviderBytesClass;
452
453struct _GdkContentProviderBytes
454{
455 GdkContentProvider parent;
456
457 /* interned */const char *mime_type;
458 GBytes *bytes;
459};
460
461struct _GdkContentProviderBytesClass
462{
463 GdkContentProviderClass parent_class;
464};
465
466GType gdk_content_provider_bytes_get_type (void) G_GNUC_CONST;
467
468G_DEFINE_TYPE (GdkContentProviderBytes, gdk_content_provider_bytes, GDK_TYPE_CONTENT_PROVIDER)
469
470static void
471gdk_content_provider_bytes_finalize (GObject *object)
472{
473 GdkContentProviderBytes *content = GDK_CONTENT_PROVIDER_BYTES (object);
474
475 g_bytes_unref (bytes: content->bytes);
476
477 G_OBJECT_CLASS (gdk_content_provider_bytes_parent_class)->finalize (object);
478}
479
480static GdkContentFormats *
481gdk_content_provider_bytes_ref_formats (GdkContentProvider *provider)
482{
483 GdkContentProviderBytes *content = GDK_CONTENT_PROVIDER_BYTES (provider);
484 GdkContentFormatsBuilder *builder;
485
486 builder = gdk_content_formats_builder_new ();
487 gdk_content_formats_builder_add_mime_type (builder, mime_type: content->mime_type);
488 return gdk_content_formats_builder_free_to_formats (builder);
489}
490
491static void
492gdk_content_provider_bytes_write_mime_type_done (GObject *stream,
493 GAsyncResult *result,
494 gpointer task)
495{
496 GError *error = NULL;
497
498 if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream),
499 result,
500 NULL,
501 error: &error))
502 {
503 g_task_return_error (task, error);
504 }
505 else
506 {
507 g_task_return_boolean (task, TRUE);
508 }
509
510 g_object_unref (object: task);
511}
512
513static void
514gdk_content_provider_bytes_write_mime_type_async (GdkContentProvider *provider,
515 const char *mime_type,
516 GOutputStream *stream,
517 int io_priority,
518 GCancellable *cancellable,
519 GAsyncReadyCallback callback,
520 gpointer user_data)
521{
522 GdkContentProviderBytes *content = GDK_CONTENT_PROVIDER_BYTES (provider);
523 GTask *task;
524
525 task = g_task_new (source_object: content, cancellable, callback, callback_data: user_data);
526 g_task_set_priority (task, priority: io_priority);
527 g_task_set_source_tag (task, gdk_content_provider_bytes_write_mime_type_async);
528
529 if (mime_type != content->mime_type)
530 {
531 g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED,
532 _("Cannot provide contents as ā€œ%sā€"), mime_type);
533 g_object_unref (object: task);
534 return;
535 }
536
537 g_output_stream_write_all_async (stream,
538 buffer: g_bytes_get_data (bytes: content->bytes, NULL),
539 count: g_bytes_get_size (bytes: content->bytes),
540 io_priority,
541 cancellable,
542 callback: gdk_content_provider_bytes_write_mime_type_done,
543 user_data: task);
544}
545
546static gboolean
547gdk_content_provider_bytes_write_mime_type_finish (GdkContentProvider *provider,
548 GAsyncResult *result,
549 GError **error)
550{
551 g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
552 g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_content_provider_bytes_write_mime_type_async, FALSE);
553
554 return g_task_propagate_boolean (G_TASK (result), error);
555}
556
557static void
558gdk_content_provider_bytes_class_init (GdkContentProviderBytesClass *class)
559{
560 GObjectClass *object_class = G_OBJECT_CLASS (class);
561 GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
562
563 object_class->finalize = gdk_content_provider_bytes_finalize;
564
565 provider_class->ref_formats = gdk_content_provider_bytes_ref_formats;
566 provider_class->write_mime_type_async = gdk_content_provider_bytes_write_mime_type_async;
567 provider_class->write_mime_type_finish = gdk_content_provider_bytes_write_mime_type_finish;
568}
569
570static void
571gdk_content_provider_bytes_init (GdkContentProviderBytes *content)
572{
573}
574
575/**
576 * gdk_content_provider_new_for_bytes:
577 * @mime_type: the mime type
578 * @bytes: (transfer none): a `GBytes` with the data for @mime_type
579 *
580 * Create a content provider that provides the given @bytes as data for
581 * the given @mime_type.
582 *
583 * Returns: a new `GdkContentProvider`
584 */
585GdkContentProvider *
586gdk_content_provider_new_for_bytes (const char *mime_type,
587 GBytes *bytes)
588{
589 GdkContentProviderBytes *content;
590
591 g_return_val_if_fail (mime_type != NULL, NULL);
592 g_return_val_if_fail (bytes != NULL, NULL);
593
594 content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_BYTES, NULL);
595 content->mime_type = g_intern_string (string: mime_type);
596 content->bytes = g_bytes_ref (bytes);
597
598 return GDK_CONTENT_PROVIDER (content);
599}
600
601

source code of gtk/gdk/gdkcontentproviderimpl.c