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