cleanup
[platform/upstream/glib.git] / gio / gicon.c
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 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 #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 {
82 }
83
84 /**
85  * g_icon_hash:
86  * @icon: #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 {
97   GIconIface *iface;
98
99   g_return_val_if_fail (G_IS_ICON (icon), 0);
100
101   iface = G_ICON_GET_IFACE (icon);
102
103   return (* iface->hash) ((GIcon *)icon);
104 }
105
106 /**
107  * g_icon_equal:
108  * @icon1: (allow-none): pointer to the first #GIcon.
109  * @icon2: (allow-none): 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 {
119   GIconIface *iface;
120
121   if (icon1 == NULL && icon2 == NULL)
122     return TRUE;
123
124   if (icon1 == NULL || icon2 == NULL)
125     return FALSE;
126   
127   if (G_TYPE_FROM_INSTANCE (icon1) != G_TYPE_FROM_INSTANCE (icon2))
128     return FALSE;
129
130   iface = G_ICON_GET_IFACE (icon1);
131   
132   return (* iface->equal) (icon1, icon2);
133 }
134
135 static gboolean
136 g_icon_to_string_tokenized (GIcon *icon, GString *s)
137 {
138   GPtrArray *tokens;
139   gint version;
140   GIconIface *icon_iface;
141   int i;
142
143   g_return_val_if_fail (icon != NULL, FALSE);
144   g_return_val_if_fail (G_IS_ICON (icon), FALSE);
145
146   icon_iface = G_ICON_GET_IFACE (icon);
147   if (icon_iface->to_tokens == NULL)
148     return FALSE;
149
150   tokens = g_ptr_array_new ();
151   if (!icon_iface->to_tokens (icon, tokens, &version))
152     {
153       g_ptr_array_free (tokens, TRUE);
154       return FALSE;
155     }
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   g_string_append (s, g_type_name_from_instance ((GTypeInstance *)icon));
162   if (version != 0)
163     g_string_append_printf (s, ".%d", version);
164   
165   for (i = 0; i < tokens->len; i++)
166     {
167       char *token;
168
169       token = g_ptr_array_index (tokens, i);
170
171       g_string_append_c (s, ' ');
172       /* We really only need to escape spaces here, so allow lots of otherwise reserved chars */
173       g_string_append_uri_escaped (s, token,
174                                    G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
175
176       g_free (token);
177     }
178   
179   g_ptr_array_free (tokens, TRUE);
180   
181   return TRUE;
182 }
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, the encoding is
203  *    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 {
214   gchar *ret;
215
216   g_return_val_if_fail (icon != NULL, NULL);
217   g_return_val_if_fail (G_IS_ICON (icon), NULL);
218
219   ret = NULL;
220
221   if (G_IS_FILE_ICON (icon))
222     {
223       GFile *file;
224
225       file = g_file_icon_get_file (G_FILE_ICON (icon));
226       if (g_file_is_native (file))
227         {
228           ret = g_file_get_path (file);
229           if (!g_utf8_validate (ret, -1, NULL))
230             {
231               g_free (ret);
232               ret = NULL;
233             }
234         }
235       else
236         ret = g_file_get_uri (file);
237     }
238   else if (G_IS_THEMED_ICON (icon))
239     {
240       const char * const *names;
241
242       names = g_themed_icon_get_names (G_THEMED_ICON (icon));
243       if (names != NULL &&
244           names[0] != NULL &&
245           names[0][0] != '.' && /* Allowing icons starting with dot would break G_ICON_SERIALIZATION_MAGIC0 */
246           g_utf8_validate (names[0], -1, NULL) && /* Only return utf8 strings */
247           names[1] == NULL)
248         ret = g_strdup (names[0]);
249     }
250
251   if (ret == NULL)
252     {
253       GString *s;
254
255       s = g_string_new (G_ICON_SERIALIZATION_MAGIC0);
256
257       if (g_icon_to_string_tokenized (icon, s))
258         ret = g_string_free (s, FALSE);
259       else
260         g_string_free (s, TRUE);
261     }
262
263   return ret;
264 }
265
266 static GIcon *
267 g_icon_new_from_tokens (char   **tokens,
268                         GError **error)
269 {
270   GIcon *icon;
271   char *typename, *version_str;
272   GType type;
273   gpointer klass;
274   GIconIface *icon_iface;
275   gint version;
276   char *endp;
277   int num_tokens;
278   int i;
279
280   icon = NULL;
281   klass = NULL;
282
283   num_tokens = g_strv_length (tokens);
284
285   if (num_tokens < 1)
286     {
287       g_set_error (error,
288                    G_IO_ERROR,
289                    G_IO_ERROR_INVALID_ARGUMENT,
290                    _("Wrong number of tokens (%d)"),
291                    num_tokens);
292       goto out;
293     }
294   
295   typename = tokens[0];
296   version_str = strchr (typename, '.');
297   if (version_str)
298     {
299       *version_str = 0;
300       version_str += 1;
301     }
302   
303   
304   type = g_type_from_name (tokens[0]);
305   if (type == 0)
306     {
307       g_set_error (error,
308                    G_IO_ERROR,
309                    G_IO_ERROR_INVALID_ARGUMENT,
310                    _("No type for class name %s"),
311                    tokens[0]);
312       goto out;
313     }
314
315   if (!g_type_is_a (type, G_TYPE_ICON))
316     {
317       g_set_error (error,
318                    G_IO_ERROR,
319                    G_IO_ERROR_INVALID_ARGUMENT,
320                    _("Type %s does not implement the GIcon interface"),
321                    tokens[0]);
322       goto out;
323     }
324
325   klass = g_type_class_ref (type);
326   if (klass == NULL)
327     {
328       g_set_error (error,
329                    G_IO_ERROR,
330                    G_IO_ERROR_INVALID_ARGUMENT,
331                    _("Type %s is not classed"),
332                    tokens[0]);
333       goto out;
334     }
335
336   version = 0;
337   if (version_str)
338     {
339       version = strtol (version_str, &endp, 10);
340       if (endp == NULL || *endp != '\0')
341         {
342           g_set_error (error,
343                        G_IO_ERROR,
344                        G_IO_ERROR_INVALID_ARGUMENT,
345                        _("Malformed version number: %s"),
346                        version_str);
347           goto out;
348         }
349     }
350
351   icon_iface = g_type_interface_peek (klass, G_TYPE_ICON);
352   g_assert (icon_iface != NULL);
353
354   if (icon_iface->from_tokens == NULL)
355     {
356       g_set_error (error,
357                    G_IO_ERROR,
358                    G_IO_ERROR_INVALID_ARGUMENT,
359                    _("Type %s does not implement from_tokens() on the GIcon interface"),
360                    tokens[0]);
361       goto out;
362     }
363
364   for (i = 1;  i < num_tokens; i++)
365     {
366       char *escaped;
367
368       escaped = tokens[i];
369       tokens[i] = g_uri_unescape_string (escaped, NULL);
370       g_free (escaped);
371     }
372   
373   icon = icon_iface->from_tokens (tokens + 1, num_tokens - 1, version, error);
374
375  out:
376   if (klass != NULL)
377     g_type_class_unref (klass);
378   return icon;
379 }
380
381 static void
382 ensure_builtin_icon_types (void)
383 {
384   g_type_ensure (G_TYPE_THEMED_ICON);
385   g_type_ensure (G_TYPE_FILE_ICON);
386   g_type_ensure (G_TYPE_EMBLEMED_ICON);
387   g_type_ensure (G_TYPE_EMBLEM);
388 }
389
390 /* handles the 'simple' cases: GFileIcon and GThemedIcon */
391 static GIcon *
392 g_icon_new_for_string_simple (const gchar *str)
393 {
394   gchar *scheme;
395   GIcon *icon;
396
397   if (str[0] == '.')
398     return NULL;
399
400   /* handle special GFileIcon and GThemedIcon cases */
401   scheme = g_uri_parse_scheme (str);
402   if (scheme != NULL || str[0] == '/' || str[0] == G_DIR_SEPARATOR)
403     {
404       GFile *location;
405       location = g_file_new_for_commandline_arg (str);
406       icon = g_file_icon_new (location);
407       g_object_unref (location);
408     }
409   else
410     icon = g_themed_icon_new (str);
411
412   g_free (scheme);
413
414   return icon;
415 }
416
417 /**
418  * g_icon_new_for_string:
419  * @str: A string obtained via g_icon_to_string().
420  * @error: Return location for error.
421  *
422  * Generate a #GIcon instance from @str. This function can fail if
423  * @str is not valid - see g_icon_to_string() for discussion.
424  *
425  * If your application or library provides one or more #GIcon
426  * implementations you need to ensure that each #GType is registered
427  * with the type system prior to calling g_icon_new_for_string().
428  *
429  * Returns: (transfer full): An object implementing the #GIcon
430  *          interface or %NULL if @error is set.
431  *
432  * Since: 2.20
433  **/
434 GIcon *
435 g_icon_new_for_string (const gchar   *str,
436                        GError       **error)
437 {
438   GIcon *icon = NULL;
439
440   g_return_val_if_fail (str != NULL, NULL);
441
442   icon = g_icon_new_for_string_simple (str);
443   if (icon)
444     return icon;
445
446   ensure_builtin_icon_types ();
447
448   if (g_str_has_prefix (str, G_ICON_SERIALIZATION_MAGIC0))
449     {
450       gchar **tokens;
451
452       /* handle tokenized encoding */
453       tokens = g_strsplit (str + sizeof (G_ICON_SERIALIZATION_MAGIC0) - 1, " ", 0);
454       icon = g_icon_new_from_tokens (tokens, error);
455       g_strfreev (tokens);
456     }
457   else
458     g_set_error_literal (error,
459                          G_IO_ERROR,
460                          G_IO_ERROR_INVALID_ARGUMENT,
461                          _("Can't handle the supplied version of the icon encoding"));
462
463   return icon;
464 }
465
466 static GEmblem *
467 g_icon_deserialize_emblem (GVariant *value)
468 {
469   GVariant *emblem_metadata;
470   GVariant *emblem_data;
471   const gchar *origin_nick;
472   GIcon *emblem_icon;
473   GEmblem *emblem;
474
475   g_variant_get (value, "(v@a{sv})", &emblem_data, &emblem_metadata);
476
477   emblem = NULL;
478
479   emblem_icon = g_icon_deserialize (emblem_data);
480   if (emblem_icon != NULL)
481     {
482       /* Check if we should create it with an origin. */
483       if (g_variant_lookup (emblem_metadata, "origin", "&s", &origin_nick))
484         {
485           GEnumClass *origin_class;
486           GEnumValue *origin_value;
487
488           origin_class = g_type_class_ref (G_TYPE_EMBLEM_ORIGIN);
489           origin_value = g_enum_get_value_by_nick (origin_class, origin_nick);
490           if (origin_value)
491             emblem = g_emblem_new_with_origin (emblem_icon, origin_value->value);
492           g_type_class_unref (origin_class);
493         }
494
495       /* We didn't create it with an origin, so do it without. */
496       if (emblem == NULL)
497         emblem = g_emblem_new (emblem_icon);
498
499       g_object_unref (emblem_icon);
500     }
501
502   g_variant_unref (emblem_metadata);
503   g_variant_unref (emblem_data);
504
505   return emblem;
506 }
507
508 static GIcon *
509 g_icon_deserialize_emblemed (GVariant *value)
510 {
511   GVariantIter *emblems;
512   GVariant *icon_data;
513   GIcon *main_icon;
514   GIcon *icon;
515
516   g_variant_get (value, "(va(va{sv}))", &icon_data, &emblems);
517   main_icon = g_icon_deserialize (icon_data);
518
519   if (main_icon)
520     {
521       GVariant *emblem_data;
522
523       icon = g_emblemed_icon_new (main_icon, NULL);
524
525       while ((emblem_data = g_variant_iter_next_value (emblems)))
526         {
527           GEmblem *emblem;
528
529           emblem = g_icon_deserialize_emblem (emblem_data);
530
531           if (emblem)
532             {
533               g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (icon), emblem);
534               g_object_unref (emblem);
535             }
536
537           g_variant_unref (emblem_data);
538         }
539
540       g_object_unref (main_icon);
541     }
542   else
543     icon = NULL;
544
545   g_variant_iter_free (emblems);
546   g_variant_unref (icon_data);
547
548   return icon;
549 }
550
551 /**
552  * g_icon_deserialize:
553  * @value: a #GVariant created with g_icon_serialize()
554  *
555  * Deserializes a #GIcon previously serialized using g_icon_serialize().
556  *
557  * Returns: (transfer full): a #GIcon, or %NULL when deserialization fails.
558  *
559  * Since: 2.38
560  */
561 GIcon *
562 g_icon_deserialize (GVariant *value)
563 {
564   const gchar *tag;
565   GVariant *val;
566   GIcon *icon;
567
568   g_return_val_if_fail (value != NULL, NULL);
569   g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) ||
570                         g_variant_is_of_type (value, G_VARIANT_TYPE ("(sv)")), NULL);
571
572   /* Handle some special cases directly so that people can hard-code
573    * stuff into GMenuModel xml files without resorting to using GVariant
574    * text format to describe one of the explicitly-tagged possibilities
575    * below.
576    */
577   if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
578     return g_icon_new_for_string_simple (g_variant_get_string (value, NULL));
579
580   /* Otherwise, use the tagged union format */
581   g_variant_get (value, "(&sv)", &tag, &val);
582
583   icon = NULL;
584
585   if (g_str_equal (tag, "file") && g_variant_is_of_type (val, G_VARIANT_TYPE_STRING))
586     {
587       GFile *file;
588
589       file = g_file_new_for_commandline_arg (g_variant_get_string (val, NULL));
590       icon = g_file_icon_new (file);
591       g_object_unref (file);
592     }
593   else if (g_str_equal (tag, "themed") && g_variant_is_of_type (val, G_VARIANT_TYPE_STRING_ARRAY))
594     {
595       const gchar **names;
596       gsize size;
597
598       names = g_variant_get_strv (val, &size);
599       icon = g_themed_icon_new_from_names ((gchar **) names, size);
600       g_free (names);
601     }
602   else if (g_str_equal (tag, "bytes") && g_variant_is_of_type (val, G_VARIANT_TYPE_BYTESTRING))
603     {
604       GBytes *bytes;
605
606       bytes = g_variant_get_data_as_bytes (val);
607       icon = g_bytes_icon_new (bytes);
608       g_bytes_unref (bytes);
609     }
610   else if (g_str_equal (tag, "emblem") && g_variant_is_of_type (val, G_VARIANT_TYPE ("(va{sv})")))
611     {
612       GEmblem *emblem;
613
614       emblem = g_icon_deserialize_emblem (val);
615       if (emblem)
616         icon = G_ICON (emblem);
617     }
618   else if (g_str_equal (tag, "emblemed") && g_variant_is_of_type (val, G_VARIANT_TYPE ("(va(va{sv}))")))
619     {
620       icon = g_icon_deserialize_emblemed (val);
621     }
622   else if (g_str_equal (tag, "gvfs"))
623     {
624       GVfsClass *class;
625       GVfs *vfs;
626
627       vfs = g_vfs_get_default ();
628       class = G_VFS_GET_CLASS (vfs);
629       if (class->deserialize_icon)
630         icon = (* class->deserialize_icon) (vfs, val);
631     }
632
633   g_variant_unref (val);
634
635   return icon;
636 }
637
638 /**
639  * g_icon_serialize:
640  * @icon: a #GIcon
641  *
642  * Serializes a #GIcon into a #GVariant. An equivalent #GIcon can be retrieved
643  * back by calling g_icon_deserialize() on the returned value.
644  * As serialization will avoid using raw icon data when possible, it only
645  * makes sense to transfer the #GVariant between processes on the same machine,
646  * (as opposed to over the network), and within the same file system namespace.
647  *
648  * Returns: (transfer full): a #GVariant, or %NULL when serialization fails.
649  *
650  * Since: 2.38
651  */
652 GVariant *
653 g_icon_serialize (GIcon *icon)
654 {
655   GIconInterface *iface;
656   GVariant *result;
657
658   iface = G_ICON_GET_IFACE (icon);
659
660   if (!iface->serialize)
661     {
662       g_critical ("g_icon_serialize() on icon type '%s' is not implemented", G_OBJECT_TYPE_NAME (icon));
663       return NULL;
664     }
665
666   result = (* iface->serialize) (icon);
667
668   if (result)
669     {
670       g_variant_take_ref (result);
671
672       if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(sv)")))
673         {
674           g_critical ("g_icon_serialize() on icon type '%s' returned GVariant of type '%s' but it must return "
675                       "one with type '(sv)'", G_OBJECT_TYPE_NAME (icon), g_variant_get_type_string (result));
676           g_variant_unref (result);
677           result = NULL;
678         }
679     }
680
681   return result;
682 }