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 #include <stdlib.h> 25 #include <string.h> 26 27 #include "gicon.h" 28 #include "gthemedicon.h" 29 #include "gfileicon.h" 30 #include "gemblemedicon.h" 31 #include "gfile.h" 32 #include "gioerror.h" 33 34 #include "glibintl.h" 35 36 #include "gioalias.h" 37 38 /* There versioning of this is implicit, version 1 would be ".1 " */ 39 #define G_ICON_SERIALIZATION_MAGIC0 ". " 40 41 /** 42 * SECTION:gicon 43 * @short_description: Interface for icons 44 * @include: gio/gio.h 45 * 46 * #GIcon is a very minimal interface for icons. It provides functions 47 * for checking the equality of two icons, hashing of icons and 48 * serializing an icon to and from strings. 49 * 50 * #GIcon does not provide the actual pixmap for the icon as this is out 51 * of GIO's scope, however implementations of #GIcon may contain the name 52 * of an icon (see #GThemedIcon), or the path to an icon (see #GLoadableIcon). 53 * 54 * To obtain a hash of a #GIcon, see g_icon_hash(). 55 * 56 * To check if two #GIcons are equal, see g_icon_equal(). 57 * 58 * For serializing a #GIcon, use g_icon_to_string() and 59 * g_icon_new_for_string(). 60 * 61 * If your application or library provides one or more #GIcon 62 * implementations you need to ensure that each #GType is registered 63 * with the type system prior to calling g_icon_new_for_string(). 64 **/ 65 66 static void g_icon_base_init (gpointer g_class); 67 static void g_icon_class_init (gpointer g_class, 68 gpointer class_data); 69 70 GType 71 g_icon_get_type (void) 72 { 73 static volatile gsize g_define_type_id__volatile = 0; 74 75 if (g_once_init_enter (&g_define_type_id__volatile)) 76 { 77 const GTypeInfo icon_info = 78 { 79 sizeof (GIconIface), /* class_size */ 80 g_icon_base_init, /* base_init */ 81 NULL, /* base_finalize */ 82 g_icon_class_init, 83 NULL, /* class_finalize */ 84 NULL, /* class_data */ 85 0, 86 0, /* n_preallocs */ 87 NULL 88 }; 89 GType g_define_type_id = 90 g_type_register_static (G_TYPE_INTERFACE, I_("GIcon"), 91 &icon_info, 0); 92 93 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT); 94 95 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); 96 } 97 98 return g_define_type_id__volatile; 99 } 100 101 static void 102 g_icon_class_init (gpointer g_class, 103 gpointer class_data) 104 { 105 } 106 107 static void 108 g_icon_base_init (gpointer g_class) 109 { 110 } 111 112 /** 113 * g_icon_hash: 114 * @icon: #gconstpointer to an icon object. 115 * 116 * Gets a hash for an icon. 117 * 118 * Returns: a #guint containing a hash for the @icon, suitable for 119 * use in a #GHashTable or similar data structure. 120 **/ 121 guint 122 g_icon_hash (gconstpointer icon) 123 { 124 GIconIface *iface; 125 126 g_return_val_if_fail (G_IS_ICON (icon), 0); 127 128 iface = G_ICON_GET_IFACE (icon); 129 130 return (* iface->hash) ((GIcon *)icon); 131 } 132 133 /** 134 * g_icon_equal: 135 * @icon1: pointer to the first #GIcon. 136 * @icon2: pointer to the second #GIcon. 137 * 138 * Checks if two icons are equal. 139 * 140 * Returns: %TRUE if @icon1 is equal to @icon2. %FALSE otherwise. 141 **/ 142 gboolean 143 g_icon_equal (GIcon *icon1, 144 GIcon *icon2) 145 { 146 GIconIface *iface; 147 148 if (icon1 == NULL && icon2 == NULL) 149 return TRUE; 150 151 if (icon1 == NULL || icon2 == NULL) 152 return FALSE; 153 154 if (G_TYPE_FROM_INSTANCE (icon1) != G_TYPE_FROM_INSTANCE (icon2)) 155 return FALSE; 156 157 iface = G_ICON_GET_IFACE (icon1); 158 159 return (* iface->equal) (icon1, icon2); 160 } 161 162 static gboolean 163 g_icon_to_string_tokenized (GIcon *icon, GString *s) 164 { 165 char *ret; 166 GPtrArray *tokens; 167 gint version; 168 GIconIface *icon_iface; 169 int i; 170 171 g_return_val_if_fail (icon != NULL, FALSE); 172 g_return_val_if_fail (G_IS_ICON (icon), FALSE); 173 174 ret = NULL; 175 176 icon_iface = G_ICON_GET_IFACE (icon); 177 if (icon_iface->to_tokens == NULL) 178 return FALSE; 179 180 tokens = g_ptr_array_new (); 181 if (!icon_iface->to_tokens (icon, tokens, &version)) 182 { 183 g_ptr_array_free (tokens, TRUE); 184 return FALSE; 185 } 186 187 /* format: TypeName[.Version] <token_0> .. <token_N-1> 188 version 0 is implicit and can be omitted 189 all the tokens are url escaped to ensure they have no spaces in them */ 190 191 g_string_append (s, g_type_name_from_instance ((GTypeInstance *)icon)); 192 if (version != 0) 193 g_string_append_printf (s, ".%d", version); 194 195 for (i = 0; i < tokens->len; i++) 196 { 197 char *token; 198 199 token = g_ptr_array_index (tokens, i); 200 201 g_string_append_c (s, ' '); 202 /* We really only need to escape spaces here, so allow lots of otherwise reserved chars */ 203 g_string_append_uri_escaped (s, token, 204 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE); 205 206 g_free (token); 207 } 208 209 g_ptr_array_free (tokens, TRUE); 210 211 return TRUE; 212 } 213 214 /** 215 * g_icon_to_string: 216 * @icon: a #GIcon. 217 * 218 * Generates a textual representation of @icon that can be used for 219 * serialization such as when passing @icon to a different process or 220 * saving it to persistent storage. Use g_icon_new_for_string() to 221 * get @icon back from the returned string. 222 * 223 * The encoding of the returned string is proprietary to #GIcon except 224 * in the following two cases 225 * 226 * <itemizedlist> 227 * <listitem><para> 228 * If @icon is a #GFileIcon, the returned string is a native path 229 * (such as <literal>/path/to/my icon.png</literal>) without escaping 230 * if the #GFile for @icon is a native file. If the file is not 231 * native, the returned string is the result of g_file_get_uri() 232 * (such as <literal>sftp://path/to/my%%20icon.png</literal>). 233 * </para></listitem> 234 * <listitem><para> 235 * If @icon is a #GThemedIcon with exactly one name, the encoding is 236 * simply the name (such as <literal>network-server</literal>). 237 * </para></listitem> 238 * </itemizedlist> 239 * 240 * Returns: An allocated NUL-terminated UTF8 string or %NULL if @icon can't 241 * be serialized. Use g_free() to free. 242 * 243 * Since: 2.20 244 */ 245 gchar * 246 g_icon_to_string (GIcon *icon) 247 { 248 gchar *ret; 249 250 g_return_val_if_fail (icon != NULL, NULL); 251 g_return_val_if_fail (G_IS_ICON (icon), NULL); 252 253 ret = NULL; 254 255 if (G_IS_FILE_ICON (icon)) 256 { 257 GFile *file; 258 259 file = g_file_icon_get_file (G_FILE_ICON (icon)); 260 if (g_file_is_native (file)) 261 { 262 ret = g_file_get_path (file); 263 if (!g_utf8_validate (ret, -1, NULL)) 264 { 265 g_free (ret); 266 ret = NULL; 267 } 268 } 269 else 270 ret = g_file_get_uri (file); 271 } 272 else if (G_IS_THEMED_ICON (icon)) 273 { 274 const char * const *names; 275 276 names = g_themed_icon_get_names (G_THEMED_ICON (icon)); 277 if (names != NULL && 278 names[0] != NULL && 279 names[0][0] != '.' && /* Allowing icons starting with dot would break G_ICON_SERIALIZATION_MAGIC0 */ 280 g_utf8_validate (names[0], -1, NULL) && /* Only return utf8 strings */ 281 names[1] == NULL) 282 ret = g_strdup (names[0]); 283 } 284 285 if (ret == NULL) 286 { 287 GString *s; 288 289 s = g_string_new (G_ICON_SERIALIZATION_MAGIC0); 290 291 if (g_icon_to_string_tokenized (icon, s)) 292 ret = g_string_free (s, FALSE); 293 else 294 g_string_free (s, TRUE); 295 } 296 297 return ret; 298 } 299 300 static GIcon * 301 g_icon_new_from_tokens (char **tokens, 302 GError **error) 303 { 304 GIcon *icon; 305 char *typename, *version_str; 306 GType type; 307 gpointer klass; 308 GIconIface *icon_iface; 309 gint version; 310 char *endp; 311 int num_tokens; 312 int i; 313 314 icon = NULL; 315 klass = NULL; 316 317 num_tokens = g_strv_length (tokens); 318 319 if (num_tokens < 1) 320 { 321 g_set_error (error, 322 G_IO_ERROR, 323 G_IO_ERROR_INVALID_ARGUMENT, 324 _("Wrong number of tokens (%d)"), 325 num_tokens); 326 goto out; 327 } 328 329 typename = tokens[0]; 330 version_str = strchr (typename, '.'); 331 if (version_str) 332 { 333 *version_str = 0; 334 version_str += 1; 335 } 336 337 338 type = g_type_from_name (tokens[0]); 339 if (type == 0) 340 { 341 g_set_error (error, 342 G_IO_ERROR, 343 G_IO_ERROR_INVALID_ARGUMENT, 344 _("No type for class name %s"), 345 tokens[0]); 346 goto out; 347 } 348 349 if (!g_type_is_a (type, G_TYPE_ICON)) 350 { 351 g_set_error (error, 352 G_IO_ERROR, 353 G_IO_ERROR_INVALID_ARGUMENT, 354 _("Type %s does not implement the GIcon interface"), 355 tokens[0]); 356 goto out; 357 } 358 359 klass = g_type_class_ref (type); 360 if (klass == NULL) 361 { 362 g_set_error (error, 363 G_IO_ERROR, 364 G_IO_ERROR_INVALID_ARGUMENT, 365 _("Type %s is not classed"), 366 tokens[0]); 367 goto out; 368 } 369 370 version = 0; 371 if (version_str) 372 { 373 version = strtol (version_str, &endp, 10); 374 if (endp == NULL || *endp != '\0') 375 { 376 g_set_error (error, 377 G_IO_ERROR, 378 G_IO_ERROR_INVALID_ARGUMENT, 379 _("Malformed version number: %s"), 380 version_str); 381 goto out; 382 } 383 } 384 385 icon_iface = g_type_interface_peek (klass, G_TYPE_ICON); 386 g_assert (icon_iface != NULL); 387 388 if (icon_iface->from_tokens == NULL) 389 { 390 g_set_error (error, 391 G_IO_ERROR, 392 G_IO_ERROR_INVALID_ARGUMENT, 393 _("Type %s does not implement from_tokens() on the GIcon interface"), 394 tokens[0]); 395 goto out; 396 } 397 398 for (i = 1; i < num_tokens; i++) 399 { 400 char *escaped; 401 402 escaped = tokens[i]; 403 tokens[i] = g_uri_unescape_string (escaped, NULL); 404 g_free (escaped); 405 } 406 407 icon = icon_iface->from_tokens (tokens + 1, num_tokens - 1, version, error); 408 409 out: 410 if (klass != NULL) 411 g_type_class_unref (klass); 412 return icon; 413 } 414 415 static void 416 ensure_builtin_icon_types (void) 417 { 418 static volatile GType t; 419 t = g_themed_icon_get_type (); 420 t = g_file_icon_get_type (); 421 t = g_emblemed_icon_get_type (); 422 t = g_emblem_get_type (); 423 } 424 425 /** 426 * g_icon_new_for_string: 427 * @str: A string obtained via g_icon_to_string(). 428 * @error: Return location for error. 429 * 430 * Generate a #GIcon instance from @str. This function can fail if 431 * @str is not valid - see g_icon_to_string() for discussion. 432 * 433 * If your application or library provides one or more #GIcon 434 * implementations you need to ensure that each #GType is registered 435 * with the type system prior to calling g_icon_new_for_string(). 436 * 437 * Returns: An object implementing the #GIcon interface or %NULL if 438 * @error is set. 439 * 440 * Since: 2.20 441 **/ 442 GIcon * 443 g_icon_new_for_string (const gchar *str, 444 GError **error) 445 { 446 GIcon *icon; 447 448 g_return_val_if_fail (str != NULL, NULL); 449 450 ensure_builtin_icon_types (); 451 452 icon = NULL; 453 454 if (*str == '.') 455 { 456 if (g_str_has_prefix (str, G_ICON_SERIALIZATION_MAGIC0)) 457 { 458 gchar **tokens; 459 460 /* handle tokenized encoding */ 461 tokens = g_strsplit (str + sizeof (G_ICON_SERIALIZATION_MAGIC0) - 1, " ", 0); 462 icon = g_icon_new_from_tokens (tokens, error); 463 g_strfreev (tokens); 464 } 465 else 466 g_set_error_literal (error, 467 G_IO_ERROR, 468 G_IO_ERROR_INVALID_ARGUMENT, 469 _("Can't handle the supplied version the icon encoding")); 470 } 471 else 472 { 473 gchar *scheme; 474 475 /* handle special GFileIcon and GThemedIcon cases */ 476 scheme = g_uri_parse_scheme (str); 477 if (scheme != NULL || str[0] == '/') 478 { 479 GFile *location; 480 location = g_file_new_for_commandline_arg (str); 481 icon = g_file_icon_new (location); 482 g_object_unref (location); 483 } 484 else 485 icon = g_themed_icon_new (str); 486 g_free (scheme); 487 } 488 489 return icon; 490 } 491 492 493 #define __G_ICON_C__ 494 #include "gioaliasdef.c" 495