• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22 
23 #include "config.h"
24 
25 #include <string.h>
26 
27 #include "gthemedicon.h"
28 #include "gicon.h"
29 #include "gioerror.h"
30 #include "glibintl.h"
31 
32 #include "gioalias.h"
33 
34 /**
35  * SECTION:gthemedicon
36  * @short_description: Icon theming support
37  * @include: gio/gio.h
38  * @see_also: #GIcon, #GLoadableIcon
39  *
40  * #GThemedIcon is an implementation of #GIcon that supports icon themes.
41  * #GThemedIcon contains a list of all of the icons present in an icon
42  * theme, so that icons can be looked up quickly. #GThemedIcon does
43  * not provide actual pixmaps for icons, just the icon names.
44  * Ideally something like gtk_icon_theme_choose_icon() should be used to
45  * resolve the list of names so that fallback icons work nicely with
46  * themes that inherit other themes.
47  **/
48 
49 static void g_themed_icon_icon_iface_init (GIconIface *iface);
50 
51 struct _GThemedIcon
52 {
53   GObject parent_instance;
54 
55   char     **names;
56   gboolean   use_default_fallbacks;
57 };
58 
59 struct _GThemedIconClass
60 {
61   GObjectClass parent_class;
62 };
63 
64 enum
65 {
66   PROP_0,
67   PROP_NAME,
68   PROP_NAMES,
69   PROP_USE_DEFAULT_FALLBACKS
70 };
71 
G_DEFINE_TYPE_WITH_CODE(GThemedIcon,g_themed_icon,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_ICON,g_themed_icon_icon_iface_init))72 G_DEFINE_TYPE_WITH_CODE (GThemedIcon, g_themed_icon, G_TYPE_OBJECT,
73 			 G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
74 						g_themed_icon_icon_iface_init))
75 
76 static void
77 g_themed_icon_get_property (GObject    *object,
78                             guint       prop_id,
79                             GValue     *value,
80                             GParamSpec *pspec)
81 {
82   GThemedIcon *icon = G_THEMED_ICON (object);
83 
84   switch (prop_id)
85     {
86       case PROP_NAMES:
87         g_value_set_boxed (value, icon->names);
88         break;
89 
90       case PROP_USE_DEFAULT_FALLBACKS:
91         g_value_set_boolean (value, icon->use_default_fallbacks);
92         break;
93 
94       default:
95         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96     }
97 }
98 
99 static void
g_themed_icon_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)100 g_themed_icon_set_property (GObject      *object,
101                             guint         prop_id,
102                             const GValue *value,
103                             GParamSpec   *pspec)
104 {
105   GThemedIcon *icon = G_THEMED_ICON (object);
106   gchar **names;
107   const gchar *name;
108 
109   switch (prop_id)
110     {
111       case PROP_NAME:
112         name = g_value_get_string (value);
113 
114         if (!name)
115           break;
116 
117         if (icon->names)
118           g_strfreev (icon->names);
119 
120         icon->names = g_new (char *, 2);
121         icon->names[0] = g_strdup (name);
122         icon->names[1] = NULL;
123         break;
124 
125       case PROP_NAMES:
126         names = g_value_dup_boxed (value);
127 
128         if (!names)
129           break;
130 
131         if (icon->names)
132           g_strfreev (icon->names);
133 
134         icon->names = names;
135         break;
136 
137       case PROP_USE_DEFAULT_FALLBACKS:
138         icon->use_default_fallbacks = g_value_get_boolean (value);
139         break;
140 
141       default:
142         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
143     }
144 }
145 
146 static void
g_themed_icon_constructed(GObject * object)147 g_themed_icon_constructed (GObject *object)
148 {
149   GThemedIcon *themed = G_THEMED_ICON (object);
150 
151   g_return_if_fail (themed->names != NULL && themed->names[0] != NULL);
152 
153   if (themed->use_default_fallbacks)
154     {
155       int i = 0, dashes = 0;
156       const char *p;
157       char *dashp;
158       char *last;
159 
160       p = themed->names[0];
161       while (*p)
162         {
163           if (*p == '-')
164             dashes++;
165           p++;
166         }
167 
168       last = g_strdup (themed->names[0]);
169 
170       g_strfreev (themed->names);
171 
172       themed->names = g_new (char *, dashes + 1 + 1);
173       themed->names[i++] = last;
174 
175       while ((dashp = strrchr (last, '-')) != NULL)
176         themed->names[i++] = last = g_strndup (last, dashp - last);
177 
178       themed->names[i++] = NULL;
179     }
180 }
181 
182 static void
g_themed_icon_finalize(GObject * object)183 g_themed_icon_finalize (GObject *object)
184 {
185   GThemedIcon *themed;
186 
187   themed = G_THEMED_ICON (object);
188 
189   g_strfreev (themed->names);
190 
191   G_OBJECT_CLASS (g_themed_icon_parent_class)->finalize (object);
192 }
193 
194 static void
g_themed_icon_class_init(GThemedIconClass * klass)195 g_themed_icon_class_init (GThemedIconClass *klass)
196 {
197   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
198 
199   gobject_class->finalize = g_themed_icon_finalize;
200   gobject_class->constructed = g_themed_icon_constructed;
201   gobject_class->set_property = g_themed_icon_set_property;
202   gobject_class->get_property = g_themed_icon_get_property;
203 
204   /**
205    * GThemedIcon:name:
206    *
207    * The icon name.
208    */
209   g_object_class_install_property (gobject_class, PROP_NAME,
210                                    g_param_spec_string ("name",
211                                                         _("name"),
212                                                         _("The name of the icon"),
213                                                         NULL,
214                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
215 
216   /**
217    * GThemedIcon:names:
218    *
219    * A %NULL-terminated array of icon names.
220    */
221   g_object_class_install_property (gobject_class, PROP_NAMES,
222                                    g_param_spec_boxed ("names",
223                                                        _("names"),
224                                                        _("An array containing the icon names"),
225                                                        G_TYPE_STRV,
226                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
227 
228   /**
229    * GThemedIcon:use-default-fallbacks:
230    *
231    * Whether to use the default fallbacks found by shortening the icon name
232    * at '-' characters. If the "names" array has more than one element,
233    * ignores any past the first.
234    *
235    * For example, if the icon name was "gnome-dev-cdrom-audio", the array
236    * would become
237    * |[
238    * {
239    *   "gnome-dev-cdrom-audio",
240    *   "gnome-dev-cdrom",
241    *   "gnome-dev",
242    *   "gnome",
243    *   NULL
244    * };
245    * ]|
246    */
247   g_object_class_install_property (gobject_class, PROP_USE_DEFAULT_FALLBACKS,
248                                    g_param_spec_boolean ("use-default-fallbacks",
249                                                          _("use default fallbacks"),
250                                                          _("Whether to use default fallbacks found by shortening the name at '-' characters. Ignores names after the first if multiple names are given."),
251                                                          FALSE,
252                                                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
253 }
254 
255 static void
g_themed_icon_init(GThemedIcon * themed)256 g_themed_icon_init (GThemedIcon *themed)
257 {
258   themed->names = NULL;
259 }
260 
261 /**
262  * g_themed_icon_new:
263  * @iconname: a string containing an icon name.
264  *
265  * Creates a new themed icon for @iconname.
266  *
267  * Returns: a new #GThemedIcon.
268  **/
269 GIcon *
g_themed_icon_new(const char * iconname)270 g_themed_icon_new (const char *iconname)
271 {
272   g_return_val_if_fail (iconname != NULL, NULL);
273 
274   return G_ICON (g_object_new (G_TYPE_THEMED_ICON, "name", iconname, NULL));
275 }
276 
277 /**
278  * g_themed_icon_new_from_names:
279  * @iconnames: an array of strings containing icon names.
280  * @len: the length of the @iconnames array, or -1 if @iconnames is
281  *     %NULL-terminated
282  *
283  * Creates a new themed icon for @iconnames.
284  *
285  * Returns: a new #GThemedIcon
286  **/
287 GIcon *
g_themed_icon_new_from_names(char ** iconnames,int len)288 g_themed_icon_new_from_names (char **iconnames,
289                               int    len)
290 {
291   GIcon *icon = icon;
292 
293   g_return_val_if_fail (iconnames != NULL, NULL);
294 
295   if (len >= 0)
296     {
297       char **names;
298       int i;
299 
300       names = g_new (char *, len + 1);
301 
302       for (i = 0; i < len; i++)
303         names[i] = iconnames[i];
304 
305       names[i] = NULL;
306 
307       icon = G_ICON (g_object_new (G_TYPE_THEMED_ICON, "names", names, NULL));
308 
309       g_free (names);
310     }
311   else
312     icon = G_ICON (g_object_new (G_TYPE_THEMED_ICON, "names", iconnames, NULL));
313 
314   return icon;
315 }
316 
317 /**
318  * g_themed_icon_new_with_default_fallbacks:
319  * @iconname: a string containing an icon name
320  *
321  * Creates a new themed icon for @iconname, and all the names
322  * that can be created by shortening @iconname at '-' characters.
323  *
324  * In the following example, @icon1 and @icon2 are equivalent:
325  * |[
326  * const char *names[] = {
327  *   "gnome-dev-cdrom-audio",
328  *   "gnome-dev-cdrom",
329  *   "gnome-dev",
330  *   "gnome"
331  * };
332  *
333  * icon1 = g_themed_icon_new_from_names (names, 4);
334  * icon2 = g_themed_icon_new_with_default_fallbacks ("gnome-dev-cdrom-audio");
335  * ]|
336  *
337  * Returns: a new #GThemedIcon.
338  */
339 GIcon *
g_themed_icon_new_with_default_fallbacks(const char * iconname)340 g_themed_icon_new_with_default_fallbacks (const char *iconname)
341 {
342   g_return_val_if_fail (iconname != NULL, NULL);
343 
344   return G_ICON (g_object_new (G_TYPE_THEMED_ICON, "name", iconname, "use-default-fallbacks", TRUE, NULL));
345 }
346 
347 
348 /**
349  * g_themed_icon_get_names:
350  * @icon: a #GThemedIcon.
351  *
352  * Gets the names of icons from within @icon.
353  *
354  * Returns: a list of icon names.
355  **/
356 const char * const *
g_themed_icon_get_names(GThemedIcon * icon)357 g_themed_icon_get_names (GThemedIcon *icon)
358 {
359   g_return_val_if_fail (G_IS_THEMED_ICON (icon), NULL);
360   return (const char * const *)icon->names;
361 }
362 
363 /**
364  * g_themed_icon_append_name:
365  * @icon: a #GThemedIcon
366  * @iconname: name of icon to append to list of icons from within @icon.
367  *
368  * Append a name to the list of icons from within @icon.
369  *
370  * <note><para>
371  * Note that doing so invalidates the hash computed by prior calls
372  * to g_icon_hash().
373  * </para></note>
374  */
375 void
g_themed_icon_append_name(GThemedIcon * icon,const char * iconname)376 g_themed_icon_append_name (GThemedIcon *icon,
377                            const char  *iconname)
378 {
379   guint num_names;
380 
381   g_return_if_fail (G_IS_THEMED_ICON (icon));
382   g_return_if_fail (iconname != NULL);
383 
384   num_names = g_strv_length (icon->names);
385   icon->names = g_realloc (icon->names, sizeof (char*) * (num_names + 2));
386   icon->names[num_names] = g_strdup (iconname);
387   icon->names[num_names + 1] = NULL;
388 
389   g_object_notify (G_OBJECT (icon), "names");
390 }
391 
392 /**
393  * g_themed_icon_prepend_name:
394  * @icon: a #GThemedIcon
395  * @iconname: name of icon to prepend to list of icons from within @icon.
396  *
397  * Prepend a name to the list of icons from within @icon.
398  *
399  * <note><para>
400  * Note that doing so invalidates the hash computed by prior calls
401  * to g_icon_hash().
402  * </para></note>
403  *
404  * Since: 2.18
405  */
406 void
g_themed_icon_prepend_name(GThemedIcon * icon,const char * iconname)407 g_themed_icon_prepend_name (GThemedIcon *icon,
408                             const char  *iconname)
409 {
410   guint num_names;
411   gchar **names;
412   gint i;
413 
414   g_return_if_fail (G_IS_THEMED_ICON (icon));
415   g_return_if_fail (iconname != NULL);
416 
417   num_names = g_strv_length (icon->names);
418   names = g_new (char*, num_names + 2);
419   for (i = 0; icon->names[i]; i++)
420     names[i + 1] = icon->names[i];
421   names[0] = g_strdup (iconname);
422   names[num_names + 1] = NULL;
423 
424   g_free (icon->names);
425   icon->names = names;
426 
427   g_object_notify (G_OBJECT (icon), "names");
428 }
429 
430 static guint
g_themed_icon_hash(GIcon * icon)431 g_themed_icon_hash (GIcon *icon)
432 {
433   GThemedIcon *themed = G_THEMED_ICON (icon);
434   guint hash;
435   int i;
436 
437   hash = 0;
438 
439   for (i = 0; themed->names[i] != NULL; i++)
440     hash ^= g_str_hash (themed->names[i]);
441 
442   return hash;
443 }
444 
445 static gboolean
g_themed_icon_equal(GIcon * icon1,GIcon * icon2)446 g_themed_icon_equal (GIcon *icon1,
447                      GIcon *icon2)
448 {
449   GThemedIcon *themed1 = G_THEMED_ICON (icon1);
450   GThemedIcon *themed2 = G_THEMED_ICON (icon2);
451   int i;
452 
453   for (i = 0; themed1->names[i] != NULL && themed2->names[i] != NULL; i++)
454     {
455       if (!g_str_equal (themed1->names[i], themed2->names[i]))
456 	return FALSE;
457     }
458 
459   return themed1->names[i] == NULL && themed2->names[i] == NULL;
460 }
461 
462 
463 static gboolean
g_themed_icon_to_tokens(GIcon * icon,GPtrArray * tokens,gint * out_version)464 g_themed_icon_to_tokens (GIcon *icon,
465 			 GPtrArray *tokens,
466                          gint  *out_version)
467 {
468   GThemedIcon *themed_icon = G_THEMED_ICON (icon);
469   int n;
470 
471   g_return_val_if_fail (out_version != NULL, FALSE);
472 
473   *out_version = 0;
474 
475   for (n = 0; themed_icon->names[n] != NULL; n++)
476     g_ptr_array_add (tokens,
477 		     g_strdup (themed_icon->names[n]));
478 
479   return TRUE;
480 }
481 
482 static GIcon *
g_themed_icon_from_tokens(gchar ** tokens,gint num_tokens,gint version,GError ** error)483 g_themed_icon_from_tokens (gchar  **tokens,
484                            gint     num_tokens,
485                            gint     version,
486                            GError **error)
487 {
488   GIcon *icon;
489   gchar **names;
490   int n;
491 
492   icon = NULL;
493 
494   if (version != 0)
495     {
496       g_set_error (error,
497                    G_IO_ERROR,
498                    G_IO_ERROR_INVALID_ARGUMENT,
499                    _("Can't handle version %d of GThemedIcon encoding"),
500                    version);
501       goto out;
502     }
503 
504   names = g_new0 (gchar *, num_tokens + 1);
505   for (n = 0; n < num_tokens; n++)
506     names[n] = tokens[n];
507   names[n] = NULL;
508 
509   icon = g_themed_icon_new_from_names (names, num_tokens);
510   g_free (names);
511 
512  out:
513   return icon;
514 }
515 
516 static void
g_themed_icon_icon_iface_init(GIconIface * iface)517 g_themed_icon_icon_iface_init (GIconIface *iface)
518 {
519   iface->hash = g_themed_icon_hash;
520   iface->equal = g_themed_icon_equal;
521   iface->to_tokens = g_themed_icon_to_tokens;
522   iface->from_tokens = g_themed_icon_from_tokens;
523 }
524 
525 #define __G_THEMED_ICON_C__
526 #include "gioaliasdef.c"
527