1 /* GTK - The GIMP Toolkit
2 * gtkfilesystem.c: Filesystem abstraction functions.
3 * Copyright (C) 2003, Red Hat, Inc.
4 * Copyright (C) 2007-2008 Carlos Garnacho
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20 * Authors: Carlos Garnacho <carlos@imendio.com>
24 #include <glib/gi18n.h>
27 #include "gtkfilesystem.h"
28 #include "gtkprivate.h"
30 /* #define DEBUG_MODE */
32 #define DEBUG(x...) g_debug (x);
37 #define GTK_FILE_SYSTEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FILE_SYSTEM, GtkFileSystemPrivate))
38 #define GTK_FOLDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FOLDER, GtkFolderPrivate))
39 #define FILES_PER_QUERY 100
41 /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
42 * really pointers to GDrive, GVolume or GMount objects. We need an extra
43 * token for the fake "File System" volume. So, we'll return a pointer to
44 * this particular string.
46 static const gchar *root_volume_token = N_("File System");
47 #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
71 static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
72 static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, };
74 typedef struct GtkFileSystemPrivate GtkFileSystemPrivate;
75 typedef struct GtkFolderPrivate GtkFolderPrivate;
76 typedef struct AsyncFuncData AsyncFuncData;
78 struct GtkFileSystemPrivate
80 GVolumeMonitor *volume_monitor;
82 /* This list contains elements that can be
83 * of type GDrive, GVolume and GMount
87 /* This list contains GtkFileSystemBookmark structs */
90 GFileMonitor *bookmarks_monitor;
93 struct GtkFolderPrivate
97 GFileMonitor *directory_monitor;
98 GFileEnumerator *enumerator;
99 GCancellable *cancellable;
102 guint finished_loading : 1;
107 GtkFileSystem *file_system;
110 GCancellable *cancellable;
117 struct GtkFileSystemBookmark
123 G_DEFINE_TYPE (GtkFileSystem, gtk_file_system, G_TYPE_OBJECT)
125 G_DEFINE_TYPE (GtkFolder, gtk_folder, G_TYPE_OBJECT)
128 static void _gtk_folder_set_finished_loading (GtkFolder *folder,
129 gboolean finished_loading);
130 static void _gtk_folder_add_file (GtkFolder *folder,
136 gtk_file_system_error_quark (void)
138 return g_quark_from_static_string ("gtk-file-system-error-quark");
141 /* GtkFileSystemBookmark methods */
143 gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
145 g_object_unref (bookmark->file);
146 g_free (bookmark->label);
147 g_slice_free (GtkFileSystemBookmark, bookmark);
150 /* GtkFileSystem methods */
152 volumes_changed (GVolumeMonitor *volume_monitor,
156 GtkFileSystem *file_system;
158 gdk_threads_enter ();
160 file_system = GTK_FILE_SYSTEM (user_data);
161 g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
162 gdk_threads_leave ();
166 gtk_file_system_dispose (GObject *object)
168 GtkFileSystemPrivate *priv;
172 priv = GTK_FILE_SYSTEM_GET_PRIVATE (object);
176 g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
177 g_slist_free (priv->volumes);
178 priv->volumes = NULL;
181 if (priv->volume_monitor)
183 g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
184 g_object_unref (priv->volume_monitor);
185 priv->volume_monitor = NULL;
188 G_OBJECT_CLASS (gtk_file_system_parent_class)->dispose (object);
192 gtk_file_system_finalize (GObject *object)
194 GtkFileSystemPrivate *priv;
198 priv = GTK_FILE_SYSTEM_GET_PRIVATE (object);
200 if (priv->bookmarks_monitor)
201 g_object_unref (priv->bookmarks_monitor);
205 g_slist_foreach (priv->bookmarks, (GFunc) gtk_file_system_bookmark_free, NULL);
206 g_slist_free (priv->bookmarks);
209 G_OBJECT_CLASS (gtk_file_system_parent_class)->finalize (object);
213 gtk_file_system_class_init (GtkFileSystemClass *class)
215 GObjectClass *object_class = G_OBJECT_CLASS (class);
217 object_class->dispose = gtk_file_system_dispose;
218 object_class->finalize = gtk_file_system_finalize;
220 fs_signals[BOOKMARKS_CHANGED] =
221 g_signal_new ("bookmarks-changed",
222 G_TYPE_FROM_CLASS (object_class),
224 G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
226 g_cclosure_marshal_VOID__VOID,
229 fs_signals[VOLUMES_CHANGED] =
230 g_signal_new ("volumes-changed",
231 G_TYPE_FROM_CLASS (object_class),
233 G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
235 g_cclosure_marshal_VOID__VOID,
238 g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
242 get_bookmarks_file (void)
247 filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
248 file = g_file_new_for_path (filename);
255 read_bookmarks (GFile *file)
258 gchar **lines, *space;
259 GSList *bookmarks = NULL;
262 if (!g_file_load_contents (file, NULL, &contents,
266 lines = g_strsplit (contents, "\n", -1);
268 for (i = 0; lines[i]; i++)
270 GtkFileSystemBookmark *bookmark;
275 bookmark = g_slice_new0 (GtkFileSystemBookmark);
277 if ((space = strchr (lines[i], ' ')) != NULL)
280 bookmark->label = g_strdup (space + 1);
283 bookmark->file = g_file_new_for_uri (lines[i]);
284 bookmarks = g_slist_prepend (bookmarks, bookmark);
287 bookmarks = g_slist_reverse (bookmarks);
295 save_bookmarks (GFile *bookmarks_file,
298 GError *error = NULL;
301 contents = g_string_new ("");
305 GtkFileSystemBookmark *bookmark;
308 bookmark = bookmarks->data;
309 uri = g_file_get_uri (bookmark->file);
310 g_string_append (contents, uri);
313 g_string_append_printf (contents, " %s", bookmark->label);
315 g_string_append_c (contents, '\n');
316 bookmarks = bookmarks->next;
320 if (!g_file_replace_contents (bookmarks_file,
322 strlen (contents->str),
323 NULL, FALSE, 0, NULL,
326 g_critical (error->message);
327 g_error_free (error);
330 g_string_free (contents, TRUE);
334 bookmarks_file_changed (GFileMonitor *monitor,
337 GFileMonitorEvent event,
340 GtkFileSystemPrivate *priv;
342 priv = GTK_FILE_SYSTEM_GET_PRIVATE (data);
346 case G_FILE_MONITOR_EVENT_CHANGED:
347 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
348 case G_FILE_MONITOR_EVENT_CREATED:
349 case G_FILE_MONITOR_EVENT_DELETED:
350 g_slist_foreach (priv->bookmarks, (GFunc) gtk_file_system_bookmark_free, NULL);
351 g_slist_free (priv->bookmarks);
353 priv->bookmarks = read_bookmarks (file);
355 gdk_threads_enter ();
356 g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
357 gdk_threads_leave ();
360 /* ignore at the moment */
366 get_volumes_list (GtkFileSystem *file_system)
368 GtkFileSystemPrivate *priv;
377 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
381 g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
382 g_slist_free (priv->volumes);
383 priv->volumes = NULL;
386 /* first go through all connected drives */
387 drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
389 for (l = drives; l != NULL; l = l->next)
392 volumes = g_drive_get_volumes (drive);
396 for (ll = volumes; ll != NULL; ll = ll->next)
399 mount = g_volume_get_mount (volume);
403 /* Show mounted volume */
404 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
405 g_object_unref (mount);
409 /* Do show the unmounted volumes in the sidebar;
410 * this is so the user can mount it (in case automounting
413 * Also, even if automounting is enabled, this gives a visual
414 * cue that the user should remember to yank out the media if
415 * he just unmounted it.
417 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
420 g_object_unref (volume);
423 else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
425 /* If the drive has no mountable volumes and we cannot detect media change.. we
426 * display the drive in the sidebar so the user can manually poll the drive by
427 * right clicking and selecting "Rescan..."
429 * This is mainly for drives like floppies where media detection doesn't
430 * work.. but it's also for human beings who like to turn off media detection
431 * in the OS to save battery juice.
434 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
438 g_list_free (drives);
440 /* add all volumes that is not associated with a drive */
441 volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
443 for (l = volumes; l != NULL; l = l->next)
446 drive = g_volume_get_drive (volume);
450 g_object_unref (drive);
454 mount = g_volume_get_mount (volume);
458 /* show this mount */
459 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
460 g_object_unref (mount);
464 /* see comment above in why we add an icon for a volume */
465 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
469 g_list_free (volumes);
471 /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
472 mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
474 for (l = mounts; l != NULL; l = l->next)
477 volume = g_mount_get_volume (mount);
481 g_object_unref (volume);
485 /* show this mount */
486 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
489 g_list_free (mounts);
493 gtk_file_system_init (GtkFileSystem *file_system)
495 GtkFileSystemPrivate *priv;
496 GFile *bookmarks_file;
497 GError *error = NULL;
501 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
504 priv->volume_monitor = g_volume_monitor_get ();
506 g_signal_connect (priv->volume_monitor, "mount-added",
507 G_CALLBACK (volumes_changed), file_system);
508 g_signal_connect (priv->volume_monitor, "mount-removed",
509 G_CALLBACK (volumes_changed), file_system);
510 g_signal_connect (priv->volume_monitor, "mount-changed",
511 G_CALLBACK (volumes_changed), file_system);
512 g_signal_connect (priv->volume_monitor, "volume-added",
513 G_CALLBACK (volumes_changed), file_system);
514 g_signal_connect (priv->volume_monitor, "volume-removed",
515 G_CALLBACK (volumes_changed), file_system);
516 g_signal_connect (priv->volume_monitor, "volume-changed",
517 G_CALLBACK (volumes_changed), file_system);
518 g_signal_connect (priv->volume_monitor, "drive-connected",
519 G_CALLBACK (volumes_changed), file_system);
520 g_signal_connect (priv->volume_monitor, "drive-disconnected",
521 G_CALLBACK (volumes_changed), file_system);
522 g_signal_connect (priv->volume_monitor, "drive-changed",
523 G_CALLBACK (volumes_changed), file_system);
526 bookmarks_file = get_bookmarks_file ();
527 priv->bookmarks = read_bookmarks (bookmarks_file);
528 priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
532 g_warning (error->message);
534 g_signal_connect (priv->bookmarks_monitor, "changed",
535 G_CALLBACK (bookmarks_file_changed), file_system);
537 g_object_unref (bookmarks_file);
540 /* GtkFileSystem public methods */
542 gtk_file_system_new (void)
544 return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
548 gtk_file_system_list_volumes (GtkFileSystem *file_system)
550 GtkFileSystemPrivate *priv;
553 DEBUG ("list_volumes");
555 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
557 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
558 get_volumes_list (GTK_FILE_SYSTEM (file_system));
560 list = g_slist_copy (priv->volumes);
562 /* Prepend root volume */
563 list = g_slist_prepend (list, (gpointer) root_volume_token);
569 gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
571 GtkFileSystemPrivate *priv;
572 GSList *bookmarks, *files = NULL;
574 DEBUG ("list_bookmarks");
576 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
577 bookmarks = priv->bookmarks;
581 GtkFileSystemBookmark *bookmark;
583 bookmark = bookmarks->data;
584 bookmarks = bookmarks->next;
586 files = g_slist_prepend (files, g_object_ref (bookmark->file));
589 return g_slist_reverse (files);
593 gtk_file_system_parse (GtkFileSystem *file_system,
601 gboolean result = FALSE;
602 gboolean is_dir = FALSE;
603 gchar *last_slash = NULL;
608 is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR);
610 last_slash = strrchr (str, G_DIR_SEPARATOR);
613 file = g_file_parse_name (str);
615 file = g_file_resolve_relative_path (base_file, str);
617 if (g_file_equal (base_file, file))
619 /* this is when user types '.', could be the
620 * beginning of a hidden file, ./ or ../
622 *folder = g_object_ref (file);
623 *file_part = g_strdup (str);
628 /* it's a dir, or at least it ends with the dir separator */
629 *folder = g_object_ref (file);
630 *file_part = g_strdup ("");
637 parent_file = g_file_get_parent (file);
642 GTK_FILE_SYSTEM_ERROR,
643 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
644 "Could not get parent file");
650 *folder = parent_file;
654 *file_part = g_strdup (last_slash + 1);
656 *file_part = g_strdup (str);
660 g_object_unref (file);
666 free_async_data (AsyncFuncData *async_data)
668 g_object_unref (async_data->file_system);
669 g_object_unref (async_data->file);
670 g_object_unref (async_data->cancellable);
672 if (async_data->folder)
673 g_object_unref (async_data->folder);
675 g_free (async_data->attributes);
680 enumerate_children_callback (GObject *source_object,
681 GAsyncResult *result,
684 GFileEnumerator *enumerator;
685 AsyncFuncData *async_data;
686 GtkFolder *folder = NULL;
688 GError *error = NULL;
690 file = G_FILE (source_object);
691 async_data = (AsyncFuncData *) user_data;
692 enumerator = g_file_enumerate_children_finish (file, result, &error);
696 folder = g_object_new (GTK_TYPE_FOLDER,
697 "file", source_object,
698 "enumerator", enumerator,
699 "attributes", async_data->attributes,
701 g_object_unref (enumerator);
704 gdk_threads_enter ();
705 ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable,
706 folder, error, async_data->data);
707 gdk_threads_leave ();
709 free_async_data (async_data);
712 g_error_free (error);
716 gtk_file_system_get_folder (GtkFileSystem *file_system,
718 const gchar *attributes,
719 GtkFileSystemGetFolderCallback callback,
722 GCancellable *cancellable;
723 AsyncFuncData *async_data;
725 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
726 g_return_val_if_fail (G_IS_FILE (file), NULL);
728 cancellable = g_cancellable_new ();
730 async_data = g_new0 (AsyncFuncData, 1);
731 async_data->file_system = g_object_ref (file_system);
732 async_data->file = g_object_ref (file);
733 async_data->cancellable = g_object_ref (cancellable);
734 async_data->attributes = g_strdup (attributes);
736 async_data->callback = callback;
737 async_data->data = data;
739 g_file_enumerate_children_async (file,
741 G_FILE_QUERY_INFO_NONE,
744 enumerate_children_callback,
750 query_info_callback (GObject *source_object,
751 GAsyncResult *result,
754 AsyncFuncData *async_data;
755 GError *error = NULL;
756 GFileInfo *file_info;
759 DEBUG ("query_info_callback");
761 file = G_FILE (source_object);
762 async_data = (AsyncFuncData *) user_data;
763 file_info = g_file_query_info_finish (file, result, &error);
765 if (async_data->callback)
767 gdk_threads_enter ();
768 ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
769 file_info, error, async_data->data);
770 gdk_threads_leave ();
774 g_object_unref (file_info);
777 g_error_free (error);
779 free_async_data (async_data);
783 gtk_file_system_get_info (GtkFileSystem *file_system,
785 const gchar *attributes,
786 GtkFileSystemGetInfoCallback callback,
789 GCancellable *cancellable;
790 AsyncFuncData *async_data;
792 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
793 g_return_val_if_fail (G_IS_FILE (file), NULL);
795 cancellable = g_cancellable_new ();
797 async_data = g_new0 (AsyncFuncData, 1);
798 async_data->file_system = g_object_ref (file_system);
799 async_data->file = g_object_ref (file);
800 async_data->cancellable = g_object_ref (cancellable);
802 async_data->callback = callback;
803 async_data->data = data;
805 g_file_query_info_async (file,
807 G_FILE_QUERY_INFO_NONE,
817 drive_poll_for_media_cb (GObject *source_object,
818 GAsyncResult *result,
821 AsyncFuncData *async_data;
822 GError *error = NULL;
824 g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
825 async_data = (AsyncFuncData *) user_data;
827 gdk_threads_enter ();
828 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
829 (GtkFileSystemVolume *) source_object,
830 error, async_data->data);
831 gdk_threads_leave ();
834 g_error_free (error);
838 volume_mount_cb (GObject *source_object,
839 GAsyncResult *result,
842 AsyncFuncData *async_data;
843 GError *error = NULL;
845 g_volume_mount_finish (G_VOLUME (source_object), result, &error);
846 async_data = (AsyncFuncData *) user_data;
848 gdk_threads_enter ();
849 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
850 (GtkFileSystemVolume *) source_object,
851 error, async_data->data);
852 gdk_threads_leave ();
855 g_error_free (error);
859 gtk_file_system_mount_volume (GtkFileSystem *file_system,
860 GtkFileSystemVolume *volume,
861 GMountOperation *mount_operation,
862 GtkFileSystemVolumeMountCallback callback,
865 GCancellable *cancellable;
866 AsyncFuncData *async_data;
867 gboolean handled = FALSE;
869 DEBUG ("volume_mount");
871 cancellable = g_cancellable_new ();
873 async_data = g_new0 (AsyncFuncData, 1);
874 async_data->file_system = g_object_ref (file_system);
875 async_data->cancellable = g_object_ref (cancellable);
877 async_data->callback = callback;
878 async_data->data = data;
880 if (G_IS_DRIVE (volume))
882 /* this path happens for drives that are not polled by the OS and where the last media
883 * check indicated that no media was available. So the thing to do here is to
884 * invoke poll_for_media() on the drive
886 g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
889 else if (G_IS_VOLUME (volume))
891 g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
896 free_async_data (async_data);
902 enclosing_volume_mount_cb (GObject *source_object,
903 GAsyncResult *result,
906 GtkFileSystemVolume *volume;
907 AsyncFuncData *async_data;
908 GError *error = NULL;
910 async_data = (AsyncFuncData *) user_data;
911 g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
912 volume = gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
914 gdk_threads_enter ();
915 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
916 error, async_data->data);
917 gdk_threads_leave ();
920 g_error_free (error);
924 gtk_file_system_mount_enclosing_volume (GtkFileSystem *file_system,
926 GMountOperation *mount_operation,
927 GtkFileSystemVolumeMountCallback callback,
930 GCancellable *cancellable;
931 AsyncFuncData *async_data;
933 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
934 g_return_val_if_fail (G_IS_FILE (file), NULL);
936 DEBUG ("mount_enclosing_volume");
938 cancellable = g_cancellable_new ();
940 async_data = g_new0 (AsyncFuncData, 1);
941 async_data->file_system = g_object_ref (file_system);
942 async_data->file = g_object_ref (file);
943 async_data->cancellable = g_object_ref (cancellable);
945 async_data->callback = callback;
946 async_data->data = data;
948 g_file_mount_enclosing_volume (file,
952 enclosing_volume_mount_cb,
958 gtk_file_system_insert_bookmark (GtkFileSystem *file_system,
963 GtkFileSystemPrivate *priv;
965 GtkFileSystemBookmark *bookmark;
966 gboolean result = TRUE;
967 GFile *bookmarks_file;
969 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
970 bookmarks = priv->bookmarks;
974 bookmark = bookmarks->data;
975 bookmarks = bookmarks->next;
977 if (g_file_equal (bookmark->file, file))
979 /* File is already in bookmarks */
987 gchar *uri = g_file_get_uri (file);
990 GTK_FILE_SYSTEM_ERROR,
991 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
992 "%s already exists in the bookmarks list",
1000 bookmark = g_slice_new0 (GtkFileSystemBookmark);
1001 bookmark->file = g_object_ref (file);
1003 priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
1005 bookmarks_file = get_bookmarks_file ();
1006 save_bookmarks (bookmarks_file, priv->bookmarks);
1007 g_object_unref (bookmarks_file);
1009 g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1015 gtk_file_system_remove_bookmark (GtkFileSystem *file_system,
1019 GtkFileSystemPrivate *priv;
1020 GtkFileSystemBookmark *bookmark;
1022 gboolean result = FALSE;
1023 GFile *bookmarks_file;
1025 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1027 if (!priv->bookmarks)
1030 bookmarks = priv->bookmarks;
1034 bookmark = bookmarks->data;
1036 if (g_file_equal (bookmark->file, file))
1039 priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
1040 gtk_file_system_bookmark_free (bookmark);
1041 g_slist_free_1 (bookmarks);
1045 bookmarks = bookmarks->next;
1050 gchar *uri = g_file_get_uri (file);
1053 GTK_FILE_SYSTEM_ERROR,
1054 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1055 "%s does not exist in the bookmarks list",
1063 bookmarks_file = get_bookmarks_file ();
1064 save_bookmarks (bookmarks_file, priv->bookmarks);
1065 g_object_unref (bookmarks_file);
1067 g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1073 gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
1076 GtkFileSystemPrivate *priv;
1078 gchar *label = NULL;
1080 DEBUG ("get_bookmark_label");
1082 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1083 bookmarks = priv->bookmarks;
1087 GtkFileSystemBookmark *bookmark;
1089 bookmark = bookmarks->data;
1090 bookmarks = bookmarks->next;
1092 if (g_file_equal (file, bookmark->file))
1094 label = g_strdup (bookmark->label);
1103 gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
1107 GtkFileSystemPrivate *priv;
1108 gboolean changed = FALSE;
1109 GFile *bookmarks_file;
1112 DEBUG ("set_bookmark_label");
1114 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1115 bookmarks = priv->bookmarks;
1119 GtkFileSystemBookmark *bookmark;
1121 bookmark = bookmarks->data;
1122 bookmarks = bookmarks->next;
1124 if (g_file_equal (file, bookmark->file))
1126 g_free (bookmark->file);
1127 bookmark->label = g_strdup (label);
1133 bookmarks_file = get_bookmarks_file ();
1134 save_bookmarks (bookmarks_file, priv->bookmarks);
1135 g_object_unref (bookmarks_file);
1138 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1141 GtkFileSystemVolume *
1142 gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
1145 GtkFileSystemPrivate *priv;
1148 DEBUG ("get_volume_for_file");
1150 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1151 mount = g_file_find_enclosing_mount (file, NULL, NULL);
1153 if (!mount && g_file_is_native (file))
1154 return (GtkFileSystemVolume *) root_volume_token;
1156 return (GtkFileSystemVolume *) mount;
1159 /* GtkFolder methods */
1161 gtk_folder_set_property (GObject *object,
1163 const GValue *value,
1166 GtkFolderPrivate *priv;
1168 priv = GTK_FOLDER_GET_PRIVATE (object);
1173 priv->folder_file = g_value_dup_object (value);
1175 case PROP_ENUMERATOR:
1176 priv->enumerator = g_value_dup_object (value);
1178 case PROP_ATTRIBUTES:
1179 priv->attributes = g_value_dup_string (value);
1182 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1188 gtk_folder_get_property (GObject *object,
1193 GtkFolderPrivate *priv;
1195 priv = GTK_FOLDER_GET_PRIVATE (object);
1200 g_value_set_object (value, priv->folder_file);
1202 case PROP_ENUMERATOR:
1203 g_value_set_object (value, priv->enumerator);
1205 case PROP_ATTRIBUTES:
1206 g_value_set_string (value, priv->attributes);
1209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1215 query_created_file_info_callback (GObject *source_object,
1216 GAsyncResult *result,
1219 GFile *file = G_FILE (source_object);
1220 GError *error = NULL;
1225 info = g_file_query_info_finish (file, result, &error);
1229 g_error_free (error);
1233 folder = GTK_FOLDER (user_data);
1234 _gtk_folder_add_file (folder, file, info);
1236 files = g_slist_prepend (NULL, file);
1237 g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1238 g_slist_free (files);
1240 g_object_unref (info);
1244 directory_monitor_changed (GFileMonitor *monitor,
1247 GFileMonitorEvent event,
1250 GtkFolderPrivate *priv;
1254 folder = GTK_FOLDER (data);
1255 priv = GTK_FOLDER_GET_PRIVATE (folder);
1256 files = g_slist_prepend (NULL, file);
1258 gdk_threads_enter ();
1262 case G_FILE_MONITOR_EVENT_CREATED:
1263 g_file_query_info_async (file,
1265 G_FILE_QUERY_INFO_NONE,
1268 query_created_file_info_callback,
1271 case G_FILE_MONITOR_EVENT_DELETED:
1272 if (g_file_equal (file, priv->folder_file))
1273 g_signal_emit (folder, folder_signals[DELETED], 0);
1275 g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files);
1281 gdk_threads_leave ();
1283 g_slist_free (files);
1287 enumerator_files_callback (GObject *source_object,
1288 GAsyncResult *result,
1291 GFileEnumerator *enumerator;
1292 GtkFolderPrivate *priv;
1294 GError *error = NULL;
1295 GSList *files = NULL;
1296 GList *file_infos, *f;
1298 enumerator = G_FILE_ENUMERATOR (source_object);
1299 file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error);
1303 g_warning (error->message);
1304 g_error_free (error);
1308 folder = GTK_FOLDER (user_data);
1309 priv = GTK_FOLDER_GET_PRIVATE (folder);
1313 g_file_enumerator_close_async (enumerator,
1317 _gtk_folder_set_finished_loading (folder, TRUE);
1321 g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY,
1324 enumerator_files_callback,
1327 for (f = file_infos; f; f = f->next)
1333 child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info));
1334 _gtk_folder_add_file (folder, child_file, info);
1335 files = g_slist_prepend (files, child_file);
1338 gdk_threads_enter ();
1339 g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1340 gdk_threads_leave ();
1342 g_list_foreach (file_infos, (GFunc) g_object_unref, NULL);
1343 g_list_free (file_infos);
1345 g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1346 g_slist_free (files);
1350 gtk_folder_constructed (GObject *object)
1352 GtkFolderPrivate *priv;
1353 GError *error = NULL;
1355 priv = GTK_FOLDER_GET_PRIVATE (object);
1356 priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error);
1359 g_warning (error->message);
1361 g_signal_connect (priv->directory_monitor, "changed",
1362 G_CALLBACK (directory_monitor_changed), object);
1364 g_file_enumerator_next_files_async (priv->enumerator,
1368 enumerator_files_callback,
1370 /* This isn't needed anymore */
1371 g_object_unref (priv->enumerator);
1372 priv->enumerator = NULL;
1376 gtk_folder_finalize (GObject *object)
1378 GtkFolderPrivate *priv;
1380 priv = GTK_FOLDER_GET_PRIVATE (object);
1382 g_hash_table_unref (priv->children);
1384 if (priv->folder_file)
1385 g_object_unref (priv->folder_file);
1387 if (priv->directory_monitor)
1388 g_object_unref (priv->directory_monitor);
1390 g_cancellable_cancel (priv->cancellable);
1391 g_object_unref (priv->cancellable);
1392 g_free (priv->attributes);
1394 G_OBJECT_CLASS (gtk_folder_parent_class)->finalize (object);
1398 gtk_folder_class_init (GtkFolderClass *class)
1400 GObjectClass *object_class = G_OBJECT_CLASS (class);
1402 object_class->set_property = gtk_folder_set_property;
1403 object_class->get_property = gtk_folder_get_property;
1404 object_class->constructed = gtk_folder_constructed;
1405 object_class->finalize = gtk_folder_finalize;
1407 g_object_class_install_property (object_class,
1409 g_param_spec_object ("file",
1411 "GFile for the folder",
1413 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1414 g_object_class_install_property (object_class,
1416 g_param_spec_object ("enumerator",
1418 "GFileEnumerator to list files",
1419 G_TYPE_FILE_ENUMERATOR,
1420 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1421 g_object_class_install_property (object_class,
1423 g_param_spec_string ("attributes",
1425 "Attributes to query for",
1427 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1428 folder_signals[FILES_ADDED] =
1429 g_signal_new ("files-added",
1430 G_TYPE_FROM_CLASS (object_class),
1432 G_STRUCT_OFFSET (GtkFolderClass, files_added),
1434 g_cclosure_marshal_VOID__POINTER,
1435 G_TYPE_NONE, 1, G_TYPE_POINTER);
1436 folder_signals[FILES_REMOVED] =
1437 g_signal_new ("files-removed",
1438 G_TYPE_FROM_CLASS (object_class),
1440 G_STRUCT_OFFSET (GtkFolderClass, files_removed),
1442 g_cclosure_marshal_VOID__POINTER,
1443 G_TYPE_NONE, 1, G_TYPE_POINTER);
1444 folder_signals[FILES_CHANGED] =
1445 g_signal_new ("files-changed",
1446 G_TYPE_FROM_CLASS (object_class),
1448 G_STRUCT_OFFSET (GtkFolderClass, files_changed),
1450 g_cclosure_marshal_VOID__POINTER,
1451 G_TYPE_NONE, 1, G_TYPE_POINTER);
1452 folder_signals[FINISHED_LOADING] =
1453 g_signal_new ("finished-loading",
1454 G_TYPE_FROM_CLASS (object_class),
1456 G_STRUCT_OFFSET (GtkFolderClass, finished_loading),
1458 g_cclosure_marshal_VOID__VOID,
1460 folder_signals[DELETED] =
1461 g_signal_new ("deleted",
1462 G_TYPE_FROM_CLASS (object_class),
1464 G_STRUCT_OFFSET (GtkFolderClass, deleted),
1466 g_cclosure_marshal_VOID__VOID,
1469 g_type_class_add_private (object_class, sizeof (GtkFolderPrivate));
1473 gtk_folder_init (GtkFolder *folder)
1475 GtkFolderPrivate *priv;
1477 priv = GTK_FOLDER_GET_PRIVATE (folder);
1479 priv->children = g_hash_table_new_full (g_file_hash,
1480 (GEqualFunc) g_file_equal,
1481 (GDestroyNotify) g_object_unref,
1482 (GDestroyNotify) g_object_unref);
1483 priv->cancellable = g_cancellable_new ();
1487 _gtk_folder_set_finished_loading (GtkFolder *folder,
1488 gboolean finished_loading)
1490 GtkFolderPrivate *priv;
1492 priv = GTK_FOLDER_GET_PRIVATE (folder);
1493 priv->finished_loading = (finished_loading == TRUE);
1495 gdk_threads_enter ();
1496 g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0);
1497 gdk_threads_leave ();
1501 _gtk_folder_add_file (GtkFolder *folder,
1505 GtkFolderPrivate *priv;
1507 priv = GTK_FOLDER_GET_PRIVATE (folder);
1509 g_hash_table_insert (priv->children,
1510 g_object_ref (file),
1511 g_object_ref (info));
1515 gtk_folder_list_children (GtkFolder *folder)
1517 GtkFolderPrivate *priv;
1518 GList *files, *elem;
1519 GSList *children = NULL;
1521 priv = GTK_FOLDER_GET_PRIVATE (folder);
1522 files = g_hash_table_get_keys (priv->children);
1525 for (elem = files; elem; elem = elem->next)
1526 children = g_slist_prepend (children, g_object_ref (elem->data));
1528 g_list_free (files);
1534 gtk_folder_get_info (GtkFolder *folder,
1537 GtkFolderPrivate *priv;
1540 priv = GTK_FOLDER_GET_PRIVATE (folder);
1541 info = g_hash_table_lookup (priv->children, file);
1546 return g_object_ref (info);
1550 gtk_folder_is_finished_loading (GtkFolder *folder)
1552 GtkFolderPrivate *priv;
1554 priv = GTK_FOLDER_GET_PRIVATE (folder);
1556 return priv->finished_loading;
1559 /* GtkFileSystemVolume public methods */
1561 gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
1563 DEBUG ("volume_get_display_name");
1565 if (IS_ROOT_VOLUME (volume))
1566 return g_strdup (_(root_volume_token));
1567 if (G_IS_DRIVE (volume))
1568 return g_drive_get_name (G_DRIVE (volume));
1569 else if (G_IS_MOUNT (volume))
1570 return g_mount_get_name (G_MOUNT (volume));
1571 else if (G_IS_VOLUME (volume))
1572 return g_volume_get_name (G_VOLUME (volume));
1578 gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
1582 DEBUG ("volume_is_mounted");
1584 if (IS_ROOT_VOLUME (volume))
1589 if (G_IS_MOUNT (volume))
1591 else if (G_IS_VOLUME (volume))
1595 mount = g_volume_get_mount (G_VOLUME (volume));
1600 g_object_unref (mount);
1608 gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
1612 DEBUG ("volume_get_base");
1614 if (IS_ROOT_VOLUME (volume))
1615 return g_file_new_for_uri ("file:///");
1617 if (G_IS_MOUNT (volume))
1618 file = g_mount_get_root (G_MOUNT (volume));
1619 else if (G_IS_VOLUME (volume))
1623 mount = g_volume_get_mount (G_VOLUME (volume));
1627 file = g_mount_get_root (mount);
1628 g_object_unref (mount);
1636 get_pixbuf_from_gicon (GIcon *icon,
1642 GtkIconTheme *icon_theme;
1643 GtkIconInfo *icon_info;
1646 screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1647 icon_theme = gtk_icon_theme_get_for_screen (screen);
1649 icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1652 GTK_ICON_LOOKUP_USE_BUILTIN);
1657 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1658 gtk_icon_info_free (icon_info);
1664 get_icon_for_special_directory (GFile *file)
1666 const gchar *special_dir;
1667 GFile *special_file;
1669 special_dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1670 special_file = g_file_new_for_path (special_dir);
1672 if (g_file_equal (file, special_file))
1674 const char *names[] = {
1681 g_object_unref (special_file);
1682 return g_themed_icon_new_from_names (names, -1);
1685 g_object_unref (special_file);
1686 special_dir = g_get_home_dir ();
1687 special_file = g_file_new_for_path (special_dir);
1689 if (g_file_equal (file, special_file))
1691 const char *names[] = {
1698 g_object_unref (special_file);
1699 return g_themed_icon_new_from_names (names, -1);
1702 g_object_unref (special_file);
1708 gtk_file_system_volume_render_icon (GtkFileSystemVolume *volume,
1715 const char *harddisk_icons[] = {
1717 "gnome-dev-harddisk",
1722 DEBUG ("volume_get_icon_name");
1724 if (IS_ROOT_VOLUME (volume))
1725 icon = g_themed_icon_new_from_names (harddisk_icons, -1);
1726 else if (G_IS_DRIVE (volume))
1727 icon = g_drive_get_icon (G_DRIVE (volume));
1728 else if (G_IS_VOLUME (volume))
1729 icon = g_volume_get_icon (G_VOLUME (volume));
1730 else if (G_IS_MOUNT (volume))
1732 GMount *mount = G_MOUNT (volume);
1735 file = g_mount_get_root (mount);
1736 icon = get_icon_for_special_directory (file);
1737 g_object_unref (file);
1740 icon = g_mount_get_icon (mount);
1746 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
1748 g_object_unref (icon);
1754 gtk_file_system_volume_free (GtkFileSystemVolume *volume)
1756 /* Root volume doesn't need to be freed */
1757 if (IS_ROOT_VOLUME (volume))
1760 if (G_IS_MOUNT (volume) ||
1761 G_IS_VOLUME (volume) ||
1762 G_IS_DRIVE (volume))
1763 g_object_unref (volume);
1766 /* GFileInfo helper functions */
1768 gtk_file_info_render_icon (GFileInfo *info,
1773 GdkPixbuf *pixbuf = NULL;
1774 gchar *thumbnail_path;
1776 thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1779 pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
1780 icon_size, icon_size,
1785 icon = g_file_info_get_icon (info);
1788 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);