Coverage Report

Created: 2025-06-13 06:55

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