Home | History | Annotate | Download | only in gio
      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 (at) 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 
     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
    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
    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
    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
    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
    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 *
    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 *
    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 *
    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 *
    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
    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
    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
    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
    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
    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 *
    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
    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