1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2017 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/**
19 * GdkContentFormats:
20 *
21 * The `GdkContentFormats` structure is used to advertise and negotiate the
22 * format of content.
23 *
24 * You will encounter `GdkContentFormats` when interacting with objects
25 * controlling operations that pass data between different widgets, window
26 * or application, like [class@Gdk.Drag], [class@Gdk.Drop],
27 * [class@Gdk.Clipboard] or [class@Gdk.ContentProvider].
28 *
29 * GDK supports content in 2 forms: `GType` and mime type.
30 * Using `GTypes` is meant only for in-process content transfers. Mime types
31 * are meant to be used for data passing both in-process and out-of-process.
32 * The details of how data is passed is described in the documentation of
33 * the actual implementations. To transform between the two forms,
34 * [class@Gdk.ContentSerializer] and [class@Gdk.ContentDeserializer] are used.
35 *
36 * A `GdkContentFormats` describes a set of possible formats content can be
37 * exchanged in. It is assumed that this set is ordered. `GTypes` are more
38 * important than mime types. Order between different `GTypes` or mime types
39 * is the order they were added in, most important first. Functions that
40 * care about order, such as [method@Gdk.ContentFormats.union], will describe
41 * in their documentation how they interpret that order, though in general the
42 * order of the first argument is considered the primary order of the result,
43 * followed by the order of further arguments.
44 *
45 * For debugging purposes, the function [method@Gdk.ContentFormats.to_string]
46 * exists. It will print a comma-separated list of formats from most important
47 * to least important.
48 *
49 * `GdkContentFormats` is an immutable struct. After creation, you cannot change
50 * the types it represents. Instead, new `GdkContentFormats` have to be created.
51 * The [struct@Gdk.ContentFormatsBuilder] structure is meant to help in this
52 * endeavor.
53 */
54
55#include "config.h"
56
57#include "gdkcontentformats.h"
58#include "gdkcontentformatsprivate.h"
59
60#include <string.h>
61
62struct _GdkContentFormats
63{
64 /*< private >*/
65 guint ref_count;
66
67 const char **mime_types; /* interned */
68 gsize n_mime_types;
69 GType *gtypes;
70 gsize n_gtypes;
71};
72
73G_DEFINE_BOXED_TYPE (GdkContentFormats, gdk_content_formats,
74 gdk_content_formats_ref,
75 gdk_content_formats_unref)
76
77
78/**
79 * gdk_intern_mime_type:
80 * @string: (transfer none): string of a potential mime type
81 *
82 * Canonicalizes the given mime type and interns the result.
83 *
84 * If @string is not a valid mime type, %NULL is returned instead.
85 * See RFC 2048 for the syntax if mime types.
86 *
87 * Returns: (nullable): An interned string for the canonicalized
88 * mime type or %NULL if the string wasn't a valid mime type
89 */
90const char *
91gdk_intern_mime_type (const char *string)
92{
93 char *tmp;
94
95 g_return_val_if_fail (string != NULL, NULL);
96
97 if (!strchr (s: string, c: '/'))
98 return NULL;
99
100 tmp = g_ascii_strdown (str: string, len: -1);
101
102 string = g_intern_string (string: tmp);
103
104 g_free (mem: tmp);
105
106 return string;
107}
108
109static GdkContentFormats *
110gdk_content_formats_new_take (GType * gtypes,
111 gsize n_gtypes,
112 const char **mime_types,
113 gsize n_mime_types)
114{
115 GdkContentFormats *result = g_slice_new0 (GdkContentFormats);
116 result->ref_count = 1;
117
118 result->gtypes = gtypes;
119 result->n_gtypes = n_gtypes;
120 result->mime_types = mime_types;
121 result->n_mime_types = n_mime_types;
122
123 return result;
124}
125
126/**
127 * gdk_content_formats_new:
128 * @mime_types: (array length=n_mime_types) (nullable): Pointer to an
129 * array of mime types
130 * @n_mime_types: number of entries in @mime_types.
131 *
132 * Creates a new `GdkContentFormats` from an array of mime types.
133 *
134 * The mime types must be valid and different from each other or the
135 * behavior of the return value is undefined. If you cannot guarantee
136 * this, use [struct@Gdk.ContentFormatsBuilder] instead.
137 *
138 * Returns: (transfer full): the new `GdkContentFormats`.
139 */
140GdkContentFormats *
141gdk_content_formats_new (const char **mime_types,
142 guint n_mime_types)
143{
144 guint i;
145 const char **mime_types_copy;
146
147 if (n_mime_types == 0)
148 return gdk_content_formats_new_take (NULL, n_gtypes: 0, NULL, n_mime_types: 0);
149
150 mime_types_copy = g_new (const char *, n_mime_types + 1);
151
152 for (i = 0; i < n_mime_types; i++)
153 mime_types_copy[i] = g_intern_string (string: mime_types[i]);
154
155 mime_types_copy[n_mime_types] = NULL;
156
157 return gdk_content_formats_new_take (NULL, n_gtypes: 0, mime_types: mime_types_copy, n_mime_types);
158}
159
160/**
161 * gdk_content_formats_new_for_gtype:
162 * @type: a `GType`
163 *
164 * Creates a new `GdkContentFormats` for a given `GType`.
165 *
166 * Returns: a new `GdkContentFormats`
167 */
168GdkContentFormats *
169gdk_content_formats_new_for_gtype (GType type)
170{
171 GType *data;
172
173 g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
174
175 data = g_new (GType, 2);
176 data[0] = type;
177 data[1] = G_TYPE_INVALID;
178
179 return gdk_content_formats_new_take (gtypes: data, n_gtypes: 1, NULL, n_mime_types: 0);
180}
181
182/**
183 * gdk_content_formats_parse:
184 * @string: the string to parse
185 *
186 * Parses the given @string into `GdkContentFormats` and
187 * returns the formats.
188 *
189 * Strings printed via [method@Gdk.ContentFormats.to_string]
190 * can be read in again successfully using this function.
191 *
192 * If @string does not describe valid content formats, %NULL
193 * is returned.
194 *
195 * Returns: (nullable): the content formats if @string is valid
196 *
197 * Since: 4.4
198 */
199GdkContentFormats *
200gdk_content_formats_parse (const char *string)
201{
202 GdkContentFormatsBuilder *builder;
203 char **split;
204 gsize i;
205
206 g_return_val_if_fail (string != NULL, NULL);
207
208 split = g_strsplit_set (string, delimiters: "\t\n\f\r ", max_tokens: -1); /* same as g_ascii_isspace() */
209 builder = gdk_content_formats_builder_new ();
210
211 /* first the GTypes */
212 for (i = 0; split[i] != NULL; i++)
213 {
214 GType type;
215
216 if (split[i][0] == 0)
217 continue;
218
219 type = g_type_from_name (name: split[i]);
220 if (type != 0)
221 gdk_content_formats_builder_add_gtype (builder, type);
222 else
223 break;
224 }
225
226 /* then the mime types */
227 for (; split[i] != NULL; i++)
228 {
229 const char *mime_type;
230
231 if (split[i][0] == 0)
232 continue;
233
234 mime_type = gdk_intern_mime_type (string: split[i]);
235 if (mime_type)
236 gdk_content_formats_builder_add_mime_type (builder, mime_type);
237 else
238 break;
239 }
240
241 if (split[i] != NULL)
242 {
243 g_strfreev (str_array: split);
244 gdk_content_formats_builder_unref (builder);
245 return NULL;
246 }
247
248 g_strfreev (str_array: split);
249 return gdk_content_formats_builder_free_to_formats (builder);
250}
251
252/**
253 * gdk_content_formats_ref:
254 * @formats: a `GdkContentFormats`
255 *
256 * Increases the reference count of a `GdkContentFormats` by one.
257 *
258 * Returns: the passed in `GdkContentFormats`.
259 */
260GdkContentFormats *
261gdk_content_formats_ref (GdkContentFormats *formats)
262{
263 g_return_val_if_fail (formats != NULL, NULL);
264
265 formats->ref_count++;
266
267 return formats;
268}
269
270/**
271 * gdk_content_formats_unref:
272 * @formats: a `GdkContentFormats`
273 *
274 * Decreases the reference count of a `GdkContentFormats` by one.
275 *
276 * If the resulting reference count is zero, frees the formats.
277 */
278void
279gdk_content_formats_unref (GdkContentFormats *formats)
280{
281 g_return_if_fail (formats != NULL);
282 g_return_if_fail (formats->ref_count > 0);
283
284 formats->ref_count--;
285 if (formats->ref_count > 0)
286 return;
287
288 g_free (mem: formats->gtypes);
289 g_free (mem: formats->mime_types);
290 g_slice_free (GdkContentFormats, formats);
291}
292
293/**
294 * gdk_content_formats_print:
295 * @formats: a `GdkContentFormats`
296 * @string: a `GString` to print into
297 *
298 * Prints the given @formats into a string for human consumption.
299 *
300 * The result of this function can later be parsed with
301 * [func@Gdk.ContentFormats.parse].
302 */
303void
304gdk_content_formats_print (GdkContentFormats *formats,
305 GString *string)
306{
307 gsize i;
308
309 g_return_if_fail (formats != NULL);
310 g_return_if_fail (string != NULL);
311
312 for (i = 0; i < formats->n_gtypes; i++)
313 {
314 if (i > 0)
315 g_string_append (string, val: " ");
316 g_string_append (string, val: g_type_name (type: formats->gtypes[i]));
317 }
318 for (i = 0; i < formats->n_mime_types; i++)
319 {
320 if (i > 0 || formats->n_gtypes > 0)
321 g_string_append (string, val: " ");
322 g_string_append (string, val: formats->mime_types[i]);
323 }
324}
325
326/**
327 * gdk_content_formats_to_string:
328 * @formats: a `GdkContentFormats`
329 *
330 * Prints the given @formats into a human-readable string.
331 *
332 * The resulting string can be parsed with [func@Gdk.ContentFormats.parse].
333 *
334 * This is a small wrapper around [method@Gdk.ContentFormats.print]
335 * to help when debugging.
336 *
337 * Returns: (transfer full): a new string
338 */
339char *
340gdk_content_formats_to_string (GdkContentFormats *formats)
341{
342 GString *string;
343
344 g_return_val_if_fail (formats != NULL, NULL);
345
346 string = g_string_new (NULL);
347 gdk_content_formats_print (formats, string);
348
349 return g_string_free (string, FALSE);
350}
351
352/**
353 * gdk_content_formats_union:
354 * @first: (transfer full): the `GdkContentFormats` to merge into
355 * @second: (transfer none): the `GdkContentFormats` to merge from
356 *
357 * Append all missing types from @second to @first, in the order
358 * they had in @second.
359 *
360 * Returns: a new `GdkContentFormats`
361 */
362GdkContentFormats *
363gdk_content_formats_union (GdkContentFormats *first,
364 const GdkContentFormats *second)
365{
366 GdkContentFormatsBuilder *builder;
367
368 g_return_val_if_fail (first != NULL, NULL);
369 g_return_val_if_fail (second != NULL, NULL);
370
371 builder = gdk_content_formats_builder_new ();
372
373 gdk_content_formats_builder_add_formats (builder, formats: first);
374 gdk_content_formats_unref (formats: first);
375 gdk_content_formats_builder_add_formats (builder, formats: second);
376
377 return gdk_content_formats_builder_free_to_formats (builder);
378}
379
380static gboolean
381gdk_content_formats_contain_interned_mime_type (const GdkContentFormats *formats,
382 const char *mime_type)
383{
384 gsize i;
385
386 for (i = 0; i < formats->n_mime_types; i++)
387 {
388 if (mime_type == formats->mime_types[i])
389 return TRUE;
390 }
391
392 return FALSE;
393}
394
395/**
396 * gdk_content_formats_match:
397 * @first: the primary `GdkContentFormats` to intersect
398 * @second: the `GdkContentFormats` to intersect with
399 *
400 * Checks if @first and @second have any matching formats.
401 *
402 * Returns: %TRUE if a matching format was found.
403 */
404gboolean
405gdk_content_formats_match (const GdkContentFormats *first,
406 const GdkContentFormats *second)
407{
408 g_return_val_if_fail (first != NULL, FALSE);
409 g_return_val_if_fail (second != NULL, FALSE);
410
411 return gdk_content_formats_match_gtype (first, second) != G_TYPE_INVALID
412 || gdk_content_formats_match_mime_type (first, second) != NULL;
413}
414
415/**
416 * gdk_content_formats_match_gtype:
417 * @first: the primary `GdkContentFormats` to intersect
418 * @second: the `GdkContentFormats` to intersect with
419 *
420 * Finds the first `GType` from @first that is also contained
421 * in @second.
422 *
423 * If no matching `GType` is found, %G_TYPE_INVALID is returned.
424 *
425 * Returns: The first common `GType` or %G_TYPE_INVALID if none.
426 */
427GType
428gdk_content_formats_match_gtype (const GdkContentFormats *first,
429 const GdkContentFormats *second)
430{
431 gsize i;
432
433 g_return_val_if_fail (first != NULL, FALSE);
434 g_return_val_if_fail (second != NULL, FALSE);
435
436 for (i = 0; i < first->n_gtypes; i++)
437 {
438 if (gdk_content_formats_contain_gtype (formats: second, type: first->gtypes[i]))
439 return first->gtypes[i];
440 }
441
442 return G_TYPE_INVALID;
443}
444
445/**
446 * gdk_content_formats_match_mime_type:
447 * @first: the primary `GdkContentFormats` to intersect
448 * @second: the `GdkContentFormats` to intersect with
449 *
450 * Finds the first mime type from @first that is also contained
451 * in @second.
452 *
453 * If no matching mime type is found, %NULL is returned.
454 *
455 * Returns: (nullable): The first common mime type or %NULL if none
456 */
457const char *
458gdk_content_formats_match_mime_type (const GdkContentFormats *first,
459 const GdkContentFormats *second)
460{
461 gsize i;
462
463 g_return_val_if_fail (first != NULL, FALSE);
464 g_return_val_if_fail (second != NULL, FALSE);
465
466 for (i = 0; i < first->n_mime_types; i++)
467 {
468 if (gdk_content_formats_contain_interned_mime_type (formats: second, mime_type: first->mime_types[i]))
469 return first->mime_types[i];
470 }
471
472 return NULL;
473}
474
475/**
476 * gdk_content_formats_contain_gtype:
477 * @formats: a `GdkContentFormats`
478 * @type: the `GType` to search for
479 *
480 * Checks if a given `GType` is part of the given @formats.
481 *
482 * Returns: %TRUE if the `GType` was found
483 */
484gboolean
485gdk_content_formats_contain_gtype (const GdkContentFormats *formats,
486 GType type)
487{
488 g_return_val_if_fail (formats != NULL, FALSE);
489
490 gsize i;
491
492 for (i = 0; i < formats->n_gtypes; i++)
493 {
494 if (type == formats->gtypes[i])
495 return TRUE;
496 }
497
498 return FALSE;
499}
500
501/**
502 * gdk_content_formats_contain_mime_type:
503 * @formats: a `GdkContentFormats`
504 * @mime_type: the mime type to search for
505 *
506 * Checks if a given mime type is part of the given @formats.
507 *
508 * Returns: %TRUE if the mime_type was found
509 */
510gboolean
511gdk_content_formats_contain_mime_type (const GdkContentFormats *formats,
512 const char *mime_type)
513{
514 g_return_val_if_fail (formats != NULL, FALSE);
515 g_return_val_if_fail (mime_type != NULL, FALSE);
516
517 return gdk_content_formats_contain_interned_mime_type (formats,
518 mime_type: g_intern_string (string: mime_type));
519}
520
521/**
522 * gdk_content_formats_get_gtypes:
523 * @formats: a `GdkContentFormats`
524 * @n_gtypes: (out) (optional): optional pointer to take the
525 * number of `GType`s contained in the return value
526 *
527 * Gets the `GType`s included in @formats.
528 *
529 * Note that @formats may not contain any `GType`s, in particular when
530 * they are empty. In that case %NULL will be returned.
531 *
532 * Returns: (transfer none) (nullable) (array length=n_gtypes zero-terminated=1):
533 * %G_TYPE_INVALID-terminated array of types included in @formats
534 */
535const GType *
536gdk_content_formats_get_gtypes (const GdkContentFormats *formats,
537 gsize *n_gtypes)
538{
539 g_return_val_if_fail (formats != NULL, NULL);
540
541 if (n_gtypes)
542 *n_gtypes = formats->n_gtypes;
543
544 return formats->gtypes;
545}
546
547/**
548 * gdk_content_formats_get_mime_types:
549 * @formats: a `GdkContentFormats`
550 * @n_mime_types: (out) (optional): optional pointer to take the
551 * number of mime types contained in the return value
552 *
553 * Gets the mime types included in @formats.
554 *
555 * Note that @formats may not contain any mime types, in particular
556 * when they are empty. In that case %NULL will be returned.
557 *
558 * Returns: (transfer none) (nullable) (array length=n_mime_types zero-terminated=1):
559 * %NULL-terminated array of interned strings of mime types included
560 * in @formats
561 */
562const char * const *
563gdk_content_formats_get_mime_types (const GdkContentFormats *formats,
564 gsize *n_mime_types)
565{
566 g_return_val_if_fail (formats != NULL, NULL);
567
568 if (n_mime_types)
569 *n_mime_types = formats->n_mime_types;
570
571 return formats->mime_types;
572}
573
574/**
575 * GdkContentFormatsBuilder:
576 *
577 * A `GdkContentFormatsBuilder` is an auxiliary struct used to create
578 * new `GdkContentFormats`, and should not be kept around.
579 */
580
581struct _GdkContentFormatsBuilder
582{
583 int ref_count;
584
585 /* (element-type GType) */
586 GSList *gtypes;
587 gsize n_gtypes;
588
589 /* (element-type utf8) (interned) */
590 GSList *mime_types;
591 gsize n_mime_types;
592};
593
594G_DEFINE_BOXED_TYPE (GdkContentFormatsBuilder,
595 gdk_content_formats_builder,
596 gdk_content_formats_builder_ref,
597 gdk_content_formats_builder_unref)
598
599/**
600 * gdk_content_formats_builder_new:
601 *
602 * Create a new `GdkContentFormatsBuilder` object.
603 *
604 * The resulting builder would create an empty `GdkContentFormats`.
605 * Use addition functions to add types to it.
606 *
607 * Returns: a new `GdkContentFormatsBuilder`
608 */
609GdkContentFormatsBuilder *
610gdk_content_formats_builder_new (void)
611{
612 GdkContentFormatsBuilder *builder;
613
614 builder = g_slice_new0 (GdkContentFormatsBuilder);
615 builder->ref_count = 1;
616
617 return builder;
618}
619
620/**
621 * gdk_content_formats_builder_ref:
622 * @builder: a `GdkContentFormatsBuilder`
623 *
624 * Acquires a reference on the given @builder.
625 *
626 * This function is intended primarily for bindings.
627 * `GdkContentFormatsBuilder` objects should not be kept around.
628 *
629 * Returns: (transfer none): the given `GdkContentFormatsBuilder`
630 * with its reference count increased
631 */
632GdkContentFormatsBuilder *
633gdk_content_formats_builder_ref (GdkContentFormatsBuilder *builder)
634{
635 g_return_val_if_fail (builder != NULL, NULL);
636 g_return_val_if_fail (builder->ref_count > 0, NULL);
637
638 builder->ref_count += 1;
639
640 return builder;
641}
642
643static void
644gdk_content_formats_builder_clear (GdkContentFormatsBuilder *builder)
645{
646 g_clear_pointer (&builder->gtypes, g_slist_free);
647 g_clear_pointer (&builder->mime_types, g_slist_free);
648}
649
650/**
651 * gdk_content_formats_builder_unref:
652 * @builder: a `GdkContentFormatsBuilder`
653 *
654 * Releases a reference on the given @builder.
655 */
656void
657gdk_content_formats_builder_unref (GdkContentFormatsBuilder *builder)
658{
659 g_return_if_fail (builder != NULL);
660 g_return_if_fail (builder->ref_count > 0);
661
662 builder->ref_count -= 1;
663
664 if (builder->ref_count > 0)
665 return;
666
667 gdk_content_formats_builder_clear (builder);
668 g_slice_free (GdkContentFormatsBuilder, builder);
669}
670
671/**
672 * gdk_content_formats_builder_free_to_formats: (skip)
673 * @builder: a `GdkContentFormatsBuilder`
674 *
675 * Creates a new `GdkContentFormats` from the current state of the
676 * given @builder, and frees the @builder instance.
677 *
678 * Returns: (transfer full): the newly created `GdkContentFormats`
679 * with all the formats added to @builder
680 */
681GdkContentFormats *
682gdk_content_formats_builder_free_to_formats (GdkContentFormatsBuilder *builder)
683{
684 GdkContentFormats *res;
685
686 g_return_val_if_fail (builder != NULL, NULL);
687
688 res = gdk_content_formats_builder_to_formats (builder);
689
690 gdk_content_formats_builder_unref (builder);
691
692 return res;
693}
694
695/**
696 * gdk_content_formats_builder_to_formats:
697 * @builder: a `GdkContentFormats`Builder
698 *
699 * Creates a new `GdkContentFormats` from the given @builder.
700 *
701 * The given `GdkContentFormatsBuilder` is reset once this function returns;
702 * you cannot call this function multiple times on the same @builder instance.
703 *
704 * This function is intended primarily for bindings. C code should use
705 * [method@Gdk.ContentFormatsBuilder.free_to_formats].
706 *
707 * Returns: (transfer full): the newly created `GdkContentFormats`
708 * with all the formats added to @builder
709 */
710GdkContentFormats *
711gdk_content_formats_builder_to_formats (GdkContentFormatsBuilder *builder)
712{
713 GdkContentFormats *result;
714 GType *gtypes;
715 const char **mime_types;
716 GSList *l;
717 gsize i;
718
719 g_return_val_if_fail (builder != NULL, NULL);
720
721 if (builder->n_gtypes > 0)
722 {
723 gtypes = g_new (GType, builder->n_gtypes + 1);
724 i = builder->n_gtypes;
725 gtypes[i--] = G_TYPE_INVALID;
726 /* add backwards because most important type is last in the list */
727 for (l = builder->gtypes; l; l = l->next)
728 gtypes[i--] = GPOINTER_TO_SIZE (l->data);
729 }
730 else
731 {
732 gtypes = NULL;
733 }
734
735 if (builder->n_mime_types > 0)
736 {
737 mime_types = g_new (const char *, builder->n_mime_types + 1);
738 i = builder->n_mime_types;
739 mime_types[i--] = NULL;
740 /* add backwards because most important type is last in the list */
741 for (l = builder->mime_types; l; l = l->next)
742 mime_types[i--] = l->data;
743 }
744 else
745 {
746 mime_types = NULL;
747 }
748
749 result = gdk_content_formats_new_take (gtypes, n_gtypes: builder->n_gtypes,
750 mime_types, n_mime_types: builder->n_mime_types);
751
752 gdk_content_formats_builder_clear (builder);
753
754 return result;
755}
756
757/**
758 * gdk_content_formats_builder_add_formats:
759 * @builder: a `GdkContentFormatsBuilder`
760 * @formats: the formats to add
761 *
762 * Appends all formats from @formats to @builder, skipping those that
763 * already exist.
764 */
765void
766gdk_content_formats_builder_add_formats (GdkContentFormatsBuilder *builder,
767 const GdkContentFormats *formats)
768{
769 gsize i;
770
771 g_return_if_fail (builder != NULL);
772 g_return_if_fail (formats != NULL);
773
774 for (i = 0; i < formats->n_gtypes; i++)
775 gdk_content_formats_builder_add_gtype (builder, type: formats->gtypes[i]);
776
777 for (i = 0; i < formats->n_mime_types; i++)
778 gdk_content_formats_builder_add_mime_type (builder, mime_type: formats->mime_types[i]);
779}
780
781/**
782 * gdk_content_formats_builder_add_gtype:
783 * @builder: a `GdkContentFormats`Builder
784 * @type: a `GType`
785 *
786 * Appends @type to @builder if it has not already been added.
787 **/
788void
789gdk_content_formats_builder_add_gtype (GdkContentFormatsBuilder *builder,
790 GType type)
791{
792 g_return_if_fail (builder != NULL);
793 g_return_if_fail (type != G_TYPE_INVALID);
794
795 if (g_slist_find (list: builder->gtypes, GSIZE_TO_POINTER (type)))
796 return;
797
798 builder->gtypes = g_slist_prepend (list: builder->gtypes, GSIZE_TO_POINTER (type));
799 builder->n_gtypes++;
800}
801
802/**
803 * gdk_content_formats_builder_add_mime_type:
804 * @builder: a `GdkContentFormatsBuilder`
805 * @mime_type: a mime type
806 *
807 * Appends @mime_type to @builder if it has not already been added.
808 */
809void
810gdk_content_formats_builder_add_mime_type (GdkContentFormatsBuilder *builder,
811 const char *mime_type)
812{
813 g_return_if_fail (builder != NULL);
814 g_return_if_fail (mime_type != NULL);
815
816 mime_type = g_intern_string (string: mime_type);
817
818 if (g_slist_find (list: builder->mime_types, data: mime_type))
819 return;
820
821 builder->mime_types = g_slist_prepend (list: builder->mime_types, data: (gpointer) mime_type);
822 builder->n_mime_types++;
823}
824
825/* {{{ GdkFileList */
826
827/* We're using GdkFileList* and GSList* interchangeably, counting on the
828 * fact that we're just passing around gpointers; the only reason why we
829 * have a GdkFileList opaque type is for language bindings, because they
830 * can have no idea what a GSList of GFiles is.
831 */
832
833static gpointer
834gdk_file_list_copy (gpointer list)
835{
836 return g_slist_copy_deep (list, func: (GCopyFunc) g_object_ref, NULL);
837}
838
839static void
840gdk_file_list_free (gpointer list)
841{
842 g_slist_free_full (list, free_func: g_object_unref);
843}
844
845G_DEFINE_BOXED_TYPE (GdkFileList, gdk_file_list, gdk_file_list_copy, gdk_file_list_free)
846
847/**
848 * gdk_file_list_get_files:
849 * @file_list: the file list
850 *
851 * Retrieves the list of files inside a `GdkFileList`.
852 *
853 * This function is meant for language bindings.
854 *
855 * Returns: (transfer container) (element-type GFile): the files inside the list
856 *
857 * Since: 4.6
858 */
859GSList *
860gdk_file_list_get_files (GdkFileList *file_list)
861{
862 return g_slist_copy (list: (GSList *) file_list);
863}
864
865/* }}} */
866

source code of gtk/gdk/gdkcontentformats.c