emblemedicon: make GEmblemedIcon subclassable
[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  * 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 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, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
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_IMPLEMENT_INTERFACE (G_TYPE_ICON,
65                          g_emblemed_icon_icon_iface_init))
66
67
68 static void
69 g_emblemed_icon_finalize (GObject *object)
70 {
71   GEmblemedIcon *emblemed;
72
73   emblemed = G_EMBLEMED_ICON (object);
74
75   g_object_unref (emblemed->priv->icon);
76   g_list_foreach (emblemed->priv->emblems, (GFunc) g_object_unref, NULL);
77   g_list_free (emblemed->priv->emblems);
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   g_type_class_add_private (klass, sizeof (GEmblemedIconPrivate));
139 }
140
141 static void
142 g_emblemed_icon_init (GEmblemedIcon *emblemed)
143 {
144   emblemed->priv =
145     G_TYPE_INSTANCE_GET_PRIVATE (emblemed, G_TYPE_EMBLEMED_ICON,
146                                  GEmblemedIconPrivate);
147 }
148
149 /**
150  * g_emblemed_icon_new:
151  * @icon: a #GIcon
152  * @emblem: (allow-none): a #GEmblem, or %NULL
153  *
154  * Creates a new emblemed icon for @icon with the emblem @emblem.
155  *
156  * Returns: (transfer full): a new #GIcon
157  *
158  * Since: 2.18
159  **/
160 GIcon *
161 g_emblemed_icon_new (GIcon   *icon,
162                      GEmblem *emblem)
163 {
164   GEmblemedIcon *emblemed;
165   
166   g_return_val_if_fail (G_IS_ICON (icon), NULL);
167   g_return_val_if_fail (!G_IS_EMBLEM (icon), NULL);
168
169   emblemed = G_EMBLEMED_ICON (g_object_new (G_TYPE_EMBLEMED_ICON,
170                                             "gicon", icon,
171                                             NULL));
172
173   if (emblem != NULL)
174     g_emblemed_icon_add_emblem (emblemed, emblem);
175
176   return G_ICON (emblemed);
177 }
178
179
180 /**
181  * g_emblemed_icon_get_icon:
182  * @emblemed: a #GEmblemedIcon
183  *
184  * Gets the main icon for @emblemed.
185  *
186  * Returns: (transfer none): a #GIcon that is owned by @emblemed
187  *
188  * Since: 2.18
189  **/
190 GIcon *
191 g_emblemed_icon_get_icon (GEmblemedIcon *emblemed)
192 {
193   g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
194
195   return emblemed->priv->icon;
196 }
197
198 /**
199  * g_emblemed_icon_get_emblems:
200  * @emblemed: a #GEmblemedIcon
201  *
202  * Gets the list of emblems for the @icon.
203  *
204  * Returns: (element-type utf8) (transfer none): a #GList of #GEmblem <!-- -->s that
205  * is owned by @emblemed
206  *
207  * Since: 2.18
208  **/
209
210 GList *
211 g_emblemed_icon_get_emblems (GEmblemedIcon *emblemed)
212 {
213   g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
214
215   return emblemed->priv->emblems;
216 }
217
218 /**
219  * g_emblemed_icon_clear_emblems:
220  * @emblemed: a #GEmblemedIcon
221  *
222  * Removes all the emblems from @icon.
223  *
224  * Since: 2.28
225  **/
226 void
227 g_emblemed_icon_clear_emblems (GEmblemedIcon *emblemed)
228 {
229   g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
230
231   if (emblemed->priv->emblems == NULL)
232     return;
233
234   g_list_free_full (emblemed->priv->emblems, g_object_unref);
235   emblemed->priv->emblems = NULL;
236 }
237
238 static gint
239 g_emblem_comp (GEmblem *a,
240                GEmblem *b)
241 {
242   guint hash_a = g_icon_hash (G_ICON (a));
243   guint hash_b = g_icon_hash (G_ICON (b));
244
245   if(hash_a < hash_b)
246     return -1;
247
248   if(hash_a == hash_b)
249     return 0;
250
251   return 1;
252 }
253
254 /**
255  * g_emblemed_icon_add_emblem:
256  * @emblemed: a #GEmblemedIcon
257  * @emblem: a #GEmblem
258  *
259  * Adds @emblem to the #GList of #GEmblem <!-- -->s.
260  *
261  * Since: 2.18
262  **/
263 void 
264 g_emblemed_icon_add_emblem (GEmblemedIcon *emblemed,
265                             GEmblem       *emblem)
266 {
267   g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
268   g_return_if_fail (G_IS_EMBLEM (emblem));
269
270   g_object_ref (emblem);
271   emblemed->priv->emblems = g_list_insert_sorted (emblemed->priv->emblems, emblem,
272                                                   (GCompareFunc) g_emblem_comp);
273 }
274
275 static guint
276 g_emblemed_icon_hash (GIcon *icon)
277 {
278   GEmblemedIcon *emblemed = G_EMBLEMED_ICON (icon);
279   GList *list;
280   guint hash = g_icon_hash (emblemed->priv->icon);
281
282   for (list = emblemed->priv->emblems; list != NULL; list = list->next)
283     hash ^= g_icon_hash (G_ICON (list->data));
284
285   return hash;
286 }
287
288 static gboolean
289 g_emblemed_icon_equal (GIcon *icon1,
290                        GIcon *icon2)
291 {
292   GEmblemedIcon *emblemed1 = G_EMBLEMED_ICON (icon1);
293   GEmblemedIcon *emblemed2 = G_EMBLEMED_ICON (icon2);
294   GList *list1, *list2;
295
296   if (!g_icon_equal (emblemed1->priv->icon, emblemed2->priv->icon))
297     return FALSE;
298
299   list1 = emblemed1->priv->emblems;
300   list2 = emblemed2->priv->emblems;
301
302   while (list1 && list2)
303   {
304     if (!g_icon_equal (G_ICON (list1->data), G_ICON (list2->data)))
305         return FALSE;
306     
307     list1 = list1->next;
308     list2 = list2->next;
309   }
310   
311   return list1 == NULL && list2 == NULL;
312 }
313
314 static gboolean
315 g_emblemed_icon_to_tokens (GIcon *icon,
316                            GPtrArray *tokens,
317                            gint  *out_version)
318 {
319   GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
320   GList *l;
321   char *s;
322
323   /* GEmblemedIcons are encoded as
324    *
325    *   <encoded_icon> [<encoded_emblem_icon>]*
326    */
327
328   g_return_val_if_fail (out_version != NULL, FALSE);
329
330   *out_version = 0;
331
332   s = g_icon_to_string (emblemed_icon->priv->icon);
333   if (s == NULL)
334     return FALSE;
335
336   g_ptr_array_add (tokens, s);
337
338   for (l = emblemed_icon->priv->emblems; l != NULL; l = l->next)
339     {
340       GIcon *emblem_icon = G_ICON (l->data);
341
342       s = g_icon_to_string (emblem_icon);
343       if (s == NULL)
344         return FALSE;
345       
346       g_ptr_array_add (tokens, s);
347     }
348
349   return TRUE;
350 }
351
352 static GIcon *
353 g_emblemed_icon_from_tokens (gchar  **tokens,
354                              gint     num_tokens,
355                              gint     version,
356                              GError **error)
357 {
358   GEmblemedIcon *emblemed_icon;
359   int n;
360
361   emblemed_icon = NULL;
362
363   if (version != 0)
364     {
365       g_set_error (error,
366                    G_IO_ERROR,
367                    G_IO_ERROR_INVALID_ARGUMENT,
368                    _("Can't handle version %d of GEmblemedIcon encoding"),
369                    version);
370       goto fail;
371     }
372
373   if (num_tokens < 1)
374     {
375       g_set_error (error,
376                    G_IO_ERROR,
377                    G_IO_ERROR_INVALID_ARGUMENT,
378                    _("Malformed number of tokens (%d) in GEmblemedIcon encoding"),
379                    num_tokens);
380       goto fail;
381     }
382
383   emblemed_icon = g_object_new (G_TYPE_EMBLEMED_ICON, NULL);
384   emblemed_icon->priv->icon = g_icon_new_for_string (tokens[0], error);
385   if (emblemed_icon->priv->icon == NULL)
386     goto fail;
387
388   for (n = 1; n < num_tokens; n++)
389     {
390       GIcon *emblem;
391
392       emblem = g_icon_new_for_string (tokens[n], error);
393       if (emblem == NULL)
394         goto fail;
395
396       if (!G_IS_EMBLEM (emblem))
397         {
398           g_set_error_literal (error,
399                                G_IO_ERROR,
400                                G_IO_ERROR_INVALID_ARGUMENT,
401                                _("Expected a GEmblem for GEmblemedIcon"));
402           g_object_unref (emblem);
403           goto fail;
404         }
405
406       emblemed_icon->priv->emblems = g_list_append (emblemed_icon->priv->emblems, emblem);
407     }
408
409   return G_ICON (emblemed_icon);
410
411  fail:
412   if (emblemed_icon != NULL)
413     g_object_unref (emblemed_icon);
414   return NULL;
415 }
416
417 static void
418 g_emblemed_icon_icon_iface_init (GIconIface *iface)
419 {
420   iface->hash = g_emblemed_icon_hash;
421   iface->equal = g_emblemed_icon_equal;
422   iface->to_tokens = g_emblemed_icon_to_tokens;
423   iface->from_tokens = g_emblemed_icon_from_tokens;
424 }