Change LGPL-2.1+ to LGPL-2.1-or-later
[platform/upstream/glib.git] / gio / gemblemedicon.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* GIO - GLib Input, Output and Streaming Library
4  * 
5  * Copyright (C) 2006-2007 Red Hat, Inc.
6  *
7  * SPDX-License-Identifier: LGPL-2.1-or-later
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General
20  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
21  *
22  * Author: Matthias Clasen <mclasen@redhat.com>
23  *         Clemens N. Buss <cebuzz@gmail.com>
24  */
25
26 #include <config.h>
27
28 #include <string.h>
29
30 #include "gemblemedicon.h"
31 #include "glibintl.h"
32 #include "gioerror.h"
33
34
35 /**
36  * SECTION:gemblemedicon
37  * @short_description: Icon with emblems
38  * @include: gio/gio.h
39  * @see_also: #GIcon, #GLoadableIcon, #GThemedIcon, #GEmblem
40  *
41  * #GEmblemedIcon is an implementation of #GIcon that supports
42  * adding an emblem to an icon. Adding multiple emblems to an
43  * icon is ensured via g_emblemed_icon_add_emblem(). 
44  *
45  * Note that #GEmblemedIcon allows no control over the position
46  * of the emblems. See also #GEmblem for more information.
47  **/
48
49 enum {
50   PROP_GICON = 1,
51   NUM_PROPERTIES
52 };
53
54 struct _GEmblemedIconPrivate {
55   GIcon *icon;
56   GList *emblems;
57 };
58
59 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
60
61 static void g_emblemed_icon_icon_iface_init (GIconIface *iface);
62
63 G_DEFINE_TYPE_WITH_CODE (GEmblemedIcon, g_emblemed_icon, G_TYPE_OBJECT,
64                          G_ADD_PRIVATE (GEmblemedIcon)
65                          G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
66                          g_emblemed_icon_icon_iface_init))
67
68
69 static void
70 g_emblemed_icon_finalize (GObject *object)
71 {
72   GEmblemedIcon *emblemed;
73
74   emblemed = G_EMBLEMED_ICON (object);
75
76   g_clear_object (&emblemed->priv->icon);
77   g_list_free_full (emblemed->priv->emblems, g_object_unref);
78
79   (*G_OBJECT_CLASS (g_emblemed_icon_parent_class)->finalize) (object);
80 }
81
82 static void
83 g_emblemed_icon_set_property (GObject  *object,
84                               guint property_id,
85                               const GValue *value,
86                               GParamSpec *pspec)
87 {
88   GEmblemedIcon *self = G_EMBLEMED_ICON (object);
89
90   switch (property_id)
91     {
92     case PROP_GICON:
93       self->priv->icon = g_value_dup_object (value);
94       break;
95     default:
96       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
97       break;
98     }
99 }
100
101 static void
102 g_emblemed_icon_get_property (GObject  *object,
103                               guint property_id,
104                               GValue *value,
105                               GParamSpec *pspec)
106 {
107   GEmblemedIcon *self = G_EMBLEMED_ICON (object);
108
109   switch (property_id)
110     {
111     case PROP_GICON:
112       g_value_set_object (value, self->priv->icon);
113       break;
114     default:
115       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
116       break;
117     }
118 }
119
120 static void
121 g_emblemed_icon_class_init (GEmblemedIconClass *klass)
122 {
123   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
124
125   gobject_class->finalize = g_emblemed_icon_finalize;
126   gobject_class->set_property = g_emblemed_icon_set_property;
127   gobject_class->get_property = g_emblemed_icon_get_property;
128
129   properties[PROP_GICON] =
130     g_param_spec_object ("gicon",
131                          P_("The base GIcon"),
132                          P_("The GIcon to attach emblems to"),
133                          G_TYPE_ICON,
134                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
135
136   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
137 }
138
139 static void
140 g_emblemed_icon_init (GEmblemedIcon *emblemed)
141 {
142   emblemed->priv = g_emblemed_icon_get_instance_private (emblemed);
143 }
144
145 /**
146  * g_emblemed_icon_new:
147  * @icon: a #GIcon
148  * @emblem: (nullable): a #GEmblem, or %NULL
149  *
150  * Creates a new emblemed icon for @icon with the emblem @emblem.
151  *
152  * Returns: (transfer full) (type GEmblemedIcon): a new #GIcon
153  *
154  * Since: 2.18
155  **/
156 GIcon *
157 g_emblemed_icon_new (GIcon   *icon,
158                      GEmblem *emblem)
159 {
160   GEmblemedIcon *emblemed;
161   
162   g_return_val_if_fail (G_IS_ICON (icon), NULL);
163   g_return_val_if_fail (!G_IS_EMBLEM (icon), NULL);
164
165   emblemed = G_EMBLEMED_ICON (g_object_new (G_TYPE_EMBLEMED_ICON,
166                                             "gicon", icon,
167                                             NULL));
168
169   if (emblem != NULL)
170     g_emblemed_icon_add_emblem (emblemed, emblem);
171
172   return G_ICON (emblemed);
173 }
174
175
176 /**
177  * g_emblemed_icon_get_icon:
178  * @emblemed: a #GEmblemedIcon
179  *
180  * Gets the main icon for @emblemed.
181  *
182  * Returns: (transfer none): a #GIcon that is owned by @emblemed
183  *
184  * Since: 2.18
185  **/
186 GIcon *
187 g_emblemed_icon_get_icon (GEmblemedIcon *emblemed)
188 {
189   g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
190
191   return emblemed->priv->icon;
192 }
193
194 /**
195  * g_emblemed_icon_get_emblems:
196  * @emblemed: a #GEmblemedIcon
197  *
198  * Gets the list of emblems for the @icon.
199  *
200  * Returns: (element-type Gio.Emblem) (transfer none): a #GList of
201  *     #GEmblems that is owned by @emblemed
202  *
203  * Since: 2.18
204  **/
205
206 GList *
207 g_emblemed_icon_get_emblems (GEmblemedIcon *emblemed)
208 {
209   g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
210
211   return emblemed->priv->emblems;
212 }
213
214 /**
215  * g_emblemed_icon_clear_emblems:
216  * @emblemed: a #GEmblemedIcon
217  *
218  * Removes all the emblems from @icon.
219  *
220  * Since: 2.28
221  **/
222 void
223 g_emblemed_icon_clear_emblems (GEmblemedIcon *emblemed)
224 {
225   g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
226
227   if (emblemed->priv->emblems == NULL)
228     return;
229
230   g_list_free_full (emblemed->priv->emblems, g_object_unref);
231   emblemed->priv->emblems = NULL;
232 }
233
234 static gint
235 g_emblem_comp (GEmblem *a,
236                GEmblem *b)
237 {
238   guint hash_a = g_icon_hash (G_ICON (a));
239   guint hash_b = g_icon_hash (G_ICON (b));
240
241   if(hash_a < hash_b)
242     return -1;
243
244   if(hash_a == hash_b)
245     return 0;
246
247   return 1;
248 }
249
250 /**
251  * g_emblemed_icon_add_emblem:
252  * @emblemed: a #GEmblemedIcon
253  * @emblem: a #GEmblem
254  *
255  * Adds @emblem to the #GList of #GEmblems.
256  *
257  * Since: 2.18
258  **/
259 void 
260 g_emblemed_icon_add_emblem (GEmblemedIcon *emblemed,
261                             GEmblem       *emblem)
262 {
263   g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
264   g_return_if_fail (G_IS_EMBLEM (emblem));
265
266   g_object_ref (emblem);
267   emblemed->priv->emblems = g_list_insert_sorted (emblemed->priv->emblems, emblem,
268                                                   (GCompareFunc) g_emblem_comp);
269 }
270
271 static guint
272 g_emblemed_icon_hash (GIcon *icon)
273 {
274   GEmblemedIcon *emblemed = G_EMBLEMED_ICON (icon);
275   GList *list;
276   guint hash = g_icon_hash (emblemed->priv->icon);
277
278   for (list = emblemed->priv->emblems; list != NULL; list = list->next)
279     hash ^= g_icon_hash (G_ICON (list->data));
280
281   return hash;
282 }
283
284 static gboolean
285 g_emblemed_icon_equal (GIcon *icon1,
286                        GIcon *icon2)
287 {
288   GEmblemedIcon *emblemed1 = G_EMBLEMED_ICON (icon1);
289   GEmblemedIcon *emblemed2 = G_EMBLEMED_ICON (icon2);
290   GList *list1, *list2;
291
292   if (!g_icon_equal (emblemed1->priv->icon, emblemed2->priv->icon))
293     return FALSE;
294
295   list1 = emblemed1->priv->emblems;
296   list2 = emblemed2->priv->emblems;
297
298   while (list1 && list2)
299   {
300     if (!g_icon_equal (G_ICON (list1->data), G_ICON (list2->data)))
301         return FALSE;
302     
303     list1 = list1->next;
304     list2 = list2->next;
305   }
306   
307   return list1 == NULL && list2 == NULL;
308 }
309
310 static gboolean
311 g_emblemed_icon_to_tokens (GIcon *icon,
312                            GPtrArray *tokens,
313                            gint  *out_version)
314 {
315   GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
316   GList *l;
317   char *s;
318
319   /* GEmblemedIcons are encoded as
320    *
321    *   <encoded_icon> [<encoded_emblem_icon>]*
322    */
323
324   g_return_val_if_fail (out_version != NULL, FALSE);
325
326   *out_version = 0;
327
328   s = g_icon_to_string (emblemed_icon->priv->icon);
329   if (s == NULL)
330     return FALSE;
331
332   g_ptr_array_add (tokens, s);
333
334   for (l = emblemed_icon->priv->emblems; l != NULL; l = l->next)
335     {
336       GIcon *emblem_icon = G_ICON (l->data);
337
338       s = g_icon_to_string (emblem_icon);
339       if (s == NULL)
340         return FALSE;
341       
342       g_ptr_array_add (tokens, s);
343     }
344
345   return TRUE;
346 }
347
348 static GIcon *
349 g_emblemed_icon_from_tokens (gchar  **tokens,
350                              gint     num_tokens,
351                              gint     version,
352                              GError **error)
353 {
354   GEmblemedIcon *emblemed_icon;
355   int n;
356
357   emblemed_icon = NULL;
358
359   if (version != 0)
360     {
361       g_set_error (error,
362                    G_IO_ERROR,
363                    G_IO_ERROR_INVALID_ARGUMENT,
364                    _("Can’t handle version %d of GEmblemedIcon encoding"),
365                    version);
366       goto fail;
367     }
368
369   if (num_tokens < 1)
370     {
371       g_set_error (error,
372                    G_IO_ERROR,
373                    G_IO_ERROR_INVALID_ARGUMENT,
374                    _("Malformed number of tokens (%d) in GEmblemedIcon encoding"),
375                    num_tokens);
376       goto fail;
377     }
378
379   emblemed_icon = g_object_new (G_TYPE_EMBLEMED_ICON, NULL);
380   emblemed_icon->priv->icon = g_icon_new_for_string (tokens[0], error);
381   if (emblemed_icon->priv->icon == NULL)
382     goto fail;
383
384   for (n = 1; n < num_tokens; n++)
385     {
386       GIcon *emblem;
387
388       emblem = g_icon_new_for_string (tokens[n], error);
389       if (emblem == NULL)
390         goto fail;
391
392       if (!G_IS_EMBLEM (emblem))
393         {
394           g_set_error_literal (error,
395                                G_IO_ERROR,
396                                G_IO_ERROR_INVALID_ARGUMENT,
397                                _("Expected a GEmblem for GEmblemedIcon"));
398           g_object_unref (emblem);
399           goto fail;
400         }
401
402       emblemed_icon->priv->emblems = g_list_append (emblemed_icon->priv->emblems, emblem);
403     }
404
405   return G_ICON (emblemed_icon);
406
407  fail:
408   if (emblemed_icon != NULL)
409     g_object_unref (emblemed_icon);
410   return NULL;
411 }
412
413 static GVariant *
414 g_emblemed_icon_serialize (GIcon *icon)
415 {
416   GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
417   GVariantBuilder builder;
418   GVariant *icon_data;
419   GList *node;
420
421   icon_data = g_icon_serialize (emblemed_icon->priv->icon);
422   if (!icon_data)
423     return NULL;
424
425   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(va(va{sv}))"));
426
427   g_variant_builder_add (&builder, "v", icon_data);
428   g_variant_unref (icon_data);
429
430   g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(va{sv})"));
431   for (node = emblemed_icon->priv->emblems; node != NULL; node = node->next)
432     {
433       icon_data = g_icon_serialize (node->data);
434       if (icon_data)
435         {
436           /* We know how emblems serialize, so do a tweak here to
437            * reduce some of the variant wrapping and redundant storage
438            * of 'emblem' over and again...
439            */
440           if (g_variant_is_of_type (icon_data, G_VARIANT_TYPE ("(sv)")))
441             {
442               const gchar *name;
443               GVariant *content;
444
445               g_variant_get (icon_data, "(&sv)", &name, &content);
446
447               if (g_str_equal (name, "emblem") && g_variant_is_of_type (content, G_VARIANT_TYPE ("(va{sv})")))
448                 g_variant_builder_add (&builder, "@(va{sv})", content);
449
450               g_variant_unref (content);
451             }
452
453           g_variant_unref (icon_data);
454         }
455     }
456   g_variant_builder_close (&builder);
457
458   return g_variant_new ("(sv)", "emblemed", g_variant_builder_end (&builder));
459 }
460
461 static void
462 g_emblemed_icon_icon_iface_init (GIconIface *iface)
463 {
464   iface->hash = g_emblemed_icon_hash;
465   iface->equal = g_emblemed_icon_equal;
466   iface->to_tokens = g_emblemed_icon_to_tokens;
467   iface->from_tokens = g_emblemed_icon_from_tokens;
468   iface->serialize = g_emblemed_icon_serialize;
469 }