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 | |
62 | struct _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 | |
73 | G_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 | */ |
90 | const char * |
91 | gdk_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 | |
109 | static GdkContentFormats * |
110 | gdk_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 | */ |
140 | GdkContentFormats * |
141 | gdk_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 | */ |
168 | GdkContentFormats * |
169 | gdk_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 | */ |
199 | GdkContentFormats * |
200 | gdk_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 | */ |
260 | GdkContentFormats * |
261 | gdk_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 | */ |
278 | void |
279 | gdk_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 | */ |
303 | void |
304 | gdk_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 | */ |
339 | char * |
340 | gdk_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 | */ |
362 | GdkContentFormats * |
363 | gdk_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 | |
380 | static gboolean |
381 | gdk_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 | */ |
404 | gboolean |
405 | gdk_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 | */ |
427 | GType |
428 | gdk_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 | */ |
457 | const char * |
458 | gdk_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 | */ |
484 | gboolean |
485 | gdk_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 | */ |
510 | gboolean |
511 | gdk_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 | */ |
535 | const GType * |
536 | gdk_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 | */ |
562 | const char * const * |
563 | gdk_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 | |
581 | struct _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 | |
594 | G_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 | */ |
609 | GdkContentFormatsBuilder * |
610 | gdk_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 | */ |
632 | GdkContentFormatsBuilder * |
633 | gdk_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 | |
643 | static void |
644 | gdk_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 | */ |
656 | void |
657 | gdk_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 | */ |
681 | GdkContentFormats * |
682 | gdk_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 | */ |
710 | GdkContentFormats * |
711 | gdk_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 | */ |
765 | void |
766 | gdk_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 | **/ |
788 | void |
789 | gdk_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 | */ |
809 | void |
810 | gdk_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 | |
833 | static gpointer |
834 | gdk_file_list_copy (gpointer list) |
835 | { |
836 | return g_slist_copy_deep (list, func: (GCopyFunc) g_object_ref, NULL); |
837 | } |
838 | |
839 | static void |
840 | gdk_file_list_free (gpointer list) |
841 | { |
842 | g_slist_free_full (list, free_func: g_object_unref); |
843 | } |
844 | |
845 | G_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 | */ |
859 | GSList * |
860 | gdk_file_list_get_files (GdkFileList *file_list) |
861 | { |
862 | return g_slist_copy (list: (GSList *) file_list); |
863 | } |
864 | |
865 | /* }}} */ |
866 | |