Coverage Report

Created: 2025-07-01 07:09

/src/glib/gio/gicon.c
Line
Count
Source (jump to first uncovered line)
1
/* GIO - GLib Input, Output and Streaming Library
2
 * 
3
 * Copyright (C) 2006-2007 Red Hat, Inc.
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.1 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
16
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17
 *
18
 * Author: Alexander Larsson <alexl@redhat.com>
19
 */
20
21
#include "config.h"
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "gicon.h"
26
#include "gthemedicon.h"
27
#include "gfileicon.h"
28
#include "gemblemedicon.h"
29
#include "gbytesicon.h"
30
#include "gfile.h"
31
#include "gioerror.h"
32
#include "gioenumtypes.h"
33
#include "gvfs.h"
34
35
#include "glibintl.h"
36
37
38
/* There versioning of this is implicit, version 1 would be ".1 " */
39
0
#define G_ICON_SERIALIZATION_MAGIC0 ". "
40
41
/**
42
 * SECTION:gicon
43
 * @short_description: Interface for icons
44
 * @include: gio/gio.h
45
 *
46
 * #GIcon is a very minimal interface for icons. It provides functions
47
 * for checking the equality of two icons, hashing of icons and
48
 * serializing an icon to and from strings.
49
 *
50
 * #GIcon does not provide the actual pixmap for the icon as this is out 
51
 * of GIO's scope, however implementations of #GIcon may contain the name 
52
 * of an icon (see #GThemedIcon), or the path to an icon (see #GLoadableIcon). 
53
 *
54
 * To obtain a hash of a #GIcon, see g_icon_hash().
55
 *
56
 * To check if two #GIcons are equal, see g_icon_equal().
57
 *
58
 * For serializing a #GIcon, use g_icon_serialize() and
59
 * g_icon_deserialize().
60
 *
61
 * If you want to consume #GIcon (for example, in a toolkit) you must
62
 * be prepared to handle at least the three following cases:
63
 * #GLoadableIcon, #GThemedIcon and #GEmblemedIcon.  It may also make
64
 * sense to have fast-paths for other cases (like handling #GdkPixbuf
65
 * directly, for example) but all compliant #GIcon implementations
66
 * outside of GIO must implement #GLoadableIcon.
67
 *
68
 * If your application or library provides one or more #GIcon
69
 * implementations you need to ensure that your new implementation also
70
 * implements #GLoadableIcon.  Additionally, you must provide an
71
 * implementation of g_icon_serialize() that gives a result that is
72
 * understood by g_icon_deserialize(), yielding one of the built-in icon
73
 * types.
74
 **/
75
76
typedef GIconIface GIconInterface;
77
G_DEFINE_INTERFACE(GIcon, g_icon, G_TYPE_OBJECT)
78
79
static void
80
g_icon_default_init (GIconInterface *iface)
81
0
{
82
0
}
83
84
/**
85
 * g_icon_hash:
86
 * @icon: (not nullable): #gconstpointer to an icon object.
87
 * 
88
 * Gets a hash for an icon.
89
 *
90
 * Virtual: hash
91
 * Returns: a #guint containing a hash for the @icon, suitable for 
92
 * use in a #GHashTable or similar data structure.
93
 **/
94
guint
95
g_icon_hash (gconstpointer icon)
96
0
{
97
0
  GIconIface *iface;
98
99
0
  g_return_val_if_fail (G_IS_ICON (icon), 0);
100
101
0
  iface = G_ICON_GET_IFACE (icon);
102
103
0
  return (* iface->hash) ((GIcon *)icon);
104
0
}
105
106
/**
107
 * g_icon_equal:
108
 * @icon1: (nullable): pointer to the first #GIcon.
109
 * @icon2: (nullable): pointer to the second #GIcon.
110
 * 
111
 * Checks if two icons are equal.
112
 * 
113
 * Returns: %TRUE if @icon1 is equal to @icon2. %FALSE otherwise.
114
 **/
115
gboolean
116
g_icon_equal (GIcon *icon1,
117
        GIcon *icon2)
118
0
{
119
0
  GIconIface *iface;
120
121
0
  if (icon1 == NULL && icon2 == NULL)
122
0
    return TRUE;
123
124
0
  if (icon1 == NULL || icon2 == NULL)
125
0
    return FALSE;
126
  
127
0
  if (G_TYPE_FROM_INSTANCE (icon1) != G_TYPE_FROM_INSTANCE (icon2))
128
0
    return FALSE;
129
130
0
  iface = G_ICON_GET_IFACE (icon1);
131
  
132
0
  return (* iface->equal) (icon1, icon2);
133
0
}
134
135
static gboolean
136
g_icon_to_string_tokenized (GIcon *icon, GString *s)
137
0
{
138
0
  GPtrArray *tokens;
139
0
  gint version;
140
0
  GIconIface *icon_iface;
141
0
  guint i;
142
143
0
  g_return_val_if_fail (icon != NULL, FALSE);
144
0
  g_return_val_if_fail (G_IS_ICON (icon), FALSE);
145
146
0
  icon_iface = G_ICON_GET_IFACE (icon);
147
0
  if (icon_iface->to_tokens == NULL)
148
0
    return FALSE;
149
150
0
  tokens = g_ptr_array_new ();
151
0
  if (!icon_iface->to_tokens (icon, tokens, &version))
152
0
    {
153
0
      g_ptr_array_free (tokens, TRUE);
154
0
      return FALSE;
155
0
    }
156
157
  /* format: TypeName[.Version] <token_0> .. <token_N-1>
158
     version 0 is implicit and can be omitted
159
     all the tokens are url escaped to ensure they have no spaces in them */
160
  
161
0
  g_string_append (s, g_type_name_from_instance ((GTypeInstance *)icon));
162
0
  if (version != 0)
163
0
    g_string_append_printf (s, ".%d", version);
164
  
165
0
  for (i = 0; i < tokens->len; i++)
166
0
    {
167
0
      char *token;
168
169
0
      token = g_ptr_array_index (tokens, i);
170
171
0
      g_string_append_c (s, ' ');
172
      /* We really only need to escape spaces here, so allow lots of otherwise reserved chars */
173
0
      g_string_append_uri_escaped (s, token,
174
0
           G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
175
176
0
      g_free (token);
177
0
    }
178
  
179
0
  g_ptr_array_free (tokens, TRUE);
180
  
181
0
  return TRUE;
182
0
}
183
184
/**
185
 * g_icon_to_string:
186
 * @icon: a #GIcon.
187
 *
188
 * Generates a textual representation of @icon that can be used for
189
 * serialization such as when passing @icon to a different process or
190
 * saving it to persistent storage. Use g_icon_new_for_string() to
191
 * get @icon back from the returned string.
192
 *
193
 * The encoding of the returned string is proprietary to #GIcon except
194
 * in the following two cases
195
 *
196
 * - If @icon is a #GFileIcon, the returned string is a native path
197
 *   (such as `/path/to/my icon.png`) without escaping
198
 *   if the #GFile for @icon is a native file.  If the file is not
199
 *   native, the returned string is the result of g_file_get_uri()
200
 *   (such as `sftp://path/to/my%20icon.png`).
201
 * 
202
 * - If @icon is a #GThemedIcon with exactly one name and no fallbacks,
203
 *   the encoding is simply the name (such as `network-server`).
204
 *
205
 * Virtual: to_tokens
206
 * Returns: (nullable): An allocated NUL-terminated UTF8 string or
207
 * %NULL if @icon can't be serialized. Use g_free() to free.
208
 *
209
 * Since: 2.20
210
 */
211
gchar *
212
g_icon_to_string (GIcon *icon)
213
0
{
214
0
  gchar *ret;
215
216
0
  g_return_val_if_fail (icon != NULL, NULL);
217
0
  g_return_val_if_fail (G_IS_ICON (icon), NULL);
218
219
0
  ret = NULL;
220
221
0
  if (G_IS_FILE_ICON (icon))
222
0
    {
223
0
      GFile *file;
224
225
0
      file = g_file_icon_get_file (G_FILE_ICON (icon));
226
0
      if (g_file_is_native (file))
227
0
  {
228
0
    ret = g_file_get_path (file);
229
0
    if (!g_utf8_validate (ret, -1, NULL))
230
0
      {
231
0
        g_free (ret);
232
0
        ret = NULL;
233
0
      }
234
0
  }
235
0
      else
236
0
        ret = g_file_get_uri (file);
237
0
    }
238
0
  else if (G_IS_THEMED_ICON (icon))
239
0
    {
240
0
      char     **names                 = NULL;
241
0
      gboolean   use_default_fallbacks = FALSE;
242
243
0
      g_object_get (G_OBJECT (icon),
244
0
                    "names",                 &names,
245
0
                    "use-default-fallbacks", &use_default_fallbacks,
246
0
                    NULL);
247
      /* Themed icon initialized with a single name and no fallbacks. */
248
0
      if (names != NULL &&
249
0
    names[0] != NULL &&
250
0
    names[0][0] != '.' && /* Allowing icons starting with dot would break G_ICON_SERIALIZATION_MAGIC0 */
251
0
    g_utf8_validate (names[0], -1, NULL) && /* Only return utf8 strings */
252
0
          names[1] == NULL &&
253
0
          ! use_default_fallbacks)
254
0
  ret = g_strdup (names[0]);
255
256
0
      g_strfreev (names);
257
0
    }
258
259
0
  if (ret == NULL)
260
0
    {
261
0
      GString *s;
262
263
0
      s = g_string_new (G_ICON_SERIALIZATION_MAGIC0);
264
265
0
      if (g_icon_to_string_tokenized (icon, s))
266
0
  ret = g_string_free (s, FALSE);
267
0
      else
268
0
  g_string_free (s, TRUE);
269
0
    }
270
271
0
  return ret;
272
0
}
273
274
static GIcon *
275
g_icon_new_from_tokens (char   **tokens,
276
      GError **error)
277
0
{
278
0
  GIcon *icon;
279
0
  char *typename, *version_str;
280
0
  GType type;
281
0
  gpointer klass;
282
0
  GIconIface *icon_iface;
283
0
  gint version;
284
0
  char *endp;
285
0
  int num_tokens;
286
0
  int i;
287
288
0
  icon = NULL;
289
0
  klass = NULL;
290
291
0
  num_tokens = g_strv_length (tokens);
292
293
0
  if (num_tokens < 1)
294
0
    {
295
0
      g_set_error (error,
296
0
                   G_IO_ERROR,
297
0
                   G_IO_ERROR_INVALID_ARGUMENT,
298
0
                   _("Wrong number of tokens (%d)"),
299
0
                   num_tokens);
300
0
      goto out;
301
0
    }
302
  
303
0
  typename = tokens[0];
304
0
  version_str = strchr (typename, '.');
305
0
  if (version_str)
306
0
    {
307
0
      *version_str = 0;
308
0
      version_str += 1;
309
0
    }
310
  
311
  
312
0
  type = g_type_from_name (tokens[0]);
313
0
  if (type == 0)
314
0
    {
315
0
      g_set_error (error,
316
0
                   G_IO_ERROR,
317
0
                   G_IO_ERROR_INVALID_ARGUMENT,
318
0
                   _("No type for class name %s"),
319
0
                   tokens[0]);
320
0
      goto out;
321
0
    }
322
323
0
  if (!g_type_is_a (type, G_TYPE_ICON))
324
0
    {
325
0
      g_set_error (error,
326
0
                   G_IO_ERROR,
327
0
                   G_IO_ERROR_INVALID_ARGUMENT,
328
0
                   _("Type %s does not implement the GIcon interface"),
329
0
                   tokens[0]);
330
0
      goto out;
331
0
    }
332
333
0
  klass = g_type_class_ref (type);
334
0
  if (klass == NULL)
335
0
    {
336
0
      g_set_error (error,
337
0
                   G_IO_ERROR,
338
0
                   G_IO_ERROR_INVALID_ARGUMENT,
339
0
                   _("Type %s is not classed"),
340
0
                   tokens[0]);
341
0
      goto out;
342
0
    }
343
344
0
  version = 0;
345
0
  if (version_str)
346
0
    {
347
0
      version = strtol (version_str, &endp, 10);
348
0
      if (endp == NULL || *endp != '\0')
349
0
  {
350
0
    g_set_error (error,
351
0
           G_IO_ERROR,
352
0
           G_IO_ERROR_INVALID_ARGUMENT,
353
0
           _("Malformed version number: %s"),
354
0
           version_str);
355
0
    goto out;
356
0
  }
357
0
    }
358
359
0
  icon_iface = g_type_interface_peek (klass, G_TYPE_ICON);
360
0
  g_assert (icon_iface != NULL);
361
362
0
  if (icon_iface->from_tokens == NULL)
363
0
    {
364
0
      g_set_error (error,
365
0
                   G_IO_ERROR,
366
0
                   G_IO_ERROR_INVALID_ARGUMENT,
367
0
                   _("Type %s does not implement from_tokens() on the GIcon interface"),
368
0
                   tokens[0]);
369
0
      goto out;
370
0
    }
371
372
0
  for (i = 1;  i < num_tokens; i++)
373
0
    {
374
0
      char *escaped;
375
376
0
      escaped = tokens[i];
377
0
      tokens[i] = g_uri_unescape_string (escaped, NULL);
378
0
      g_free (escaped);
379
0
    }
380
  
381
0
  icon = icon_iface->from_tokens (tokens + 1, num_tokens - 1, version, error);
382
383
0
 out:
384
0
  if (klass != NULL)
385
0
    g_type_class_unref (klass);
386
0
  return icon;
387
0
}
388
389
static void
390
ensure_builtin_icon_types (void)
391
0
{
392
0
  g_type_ensure (G_TYPE_THEMED_ICON);
393
0
  g_type_ensure (G_TYPE_FILE_ICON);
394
0
  g_type_ensure (G_TYPE_EMBLEMED_ICON);
395
0
  g_type_ensure (G_TYPE_EMBLEM);
396
0
}
397
398
/* handles the 'simple' cases: GFileIcon and GThemedIcon */
399
static GIcon *
400
g_icon_new_for_string_simple (const gchar *str)
401
0
{
402
0
  gchar *scheme;
403
0
  GIcon *icon;
404
405
0
  if (str[0] == '.')
406
0
    return NULL;
407
408
  /* handle special GFileIcon and GThemedIcon cases */
409
0
  scheme = g_uri_parse_scheme (str);
410
0
  if (scheme != NULL || str[0] == '/' || str[0] == G_DIR_SEPARATOR)
411
0
    {
412
0
      GFile *location;
413
0
      location = g_file_new_for_commandline_arg (str);
414
0
      icon = g_file_icon_new (location);
415
0
      g_object_unref (location);
416
0
    }
417
0
  else
418
0
    icon = g_themed_icon_new (str);
419
420
0
  g_free (scheme);
421
422
0
  return icon;
423
0
}
424
425
/**
426
 * g_icon_new_for_string:
427
 * @str: A string obtained via g_icon_to_string().
428
 * @error: Return location for error.
429
 *
430
 * Generate a #GIcon instance from @str. This function can fail if
431
 * @str is not valid - see g_icon_to_string() for discussion.
432
 *
433
 * If your application or library provides one or more #GIcon
434
 * implementations you need to ensure that each #GType is registered
435
 * with the type system prior to calling g_icon_new_for_string().
436
 *
437
 * Returns: (transfer full): An object implementing the #GIcon
438
 *          interface or %NULL if @error is set.
439
 *
440
 * Since: 2.20
441
 **/
442
GIcon *
443
g_icon_new_for_string (const gchar   *str,
444
                       GError       **error)
445
0
{
446
0
  GIcon *icon = NULL;
447
448
0
  g_return_val_if_fail (str != NULL, NULL);
449
450
0
  icon = g_icon_new_for_string_simple (str);
451
0
  if (icon)
452
0
    return icon;
453
454
0
  ensure_builtin_icon_types ();
455
456
0
  if (g_str_has_prefix (str, G_ICON_SERIALIZATION_MAGIC0))
457
0
    {
458
0
      gchar **tokens;
459
460
      /* handle tokenized encoding */
461
0
      tokens = g_strsplit (str + sizeof (G_ICON_SERIALIZATION_MAGIC0) - 1, " ", 0);
462
0
      icon = g_icon_new_from_tokens (tokens, error);
463
0
      g_strfreev (tokens);
464
0
    }
465
0
  else
466
0
    g_set_error_literal (error,
467
0
                         G_IO_ERROR,
468
0
                         G_IO_ERROR_INVALID_ARGUMENT,
469
0
                         _("Can’t handle the supplied version of the icon encoding"));
470
471
0
  return icon;
472
0
}
473
474
static GEmblem *
475
g_icon_deserialize_emblem (GVariant *value)
476
0
{
477
0
  GVariant *emblem_metadata;
478
0
  GVariant *emblem_data;
479
0
  const gchar *origin_nick;
480
0
  GIcon *emblem_icon;
481
0
  GEmblem *emblem;
482
483
0
  g_variant_get (value, "(v@a{sv})", &emblem_data, &emblem_metadata);
484
485
0
  emblem = NULL;
486
487
0
  emblem_icon = g_icon_deserialize (emblem_data);
488
0
  if (emblem_icon != NULL)
489
0
    {
490
      /* Check if we should create it with an origin. */
491
0
      if (g_variant_lookup (emblem_metadata, "origin", "&s", &origin_nick))
492
0
        {
493
0
          GEnumClass *origin_class;
494
0
          GEnumValue *origin_value;
495
496
0
          origin_class = g_type_class_ref (G_TYPE_EMBLEM_ORIGIN);
497
0
          origin_value = g_enum_get_value_by_nick (origin_class, origin_nick);
498
0
          if (origin_value)
499
0
            emblem = g_emblem_new_with_origin (emblem_icon, origin_value->value);
500
0
          g_type_class_unref (origin_class);
501
0
        }
502
503
      /* We didn't create it with an origin, so do it without. */
504
0
      if (emblem == NULL)
505
0
        emblem = g_emblem_new (emblem_icon);
506
507
0
      g_object_unref (emblem_icon);
508
0
    }
509
510
0
  g_variant_unref (emblem_metadata);
511
0
  g_variant_unref (emblem_data);
512
513
0
  return emblem;
514
0
}
515
516
static GIcon *
517
g_icon_deserialize_emblemed (GVariant *value)
518
0
{
519
0
  GVariantIter *emblems;
520
0
  GVariant *icon_data;
521
0
  GIcon *main_icon;
522
0
  GIcon *icon;
523
524
0
  g_variant_get (value, "(va(va{sv}))", &icon_data, &emblems);
525
0
  main_icon = g_icon_deserialize (icon_data);
526
527
0
  if (main_icon)
528
0
    {
529
0
      GVariant *emblem_data;
530
531
0
      icon = g_emblemed_icon_new (main_icon, NULL);
532
533
0
      while ((emblem_data = g_variant_iter_next_value (emblems)))
534
0
        {
535
0
          GEmblem *emblem;
536
537
0
          emblem = g_icon_deserialize_emblem (emblem_data);
538
539
0
          if (emblem)
540
0
            {
541
0
              g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (icon), emblem);
542
0
              g_object_unref (emblem);
543
0
            }
544
545
0
          g_variant_unref (emblem_data);
546
0
        }
547
548
0
      g_object_unref (main_icon);
549
0
    }
550
0
  else
551
0
    icon = NULL;
552
553
0
  g_variant_iter_free (emblems);
554
0
  g_variant_unref (icon_data);
555
556
0
  return icon;
557
0
}
558
559
/**
560
 * g_icon_deserialize:
561
 * @value: (transfer none): a #GVariant created with g_icon_serialize()
562
 *
563
 * Deserializes a #GIcon previously serialized using g_icon_serialize().
564
 *
565
 * Returns: (nullable) (transfer full): a #GIcon, or %NULL when deserialization fails.
566
 *
567
 * Since: 2.38
568
 */
569
GIcon *
570
g_icon_deserialize (GVariant *value)
571
0
{
572
0
  const gchar *tag;
573
0
  GVariant *val;
574
0
  GIcon *icon;
575
576
0
  g_return_val_if_fail (value != NULL, NULL);
577
0
  g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) ||
578
0
                        g_variant_is_of_type (value, G_VARIANT_TYPE ("(sv)")), NULL);
579
580
  /* Handle some special cases directly so that people can hard-code
581
   * stuff into GMenuModel xml files without resorting to using GVariant
582
   * text format to describe one of the explicitly-tagged possibilities
583
   * below.
584
   */
585
0
  if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
586
0
    return g_icon_new_for_string_simple (g_variant_get_string (value, NULL));
587
588
  /* Otherwise, use the tagged union format */
589
0
  g_variant_get (value, "(&sv)", &tag, &val);
590
591
0
  icon = NULL;
592
593
0
  if (g_str_equal (tag, "file") && g_variant_is_of_type (val, G_VARIANT_TYPE_STRING))
594
0
    {
595
0
      GFile *file;
596
597
0
      file = g_file_new_for_commandline_arg (g_variant_get_string (val, NULL));
598
0
      icon = g_file_icon_new (file);
599
0
      g_object_unref (file);
600
0
    }
601
0
  else if (g_str_equal (tag, "themed") && g_variant_is_of_type (val, G_VARIANT_TYPE_STRING_ARRAY))
602
0
    {
603
0
      const gchar **names;
604
0
      gsize size;
605
606
0
      names = g_variant_get_strv (val, &size);
607
0
      icon = g_themed_icon_new_from_names ((gchar **) names, size);
608
0
      g_free (names);
609
0
    }
610
0
  else if (g_str_equal (tag, "bytes") && g_variant_is_of_type (val, G_VARIANT_TYPE_BYTESTRING))
611
0
    {
612
0
      GBytes *bytes;
613
614
0
      bytes = g_variant_get_data_as_bytes (val);
615
0
      icon = g_bytes_icon_new (bytes);
616
0
      g_bytes_unref (bytes);
617
0
    }
618
0
  else if (g_str_equal (tag, "emblem") && g_variant_is_of_type (val, G_VARIANT_TYPE ("(va{sv})")))
619
0
    {
620
0
      GEmblem *emblem;
621
622
0
      emblem = g_icon_deserialize_emblem (val);
623
0
      if (emblem)
624
0
        icon = G_ICON (emblem);
625
0
    }
626
0
  else if (g_str_equal (tag, "emblemed") && g_variant_is_of_type (val, G_VARIANT_TYPE ("(va(va{sv}))")))
627
0
    {
628
0
      icon = g_icon_deserialize_emblemed (val);
629
0
    }
630
0
  else if (g_str_equal (tag, "gvfs"))
631
0
    {
632
0
      GVfsClass *class;
633
0
      GVfs *vfs;
634
635
0
      vfs = g_vfs_get_default ();
636
0
      class = G_VFS_GET_CLASS (vfs);
637
0
      if (class->deserialize_icon)
638
0
        icon = (* class->deserialize_icon) (vfs, val);
639
0
    }
640
641
0
  g_variant_unref (val);
642
643
0
  return icon;
644
0
}
645
646
/**
647
 * g_icon_serialize:
648
 * @icon: a #GIcon
649
 *
650
 * Serializes a #GIcon into a #GVariant. An equivalent #GIcon can be retrieved
651
 * back by calling g_icon_deserialize() on the returned value.
652
 * As serialization will avoid using raw icon data when possible, it only
653
 * makes sense to transfer the #GVariant between processes on the same machine,
654
 * (as opposed to over the network), and within the same file system namespace.
655
 *
656
 * Returns: (nullable) (transfer full): a #GVariant, or %NULL when serialization fails. The #GVariant will not be floating.
657
 *
658
 * Since: 2.38
659
 */
660
GVariant *
661
g_icon_serialize (GIcon *icon)
662
0
{
663
0
  GIconInterface *iface;
664
0
  GVariant *result;
665
666
0
  iface = G_ICON_GET_IFACE (icon);
667
668
0
  if (!iface->serialize)
669
0
    {
670
0
      g_critical ("g_icon_serialize() on icon type '%s' is not implemented", G_OBJECT_TYPE_NAME (icon));
671
0
      return NULL;
672
0
    }
673
674
0
  result = (* iface->serialize) (icon);
675
676
0
  if (result)
677
0
    {
678
0
      g_variant_take_ref (result);
679
680
0
      if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(sv)")))
681
0
        {
682
0
          g_critical ("g_icon_serialize() on icon type '%s' returned GVariant of type '%s' but it must return "
683
0
                      "one with type '(sv)'", G_OBJECT_TYPE_NAME (icon), g_variant_get_type_string (result));
684
0
          g_variant_unref (result);
685
0
          result = NULL;
686
0
        }
687
0
    }
688
689
0
  return result;
690
0
}