1 /* GTK - The GIMP Toolkit
2 * gtkfilesystem.c: Abstract file system interfaces
3 * Copyright (C) 2003, Red Hat, Inc.
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.
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.
15 * You should have received a copy of the GNU Lesser General Public
16 * 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.
22 #include "gtkfilesystem.h"
23 #include "gtkicontheme.h"
29 GtkFileTime modification_time;
35 GtkFileIconType icon_type : 4;
41 static void gtk_file_system_base_init (gpointer g_class);
42 static void gtk_file_folder_base_init (gpointer g_class);
45 gtk_file_system_error_quark (void)
47 static GQuark quark = 0;
49 quark = g_quark_from_static_string ("gtk-file-system-error-quark");
53 /*****************************************
55 *****************************************/
57 gtk_file_info_get_type (void)
59 static GType our_type = 0;
62 our_type = g_boxed_type_register_static ("GtkFileInfo",
63 (GBoxedCopyFunc) gtk_file_info_copy,
64 (GBoxedFreeFunc) gtk_file_info_free);
70 gtk_file_info_new (void)
74 info = g_new0 (GtkFileInfo, 1);
80 gtk_file_info_copy (GtkFileInfo *info)
82 GtkFileInfo *new_info;
84 g_return_val_if_fail (info != NULL, NULL);
86 new_info = g_memdup (info, sizeof (GtkFileInfo));
87 if (new_info->display_name)
88 new_info->display_name = g_strdup (new_info->display_name);
89 if (new_info->mime_type)
90 new_info->mime_type = g_strdup (new_info->mime_type);
96 gtk_file_info_free (GtkFileInfo *info)
98 g_return_if_fail (info != NULL);
100 if (info->display_name)
101 g_free (info->display_name);
103 g_free (info->mime_type);
104 if (info->display_key)
105 g_free (info->display_key);
110 G_CONST_RETURN gchar *
111 gtk_file_info_get_display_name (const GtkFileInfo *info)
113 g_return_val_if_fail (info != NULL, NULL);
115 return info->display_name;
119 * gtk_file_info_get_display_key:
120 * @info: a #GtkFileInfo
122 * Returns results of g_utf8_collate_key() on the display name
123 * for @info. This is useful when sorting a bunch of #GtkFileInfo
124 * structures since the collate key will be only computed once.
126 * Return value: The collate key for the display name, or %NULL
127 * if the display name hasn't been set.
129 G_CONST_RETURN gchar *
130 gtk_file_info_get_display_key (const GtkFileInfo *info)
132 g_return_val_if_fail (info != NULL, NULL);
134 if (!info->display_key && info->display_name)
136 /* Since info->display_key is only a cache, we cast off the const
138 ((GtkFileInfo *)info)->display_key = g_utf8_collate_key (info->display_name, -1);
141 return info->display_key;
145 gtk_file_info_set_display_name (GtkFileInfo *info,
146 const gchar *display_name)
148 g_return_if_fail (info != NULL);
150 if (info->display_name)
151 g_free (info->display_name);
152 if (info->display_key)
154 g_free (info->display_key);
155 info->display_key = NULL;
158 info->display_name = g_strdup (display_name);
162 gtk_file_info_get_is_folder (const GtkFileInfo *info)
164 g_return_val_if_fail (info != NULL, FALSE);
166 return info->is_folder;
170 gtk_file_info_set_is_folder (GtkFileInfo *info,
173 g_return_if_fail (info != NULL);
175 info->is_folder = is_folder != FALSE;
179 gtk_file_info_get_is_hidden (const GtkFileInfo *info)
181 g_return_val_if_fail (info != NULL, FALSE);
183 return info->is_hidden;
187 gtk_file_info_set_is_hidden (GtkFileInfo *info,
190 g_return_if_fail (info != NULL);
192 info->is_hidden = is_hidden != FALSE;
195 G_CONST_RETURN gchar *
196 gtk_file_info_get_mime_type (const GtkFileInfo *info)
198 g_return_val_if_fail (info != NULL, NULL);
200 return info->mime_type;
204 gtk_file_info_set_mime_type (GtkFileInfo *info,
205 const gchar *mime_type)
207 g_return_if_fail (info != NULL);
210 g_free (info->mime_type);
212 info->mime_type = g_strdup (mime_type);
216 gtk_file_info_get_modification_time (const GtkFileInfo *info)
218 g_return_val_if_fail (info != NULL, 0);
220 return info->modification_time;
224 gtk_file_info_set_modification_time (GtkFileInfo *info,
225 GtkFileTime modification_time)
227 g_return_if_fail (info != NULL);
229 info->modification_time = modification_time;
233 gtk_file_info_get_size (const GtkFileInfo *info)
235 g_return_val_if_fail (info != NULL, 0);
241 gtk_file_info_set_size (GtkFileInfo *info,
244 g_return_if_fail (info != NULL);
245 g_return_if_fail (size >= 0);
252 gtk_file_info_set_icon_type (GtkFileInfo *info,
253 GtkFileIconType icon_type)
255 g_return_if_fail (info != NULL);
257 info->icon_type = icon_type;
261 gtk_file_info_get_icon_type (const GtkFileInfo *info)
263 g_return_val_if_fail (info != NULL, GTK_FILE_ICON_REGULAR);
265 return info->icon_type;
268 typedef struct _IconCacheElement IconCacheElement;
270 struct _IconCacheElement
277 icon_cache_element_free (IconCacheElement *element)
280 g_object_unref (element->pixbuf);
285 icon_theme_changed (GtkIconTheme *icon_theme)
289 /* Difference from the initial creation is that we don't
290 * reconnect the signal
292 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
293 (GDestroyNotify)g_free,
294 (GDestroyNotify)icon_cache_element_free);
295 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
296 cache, (GDestroyNotify)g_hash_table_destroy);
300 get_cached_icon (GtkWidget *widget,
304 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
305 GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
306 IconCacheElement *element;
310 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
311 (GDestroyNotify)g_free,
312 (GDestroyNotify)icon_cache_element_free);
314 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
315 cache, (GDestroyNotify)g_hash_table_destroy);
316 g_signal_connect (icon_theme, "changed",
317 G_CALLBACK (icon_theme_changed), NULL);
320 element = g_hash_table_lookup (cache, name);
323 element = g_new0 (IconCacheElement, 1);
324 g_hash_table_insert (cache, g_strdup (name), element);
327 if (element->size != pixel_size)
330 g_object_unref (element->pixbuf);
331 element->size = pixel_size;
332 element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
333 pixel_size, 0, NULL);
336 return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
341 gtk_file_info_render_icon (const GtkFileInfo *info,
345 const gchar *separator;
349 g_return_val_if_fail (info != NULL, NULL);
350 g_return_val_if_fail (widget != NULL, NULL);
351 g_return_val_if_fail (pixel_size > 0, NULL);
353 if (info->icon_type != GTK_FILE_ICON_REGULAR)
355 const char *name = NULL; /* Quiet gcc */
357 switch (info->icon_type)
359 case GTK_FILE_ICON_BLOCK_DEVICE:
360 name ="gnome-fs-blockdev";
362 case GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK:
363 name = "gnome-fs-symlink";
365 case GTK_FILE_ICON_CHARACTER_DEVICE:
366 name = "gnome-fs-chardev";
368 case GTK_FILE_ICON_DIRECTORY:
369 name = "gnome-fs-directory";
371 case GTK_FILE_ICON_EXECUTABLE:
372 name ="gnome-fs-executable";
374 case GTK_FILE_ICON_FIFO:
375 name = "gnome-fs-fifo";
377 case GTK_FILE_ICON_SOCKET:
378 name = "gnome-fs-socket";
380 case GTK_FILE_ICON_REGULAR:
381 g_assert_not_reached ();
384 return get_cached_icon (widget, name, pixel_size);
387 if (!info->mime_type)
390 separator = strchr (info->mime_type, '/');
394 icon_name = g_string_new ("gnome-mime-");
395 g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
396 g_string_append_c (icon_name, '-');
397 g_string_append (icon_name, separator + 1);
398 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
399 g_string_free (icon_name, TRUE);
403 icon_name = g_string_new ("gnome-mime-");
404 g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
405 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
406 g_string_free (icon_name, TRUE);
410 return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
414 /*****************************************
416 *****************************************/
418 gtk_file_system_get_type (void)
420 static GType file_system_type = 0;
422 if (!file_system_type)
424 static const GTypeInfo file_system_info =
426 sizeof (GtkFileSystemIface), /* class_size */
427 gtk_file_system_base_init, /* base_init */
428 NULL, /* base_finalize */
431 file_system_type = g_type_register_static (G_TYPE_INTERFACE,
433 &file_system_info, 0);
435 g_type_interface_add_prerequisite (file_system_type, G_TYPE_OBJECT);
438 return file_system_type;
442 gtk_file_system_base_init (gpointer g_class)
444 static gboolean initialized = FALSE;
448 GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
450 g_signal_new ("roots-changed",
453 G_STRUCT_OFFSET (GtkFileSystemIface, roots_changed),
455 g_cclosure_marshal_VOID__VOID,
457 g_signal_new ("bookmarks-changed",
460 G_STRUCT_OFFSET (GtkFileSystemIface, bookmarks_changed),
462 g_cclosure_marshal_VOID__VOID,
470 gtk_file_system_list_roots (GtkFileSystem *file_system)
472 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
474 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_roots (file_system);
478 gtk_file_system_get_root_info (GtkFileSystem *file_system,
479 const GtkFilePath *path,
480 GtkFileInfoType types,
483 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
484 g_return_val_if_fail (path != NULL, NULL);
485 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
487 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_root_info (file_system, path, types, error);
491 gtk_file_system_get_folder (GtkFileSystem *file_system,
492 const GtkFilePath *path,
493 GtkFileInfoType types,
496 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
497 g_return_val_if_fail (path != NULL, NULL);
498 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
500 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, error);
504 gtk_file_system_create_folder(GtkFileSystem *file_system,
505 const GtkFilePath *path,
508 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
509 g_return_val_if_fail (path != NULL, FALSE);
510 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
512 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, error);
516 * gtk_file_system_get_parent:
517 * @file_system: a #GtkFileSystem
518 * @path: base path name
519 * @parent: location to store parent path name
520 * @error: location to store error, or %NULL
522 * Gets the name of the parent folder of a file.
524 * Return value: TRUE if the operation was successful; note that in this case @parent
525 * can be returned as %NULL if the base @path has no parent folder (i.e. if it is
526 * already a file system root). If the operation fails, this function returns FALSE
527 * and sets the @error value if it is specified.
530 gtk_file_system_get_parent (GtkFileSystem *file_system,
531 const GtkFilePath *path,
532 GtkFilePath **parent,
535 GtkFilePath *tmp_parent = NULL;
538 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
539 g_return_val_if_fail (path != NULL, FALSE);
540 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
542 result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, path, &tmp_parent, error);
543 g_assert (result || tmp_parent == NULL);
546 *parent = tmp_parent;
548 gtk_file_path_free (tmp_parent);
554 gtk_file_system_make_path (GtkFileSystem *file_system,
555 const GtkFilePath *base_path,
556 const gchar *display_name,
559 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
560 g_return_val_if_fail (base_path != NULL, NULL);
561 g_return_val_if_fail (display_name != NULL, NULL);
562 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
564 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_path (file_system, base_path, display_name, error);
568 * gtk_file_system_parse:
569 * @file_system: a #GtkFileSystem
570 * @base_path: reference folder with respect to which relative
571 * paths should be interpreted.
572 * @str: the string to parse
573 * @folder: location to store folder portion of result, or %NULL
574 * @file_part: location to store file portion of result, or %NULL
575 * @error: location to store error, or %NULL
577 * Given a string entered by a user, parse it (possibly using
578 * heuristics) into a folder path and a UTF-8 encoded
579 * filename part. (Suitable for passing to gtk_file_system_make_path())
581 * Note that the returned filename point may point to a subfolder
582 * of the returned folder. Adding a trailing path separator is needed
583 * to enforce the interpretation as a folder name.
585 * If parsing fails because the syntax of @str is not understood,
586 * and error of type GTK_FILE_SYSTEM_ERROR_BAD_FILENAME will
587 * be set in @error and %FALSE returned.
589 * If parsing fails because a path was encountered that doesn't
590 * exist on the filesystem, then an error of type
591 * %GTK_FILE_SYSTEM_ERROR_NONEXISTENT will be set in @error
592 * and %FALSE returned. (This only applies to parsing relative paths,
593 * not to interpretation of @file_part. No check is made as
594 * to whether @file_part exists.)
596 * Return value: %TRUE if the parsing succeeds, otherwise, %FALSE.
599 gtk_file_system_parse (GtkFileSystem *file_system,
600 const GtkFilePath *base_path,
602 GtkFilePath **folder,
606 GtkFilePath *tmp_folder = NULL;
607 gchar *tmp_file_part = NULL;
610 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
611 g_return_val_if_fail (base_path != NULL, FALSE);
612 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
615 result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->parse (file_system, base_path, str,
616 &tmp_folder, &tmp_file_part,
618 g_assert (result || (tmp_folder == NULL && tmp_file_part == NULL));
621 *folder = tmp_folder;
623 gtk_file_path_free (tmp_folder);
626 *file_part = tmp_file_part;
628 g_free (tmp_file_part);
635 gtk_file_system_path_to_uri (GtkFileSystem *file_system,
636 const GtkFilePath *path)
638 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
639 g_return_val_if_fail (path != NULL, NULL);
641 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_uri (file_system, path);
645 gtk_file_system_path_to_filename (GtkFileSystem *file_system,
646 const GtkFilePath *path)
648 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
649 g_return_val_if_fail (path != NULL, NULL);
651 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_filename (file_system, path);
655 gtk_file_system_uri_to_path (GtkFileSystem *file_system,
658 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
659 g_return_val_if_fail (uri != NULL, NULL);
661 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->uri_to_path (file_system, uri);
665 gtk_file_system_filename_to_path (GtkFileSystem *file_system,
666 const gchar *filename)
668 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
669 g_return_val_if_fail (filename != NULL, NULL);
671 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->filename_to_path (file_system, filename);
675 gtk_file_system_render_icon (GtkFileSystem *file_system,
676 const GtkFilePath *path,
681 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
682 g_return_val_if_fail (path != NULL, NULL);
683 g_return_val_if_fail (widget != NULL, NULL);
684 g_return_val_if_fail (pixel_size > 0, NULL);
686 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->render_icon (file_system, path, widget, pixel_size, error);
690 * gtk_file_system_add_bookmark:
691 * @file_system: a #GtkFileSystem
692 * @bookmark: path of the bookmark to add
693 * @error: location to store error, or %NULL
695 * Adds a bookmark folder to the user's bookmarks list. If the operation succeeds,
696 * the "bookmarks_changed" signal will be emitted.
698 * Return value: TRUE if the operation succeeds, FALSE otherwise. In the latter case,
699 * the @error value will be set.
702 gtk_file_system_add_bookmark (GtkFileSystem *file_system,
703 const GtkFilePath *path,
706 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
707 g_return_val_if_fail (path != NULL, FALSE);
709 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->add_bookmark (file_system, path, error);
713 * gtk_file_system_remove_bookmark:
714 * @file_system: a #GtkFileSystem
715 * @bookmark: path of the bookmark to remove
716 * @error: location to store error, or %NULL
718 * Removes a bookmark folder from the user's bookmarks list. If the operation
719 * succeeds, the "bookmarks_changed" signal will be emitted.
721 * Return value: TRUE if the operation succeeds, FALSE otherwise. In the latter
722 * case, the @error value will be set.
725 gtk_file_system_remove_bookmark (GtkFileSystem *file_system,
726 const GtkFilePath *path,
729 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
730 g_return_val_if_fail (path != NULL, FALSE);
732 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->remove_bookmark (file_system, path, error);
736 * gtk_file_system_list_bookmarks:
737 * @file_system: a #GtkFileSystem
739 * Queries the list of bookmarks in the file system.
741 * Return value: A list of #GtkFilePath, or NULL if there are no configured
742 * bookmarks. You should use gtk_file_paths_free() to free this list.
744 * See also: gtk_file_system_get_supports_bookmarks()
747 gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
749 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
751 return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_bookmarks (file_system);
754 /*****************************************
756 *****************************************/
758 gtk_file_folder_get_type (void)
760 static GType file_folder_type = 0;
762 if (!file_folder_type)
764 static const GTypeInfo file_folder_info =
766 sizeof (GtkFileFolderIface), /* class_size */
767 gtk_file_folder_base_init, /* base_init */
768 NULL, /* base_finalize */
771 file_folder_type = g_type_register_static (G_TYPE_INTERFACE,
773 &file_folder_info, 0);
775 g_type_interface_add_prerequisite (file_folder_type, G_TYPE_OBJECT);
778 return file_folder_type;
782 gtk_file_folder_base_init (gpointer g_class)
784 static gboolean initialized = FALSE;
788 GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
790 g_signal_new ("deleted",
793 G_STRUCT_OFFSET (GtkFileFolderIface, deleted),
795 g_cclosure_marshal_VOID__VOID,
797 g_signal_new ("files-added",
800 G_STRUCT_OFFSET (GtkFileFolderIface, files_added),
802 g_cclosure_marshal_VOID__POINTER,
805 g_signal_new ("files-changed",
808 G_STRUCT_OFFSET (GtkFileFolderIface, files_changed),
810 g_cclosure_marshal_VOID__POINTER,
813 g_signal_new ("files-removed",
816 G_STRUCT_OFFSET (GtkFileFolderIface, files_removed),
818 g_cclosure_marshal_VOID__POINTER,
827 gtk_file_folder_list_children (GtkFileFolder *folder,
832 GSList *tmp_children = NULL;
834 g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE);
835 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
837 result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error);
838 g_assert (result || tmp_children == NULL);
841 *children = tmp_children;
843 gtk_file_paths_free (tmp_children);
849 gtk_file_folder_get_info (GtkFileFolder *folder,
850 const GtkFilePath *path,
853 g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL);
854 g_return_val_if_fail (path != NULL, NULL);
855 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
857 return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, path, error);
861 gtk_file_paths_sort (GSList *paths)
863 return g_slist_sort (paths, (GCompareFunc)strcmp);
867 * gtk_file_paths_copy:
868 * @paths: A #GSList of 3GtkFilePath structures.
870 * Copies a list of #GtkFilePath structures.
872 * Return value: A copy of @paths. Since the contents of the list are copied as
873 * well, you should use gtk_file_paths_free() to free the result.
876 gtk_file_paths_copy (GSList *paths)
878 GSList *head, *tail, *l;
882 for (l = paths; l; l = l->next)
888 node = g_slist_alloc ();
895 node->data = gtk_file_path_copy (path);
903 gtk_file_paths_free (GSList *paths)
907 for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next)
908 gtk_file_path_free (tmp_list->data);
910 g_slist_free (paths);