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>
27 #include <glib/gi18n-lib.h>
29 #include "gtkfilechooser.h"
30 #include "gtkfilesystem.h"
31 #include "gtkicontheme.h"
32 #include "gtkprivate.h"
36 /* #define DEBUG_MODE */
38 #define DEBUG(x) g_debug (x);
43 #define GTK_FILE_SYSTEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FILE_SYSTEM, GtkFileSystemPrivate))
44 #define GTK_FOLDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FOLDER, GtkFolderPrivate))
45 #define FILES_PER_QUERY 100
47 /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
48 * really pointers to GDrive, GVolume or GMount objects. We need an extra
49 * token for the fake "File System" volume. So, we'll return a pointer to
50 * this particular string.
52 static const gchar *root_volume_token = N_("File System");
53 #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
77 static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
78 static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, };
80 typedef struct GtkFileSystemPrivate GtkFileSystemPrivate;
81 typedef struct GtkFolderPrivate GtkFolderPrivate;
82 typedef struct AsyncFuncData AsyncFuncData;
84 struct GtkFileSystemPrivate
86 GVolumeMonitor *volume_monitor;
88 /* This list contains elements that can be
89 * of type GDrive, GVolume and GMount
93 /* This list contains GtkFileSystemBookmark structs */
96 GFileMonitor *bookmarks_monitor;
99 struct GtkFolderPrivate
102 GHashTable *children;
103 GFileMonitor *directory_monitor;
104 GFileEnumerator *enumerator;
105 GCancellable *cancellable;
108 guint finished_loading : 1;
113 GtkFileSystem *file_system;
116 GCancellable *cancellable;
123 struct GtkFileSystemBookmark
129 G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
131 G_DEFINE_TYPE (GtkFolder, _gtk_folder, G_TYPE_OBJECT)
134 static void gtk_folder_set_finished_loading (GtkFolder *folder,
135 gboolean finished_loading);
136 static void gtk_folder_add_file (GtkFolder *folder,
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 ("%s", 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 mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
374 mount_root = g_mount_get_root (mount);
376 for (l = volumes; l != NULL; l = l->next)
378 GVolume *volume = G_VOLUME (l->data);
379 GFile *volume_activation_root;
381 volume_activation_root = g_volume_get_activation_root (volume);
382 if (volume_activation_root != NULL)
384 if (g_file_has_prefix (volume_activation_root, mount_root))
387 g_object_unref (volume_activation_root);
390 g_object_unref (volume_activation_root);
394 g_object_unref (mount_root);
399 get_volumes_list (GtkFileSystem *file_system)
401 GtkFileSystemPrivate *priv;
410 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
414 g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
415 g_slist_free (priv->volumes);
416 priv->volumes = NULL;
419 /* first go through all connected drives */
420 drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
422 for (l = drives; l != NULL; l = l->next)
425 volumes = g_drive_get_volumes (drive);
429 for (ll = volumes; ll != NULL; ll = ll->next)
432 mount = g_volume_get_mount (volume);
436 /* Show mounted volume */
437 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
438 g_object_unref (mount);
442 /* Do show the unmounted volumes in the sidebar;
443 * this is so the user can mount it (in case automounting
446 * Also, even if automounting is enabled, this gives a visual
447 * cue that the user should remember to yank out the media if
448 * he just unmounted it.
450 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
453 g_object_unref (volume);
456 g_list_free (volumes);
458 else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
460 /* If the drive has no mountable volumes and we cannot detect media change.. we
461 * display the drive in the sidebar so the user can manually poll the drive by
462 * right clicking and selecting "Rescan..."
464 * This is mainly for drives like floppies where media detection doesn't
465 * work.. but it's also for human beings who like to turn off media detection
466 * in the OS to save battery juice.
469 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
473 g_list_free (drives);
475 /* add all volumes that is not associated with a drive */
476 volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
478 for (l = volumes; l != NULL; l = l->next)
481 drive = g_volume_get_drive (volume);
485 g_object_unref (drive);
489 mount = g_volume_get_mount (volume);
493 /* show this mount */
494 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
495 g_object_unref (mount);
499 /* see comment above in why we add an icon for a volume */
500 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
504 /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
505 mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
507 for (l = mounts; l != NULL; l = l->next)
510 volume = g_mount_get_volume (mount);
514 g_object_unref (volume);
518 /* if there's exists one or more volumes with an activation root inside the mount,
519 * don't display the mount
521 if (mount_referenced_by_volume_activation_root (volumes, mount))
526 /* show this mount */
527 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
530 g_list_free (volumes);
532 g_list_free (mounts);
536 _gtk_file_system_init (GtkFileSystem *file_system)
538 GtkFileSystemPrivate *priv;
539 GFile *bookmarks_file;
540 GError *error = NULL;
544 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
547 priv->volume_monitor = g_volume_monitor_get ();
549 g_signal_connect (priv->volume_monitor, "mount-added",
550 G_CALLBACK (volumes_changed), file_system);
551 g_signal_connect (priv->volume_monitor, "mount-removed",
552 G_CALLBACK (volumes_changed), file_system);
553 g_signal_connect (priv->volume_monitor, "mount-changed",
554 G_CALLBACK (volumes_changed), file_system);
555 g_signal_connect (priv->volume_monitor, "volume-added",
556 G_CALLBACK (volumes_changed), file_system);
557 g_signal_connect (priv->volume_monitor, "volume-removed",
558 G_CALLBACK (volumes_changed), file_system);
559 g_signal_connect (priv->volume_monitor, "volume-changed",
560 G_CALLBACK (volumes_changed), file_system);
561 g_signal_connect (priv->volume_monitor, "drive-connected",
562 G_CALLBACK (volumes_changed), file_system);
563 g_signal_connect (priv->volume_monitor, "drive-disconnected",
564 G_CALLBACK (volumes_changed), file_system);
565 g_signal_connect (priv->volume_monitor, "drive-changed",
566 G_CALLBACK (volumes_changed), file_system);
569 bookmarks_file = get_bookmarks_file ();
570 priv->bookmarks = read_bookmarks (bookmarks_file);
571 priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
576 g_warning ("%s", error->message);
577 g_error_free (error);
580 g_signal_connect (priv->bookmarks_monitor, "changed",
581 G_CALLBACK (bookmarks_file_changed), file_system);
583 g_object_unref (bookmarks_file);
586 /* GtkFileSystem public methods */
588 _gtk_file_system_new (void)
590 return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
594 _gtk_file_system_list_volumes (GtkFileSystem *file_system)
596 GtkFileSystemPrivate *priv;
599 DEBUG ("list_volumes");
601 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
603 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
604 get_volumes_list (GTK_FILE_SYSTEM (file_system));
606 list = g_slist_copy (priv->volumes);
609 /* Prepend root volume */
610 list = g_slist_prepend (list, (gpointer) root_volume_token);
617 _gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
619 GtkFileSystemPrivate *priv;
620 GSList *bookmarks, *files = NULL;
622 DEBUG ("list_bookmarks");
624 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
625 bookmarks = priv->bookmarks;
629 GtkFileSystemBookmark *bookmark;
631 bookmark = bookmarks->data;
632 bookmarks = bookmarks->next;
634 files = g_slist_prepend (files, g_object_ref (bookmark->file));
637 return g_slist_reverse (files);
641 is_valid_scheme_character (char c)
643 return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
647 has_uri_scheme (const char *str)
653 if (!is_valid_scheme_character (*p))
658 while (is_valid_scheme_character (*p));
660 return (strncmp (p, "://", 3) == 0);
664 _gtk_file_system_parse (GtkFileSystem *file_system,
672 gboolean result = FALSE;
673 gboolean is_dir = FALSE;
674 gchar *last_slash = NULL;
680 is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR);
682 last_slash = strrchr (str, G_DIR_SEPARATOR);
684 is_uri = has_uri_scheme (str);
689 const char *slash_after_hostname;
691 colon = strchr (str, ':');
692 g_assert (colon != NULL);
693 g_assert (strncmp (colon, "://", 3) == 0);
695 slash_after_hostname = strchr (colon + 3, '/');
697 if (slash_after_hostname == NULL)
699 /* We don't have a full hostname yet. So, don't switch the folder
700 * until we have seen a full hostname. Otherwise, completion will
701 * happen for every character the user types for the hostname.
707 GTK_FILE_CHOOSER_ERROR,
708 GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME,
709 "Incomplete hostname");
714 if (str[0] == '~' || g_path_is_absolute (str) || is_uri)
715 file = g_file_parse_name (str);
717 file = g_file_resolve_relative_path (base_file, str);
719 if (g_file_equal (base_file, file))
721 /* this is when user types '.', could be the
722 * beginning of a hidden file, ./ or ../
724 *folder = g_object_ref (file);
725 *file_part = g_strdup (str);
730 /* it's a dir, or at least it ends with the dir separator */
731 *folder = g_object_ref (file);
732 *file_part = g_strdup ("");
739 parent_file = g_file_get_parent (file);
744 GTK_FILE_CHOOSER_ERROR,
745 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
746 "Could not get parent file");
752 *folder = parent_file;
756 *file_part = g_strdup (last_slash + 1);
758 *file_part = g_strdup (str);
762 g_object_unref (file);
768 free_async_data (AsyncFuncData *async_data)
770 g_object_unref (async_data->file_system);
771 g_object_unref (async_data->file);
772 g_object_unref (async_data->cancellable);
774 if (async_data->folder)
775 g_object_unref (async_data->folder);
777 g_free (async_data->attributes);
782 enumerate_children_callback (GObject *source_object,
783 GAsyncResult *result,
786 GFileEnumerator *enumerator;
787 AsyncFuncData *async_data;
788 GtkFolder *folder = NULL;
790 GError *error = NULL;
792 file = G_FILE (source_object);
793 async_data = (AsyncFuncData *) user_data;
794 enumerator = g_file_enumerate_children_finish (file, result, &error);
798 folder = g_object_new (GTK_TYPE_FOLDER,
799 "file", source_object,
800 "enumerator", enumerator,
801 "attributes", async_data->attributes,
803 g_object_unref (enumerator);
806 gdk_threads_enter ();
807 ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable,
808 folder, error, async_data->data);
809 gdk_threads_leave ();
811 free_async_data (async_data);
814 g_error_free (error);
818 _gtk_file_system_get_folder (GtkFileSystem *file_system,
820 const gchar *attributes,
821 GtkFileSystemGetFolderCallback callback,
824 GCancellable *cancellable;
825 AsyncFuncData *async_data;
827 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
828 g_return_val_if_fail (G_IS_FILE (file), NULL);
830 cancellable = g_cancellable_new ();
832 async_data = g_new0 (AsyncFuncData, 1);
833 async_data->file_system = g_object_ref (file_system);
834 async_data->file = g_object_ref (file);
835 async_data->cancellable = g_object_ref (cancellable);
836 async_data->attributes = g_strdup (attributes);
838 async_data->callback = callback;
839 async_data->data = data;
841 g_file_enumerate_children_async (file,
843 G_FILE_QUERY_INFO_NONE,
846 enumerate_children_callback,
852 query_info_callback (GObject *source_object,
853 GAsyncResult *result,
856 AsyncFuncData *async_data;
857 GError *error = NULL;
858 GFileInfo *file_info;
861 DEBUG ("query_info_callback");
863 file = G_FILE (source_object);
864 async_data = (AsyncFuncData *) user_data;
865 file_info = g_file_query_info_finish (file, result, &error);
867 if (async_data->callback)
869 gdk_threads_enter ();
870 ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
871 file_info, error, async_data->data);
872 gdk_threads_leave ();
876 g_object_unref (file_info);
879 g_error_free (error);
881 free_async_data (async_data);
885 _gtk_file_system_get_info (GtkFileSystem *file_system,
887 const gchar *attributes,
888 GtkFileSystemGetInfoCallback callback,
891 GCancellable *cancellable;
892 AsyncFuncData *async_data;
894 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
895 g_return_val_if_fail (G_IS_FILE (file), NULL);
897 cancellable = g_cancellable_new ();
899 async_data = g_new0 (AsyncFuncData, 1);
900 async_data->file_system = g_object_ref (file_system);
901 async_data->file = g_object_ref (file);
902 async_data->cancellable = g_object_ref (cancellable);
904 async_data->callback = callback;
905 async_data->data = data;
907 g_file_query_info_async (file,
909 G_FILE_QUERY_INFO_NONE,
919 drive_poll_for_media_cb (GObject *source_object,
920 GAsyncResult *result,
923 AsyncFuncData *async_data;
924 GError *error = NULL;
926 g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
927 async_data = (AsyncFuncData *) user_data;
929 gdk_threads_enter ();
930 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
931 (GtkFileSystemVolume *) source_object,
932 error, async_data->data);
933 gdk_threads_leave ();
936 g_error_free (error);
940 volume_mount_cb (GObject *source_object,
941 GAsyncResult *result,
944 AsyncFuncData *async_data;
945 GError *error = NULL;
947 g_volume_mount_finish (G_VOLUME (source_object), result, &error);
948 async_data = (AsyncFuncData *) user_data;
950 gdk_threads_enter ();
951 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
952 (GtkFileSystemVolume *) source_object,
953 error, async_data->data);
954 gdk_threads_leave ();
957 g_error_free (error);
961 _gtk_file_system_mount_volume (GtkFileSystem *file_system,
962 GtkFileSystemVolume *volume,
963 GMountOperation *mount_operation,
964 GtkFileSystemVolumeMountCallback callback,
967 GCancellable *cancellable;
968 AsyncFuncData *async_data;
969 gboolean handled = FALSE;
971 DEBUG ("volume_mount");
973 cancellable = g_cancellable_new ();
975 async_data = g_new0 (AsyncFuncData, 1);
976 async_data->file_system = g_object_ref (file_system);
977 async_data->cancellable = g_object_ref (cancellable);
979 async_data->callback = callback;
980 async_data->data = data;
982 if (G_IS_DRIVE (volume))
984 /* this path happens for drives that are not polled by the OS and where the last media
985 * check indicated that no media was available. So the thing to do here is to
986 * invoke poll_for_media() on the drive
988 g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
991 else if (G_IS_VOLUME (volume))
993 g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
998 free_async_data (async_data);
1004 enclosing_volume_mount_cb (GObject *source_object,
1005 GAsyncResult *result,
1008 GtkFileSystemVolume *volume;
1009 AsyncFuncData *async_data;
1010 GError *error = NULL;
1012 async_data = (AsyncFuncData *) user_data;
1013 g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
1014 volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
1016 /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
1017 /* Better than doing query_info with additional I/O every time. */
1018 if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
1019 g_clear_error (&error);
1021 gdk_threads_enter ();
1022 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
1023 error, async_data->data);
1024 gdk_threads_leave ();
1027 g_error_free (error);
1031 _gtk_file_system_mount_enclosing_volume (GtkFileSystem *file_system,
1033 GMountOperation *mount_operation,
1034 GtkFileSystemVolumeMountCallback callback,
1037 GCancellable *cancellable;
1038 AsyncFuncData *async_data;
1040 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
1041 g_return_val_if_fail (G_IS_FILE (file), NULL);
1043 DEBUG ("mount_enclosing_volume");
1045 cancellable = g_cancellable_new ();
1047 async_data = g_new0 (AsyncFuncData, 1);
1048 async_data->file_system = g_object_ref (file_system);
1049 async_data->file = g_object_ref (file);
1050 async_data->cancellable = g_object_ref (cancellable);
1052 async_data->callback = callback;
1053 async_data->data = data;
1055 g_file_mount_enclosing_volume (file,
1059 enclosing_volume_mount_cb,
1065 _gtk_file_system_insert_bookmark (GtkFileSystem *file_system,
1070 GtkFileSystemPrivate *priv;
1072 GtkFileSystemBookmark *bookmark;
1073 gboolean result = TRUE;
1074 GFile *bookmarks_file;
1076 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1077 bookmarks = priv->bookmarks;
1081 bookmark = bookmarks->data;
1082 bookmarks = bookmarks->next;
1084 if (g_file_equal (bookmark->file, file))
1086 /* File is already in bookmarks */
1094 gchar *uri = g_file_get_uri (file);
1097 GTK_FILE_CHOOSER_ERROR,
1098 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
1099 "%s already exists in the bookmarks list",
1107 bookmark = g_slice_new0 (GtkFileSystemBookmark);
1108 bookmark->file = g_object_ref (file);
1110 priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
1112 bookmarks_file = get_bookmarks_file ();
1113 save_bookmarks (bookmarks_file, priv->bookmarks);
1114 g_object_unref (bookmarks_file);
1116 g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1122 _gtk_file_system_remove_bookmark (GtkFileSystem *file_system,
1126 GtkFileSystemPrivate *priv;
1127 GtkFileSystemBookmark *bookmark;
1129 gboolean result = FALSE;
1130 GFile *bookmarks_file;
1132 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1134 if (!priv->bookmarks)
1137 bookmarks = priv->bookmarks;
1141 bookmark = bookmarks->data;
1143 if (g_file_equal (bookmark->file, file))
1146 priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
1147 _gtk_file_system_bookmark_free (bookmark);
1148 g_slist_free_1 (bookmarks);
1152 bookmarks = bookmarks->next;
1157 gchar *uri = g_file_get_uri (file);
1160 GTK_FILE_CHOOSER_ERROR,
1161 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
1162 "%s does not exist in the bookmarks list",
1170 bookmarks_file = get_bookmarks_file ();
1171 save_bookmarks (bookmarks_file, priv->bookmarks);
1172 g_object_unref (bookmarks_file);
1174 g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1180 _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
1183 GtkFileSystemPrivate *priv;
1185 gchar *label = NULL;
1187 DEBUG ("get_bookmark_label");
1189 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1190 bookmarks = priv->bookmarks;
1194 GtkFileSystemBookmark *bookmark;
1196 bookmark = bookmarks->data;
1197 bookmarks = bookmarks->next;
1199 if (g_file_equal (file, bookmark->file))
1201 label = g_strdup (bookmark->label);
1210 _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
1214 GtkFileSystemPrivate *priv;
1215 gboolean changed = FALSE;
1216 GFile *bookmarks_file;
1219 DEBUG ("set_bookmark_label");
1221 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1222 bookmarks = priv->bookmarks;
1226 GtkFileSystemBookmark *bookmark;
1228 bookmark = bookmarks->data;
1229 bookmarks = bookmarks->next;
1231 if (g_file_equal (file, bookmark->file))
1233 g_free (bookmark->label);
1234 bookmark->label = g_strdup (label);
1240 bookmarks_file = get_bookmarks_file ();
1241 save_bookmarks (bookmarks_file, priv->bookmarks);
1242 g_object_unref (bookmarks_file);
1245 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1248 GtkFileSystemVolume *
1249 _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
1252 GtkFileSystemPrivate *priv;
1255 DEBUG ("get_volume_for_file");
1257 priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1258 mount = g_file_find_enclosing_mount (file, NULL, NULL);
1260 if (!mount && g_file_is_native (file))
1261 return (GtkFileSystemVolume *) root_volume_token;
1263 return (GtkFileSystemVolume *) mount;
1266 /* GtkFolder methods */
1268 gtk_folder_set_property (GObject *object,
1270 const GValue *value,
1273 GtkFolderPrivate *priv;
1275 priv = GTK_FOLDER_GET_PRIVATE (object);
1280 priv->folder_file = g_value_dup_object (value);
1282 case PROP_ENUMERATOR:
1283 priv->enumerator = g_value_dup_object (value);
1285 case PROP_ATTRIBUTES:
1286 priv->attributes = g_value_dup_string (value);
1289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1295 gtk_folder_get_property (GObject *object,
1300 GtkFolderPrivate *priv;
1302 priv = GTK_FOLDER_GET_PRIVATE (object);
1307 g_value_set_object (value, priv->folder_file);
1309 case PROP_ENUMERATOR:
1310 g_value_set_object (value, priv->enumerator);
1312 case PROP_ATTRIBUTES:
1313 g_value_set_string (value, priv->attributes);
1316 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1322 query_created_file_info_callback (GObject *source_object,
1323 GAsyncResult *result,
1326 GFile *file = G_FILE (source_object);
1327 GError *error = NULL;
1332 info = g_file_query_info_finish (file, result, &error);
1336 g_error_free (error);
1340 folder = GTK_FOLDER (user_data);
1341 gtk_folder_add_file (folder, file, info);
1343 files = g_slist_prepend (NULL, file);
1344 g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1345 g_slist_free (files);
1347 g_object_unref (info);
1351 directory_monitor_changed (GFileMonitor *monitor,
1354 GFileMonitorEvent event,
1357 GtkFolderPrivate *priv;
1361 folder = GTK_FOLDER (data);
1362 priv = GTK_FOLDER_GET_PRIVATE (folder);
1363 files = g_slist_prepend (NULL, file);
1365 gdk_threads_enter ();
1369 case G_FILE_MONITOR_EVENT_CREATED:
1370 g_file_query_info_async (file,
1372 G_FILE_QUERY_INFO_NONE,
1375 query_created_file_info_callback,
1378 case G_FILE_MONITOR_EVENT_DELETED:
1379 if (g_file_equal (file, priv->folder_file))
1380 g_signal_emit (folder, folder_signals[DELETED], 0);
1382 g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files);
1388 gdk_threads_leave ();
1390 g_slist_free (files);
1394 enumerator_files_callback (GObject *source_object,
1395 GAsyncResult *result,
1398 GFileEnumerator *enumerator;
1399 GtkFolderPrivate *priv;
1401 GError *error = NULL;
1402 GSList *files = NULL;
1403 GList *file_infos, *f;
1405 enumerator = G_FILE_ENUMERATOR (source_object);
1406 file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error);
1410 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1411 g_warning ("%s", error->message);
1413 g_error_free (error);
1417 folder = GTK_FOLDER (user_data);
1418 priv = GTK_FOLDER_GET_PRIVATE (folder);
1422 g_file_enumerator_close_async (enumerator,
1426 gtk_folder_set_finished_loading (folder, TRUE);
1430 g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY,
1433 enumerator_files_callback,
1436 for (f = file_infos; f; f = f->next)
1442 child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info));
1443 gtk_folder_add_file (folder, child_file, info);
1444 files = g_slist_prepend (files, child_file);
1447 gdk_threads_enter ();
1448 g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1449 gdk_threads_leave ();
1451 g_list_foreach (file_infos, (GFunc) g_object_unref, NULL);
1452 g_list_free (file_infos);
1454 g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1455 g_slist_free (files);
1459 gtk_folder_constructed (GObject *object)
1461 GtkFolderPrivate *priv;
1462 GError *error = NULL;
1464 priv = GTK_FOLDER_GET_PRIVATE (object);
1465 priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error);
1469 g_warning ("%s", error->message);
1470 g_error_free (error);
1473 g_signal_connect (priv->directory_monitor, "changed",
1474 G_CALLBACK (directory_monitor_changed), object);
1476 g_file_enumerator_next_files_async (priv->enumerator,
1480 enumerator_files_callback,
1482 /* This isn't needed anymore */
1483 g_object_unref (priv->enumerator);
1484 priv->enumerator = NULL;
1488 gtk_folder_finalize (GObject *object)
1490 GtkFolderPrivate *priv;
1492 priv = GTK_FOLDER_GET_PRIVATE (object);
1494 g_hash_table_unref (priv->children);
1496 if (priv->folder_file)
1497 g_object_unref (priv->folder_file);
1499 if (priv->directory_monitor)
1500 g_object_unref (priv->directory_monitor);
1502 g_cancellable_cancel (priv->cancellable);
1503 g_object_unref (priv->cancellable);
1504 g_free (priv->attributes);
1506 G_OBJECT_CLASS (_gtk_folder_parent_class)->finalize (object);
1510 _gtk_folder_class_init (GtkFolderClass *class)
1512 GObjectClass *object_class = G_OBJECT_CLASS (class);
1514 object_class->set_property = gtk_folder_set_property;
1515 object_class->get_property = gtk_folder_get_property;
1516 object_class->constructed = gtk_folder_constructed;
1517 object_class->finalize = gtk_folder_finalize;
1519 g_object_class_install_property (object_class,
1521 g_param_spec_object ("file",
1523 "GFile for the folder",
1525 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1526 g_object_class_install_property (object_class,
1528 g_param_spec_object ("enumerator",
1530 "GFileEnumerator to list files",
1531 G_TYPE_FILE_ENUMERATOR,
1532 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1533 g_object_class_install_property (object_class,
1535 g_param_spec_string ("attributes",
1537 "Attributes to query for",
1539 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1540 folder_signals[FILES_ADDED] =
1541 g_signal_new ("files-added",
1542 G_TYPE_FROM_CLASS (object_class),
1544 G_STRUCT_OFFSET (GtkFolderClass, files_added),
1546 g_cclosure_marshal_VOID__POINTER,
1547 G_TYPE_NONE, 1, G_TYPE_POINTER);
1548 folder_signals[FILES_REMOVED] =
1549 g_signal_new ("files-removed",
1550 G_TYPE_FROM_CLASS (object_class),
1552 G_STRUCT_OFFSET (GtkFolderClass, files_removed),
1554 g_cclosure_marshal_VOID__POINTER,
1555 G_TYPE_NONE, 1, G_TYPE_POINTER);
1556 folder_signals[FILES_CHANGED] =
1557 g_signal_new ("files-changed",
1558 G_TYPE_FROM_CLASS (object_class),
1560 G_STRUCT_OFFSET (GtkFolderClass, files_changed),
1562 g_cclosure_marshal_VOID__POINTER,
1563 G_TYPE_NONE, 1, G_TYPE_POINTER);
1564 folder_signals[FINISHED_LOADING] =
1565 g_signal_new ("finished-loading",
1566 G_TYPE_FROM_CLASS (object_class),
1568 G_STRUCT_OFFSET (GtkFolderClass, finished_loading),
1570 g_cclosure_marshal_VOID__VOID,
1572 folder_signals[DELETED] =
1573 g_signal_new ("deleted",
1574 G_TYPE_FROM_CLASS (object_class),
1576 G_STRUCT_OFFSET (GtkFolderClass, deleted),
1578 g_cclosure_marshal_VOID__VOID,
1581 g_type_class_add_private (object_class, sizeof (GtkFolderPrivate));
1585 _gtk_folder_init (GtkFolder *folder)
1587 GtkFolderPrivate *priv;
1589 priv = GTK_FOLDER_GET_PRIVATE (folder);
1591 priv->children = g_hash_table_new_full (g_file_hash,
1592 (GEqualFunc) g_file_equal,
1593 (GDestroyNotify) g_object_unref,
1594 (GDestroyNotify) g_object_unref);
1595 priv->cancellable = g_cancellable_new ();
1599 gtk_folder_set_finished_loading (GtkFolder *folder,
1600 gboolean finished_loading)
1602 GtkFolderPrivate *priv;
1604 priv = GTK_FOLDER_GET_PRIVATE (folder);
1605 priv->finished_loading = (finished_loading == TRUE);
1607 gdk_threads_enter ();
1608 g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0);
1609 gdk_threads_leave ();
1613 gtk_folder_add_file (GtkFolder *folder,
1617 GtkFolderPrivate *priv;
1619 priv = GTK_FOLDER_GET_PRIVATE (folder);
1621 g_hash_table_insert (priv->children,
1622 g_object_ref (file),
1623 g_object_ref (info));
1627 _gtk_folder_list_children (GtkFolder *folder)
1629 GtkFolderPrivate *priv;
1630 GList *files, *elem;
1631 GSList *children = NULL;
1633 priv = GTK_FOLDER_GET_PRIVATE (folder);
1634 files = g_hash_table_get_keys (priv->children);
1637 for (elem = files; elem; elem = elem->next)
1638 children = g_slist_prepend (children, g_object_ref (elem->data));
1640 g_list_free (files);
1646 _gtk_folder_get_info (GtkFolder *folder,
1649 GtkFolderPrivate *priv;
1652 priv = GTK_FOLDER_GET_PRIVATE (folder);
1653 info = g_hash_table_lookup (priv->children, file);
1658 return g_object_ref (info);
1662 _gtk_folder_is_finished_loading (GtkFolder *folder)
1664 GtkFolderPrivate *priv;
1666 priv = GTK_FOLDER_GET_PRIVATE (folder);
1668 return priv->finished_loading;
1671 /* GtkFileSystemVolume public methods */
1673 _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
1675 DEBUG ("volume_get_display_name");
1677 if (IS_ROOT_VOLUME (volume))
1678 return g_strdup (_(root_volume_token));
1679 if (G_IS_DRIVE (volume))
1680 return g_drive_get_name (G_DRIVE (volume));
1681 else if (G_IS_MOUNT (volume))
1682 return g_mount_get_name (G_MOUNT (volume));
1683 else if (G_IS_VOLUME (volume))
1684 return g_volume_get_name (G_VOLUME (volume));
1690 _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
1694 DEBUG ("volume_is_mounted");
1696 if (IS_ROOT_VOLUME (volume))
1701 if (G_IS_MOUNT (volume))
1703 else if (G_IS_VOLUME (volume))
1707 mount = g_volume_get_mount (G_VOLUME (volume));
1712 g_object_unref (mount);
1720 _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
1724 DEBUG ("volume_get_base");
1726 if (IS_ROOT_VOLUME (volume))
1727 return g_file_new_for_uri ("file:///");
1729 if (G_IS_MOUNT (volume))
1730 file = g_mount_get_root (G_MOUNT (volume));
1731 else if (G_IS_VOLUME (volume))
1735 mount = g_volume_get_mount (G_VOLUME (volume));
1739 file = g_mount_get_root (mount);
1740 g_object_unref (mount);
1748 get_pixbuf_from_gicon (GIcon *icon,
1754 GtkIconTheme *icon_theme;
1755 GtkIconInfo *icon_info;
1758 screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1759 icon_theme = gtk_icon_theme_get_for_screen (screen);
1761 icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1764 GTK_ICON_LOOKUP_USE_BUILTIN);
1769 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1770 gtk_icon_info_free (icon_info);
1776 _gtk_file_system_volume_render_icon (GtkFileSystemVolume *volume,
1784 DEBUG ("volume_get_icon_name");
1786 if (IS_ROOT_VOLUME (volume))
1787 icon = g_themed_icon_new ("drive-harddisk");
1788 else if (G_IS_DRIVE (volume))
1789 icon = g_drive_get_icon (G_DRIVE (volume));
1790 else if (G_IS_VOLUME (volume))
1791 icon = g_volume_get_icon (G_VOLUME (volume));
1792 else if (G_IS_MOUNT (volume))
1793 icon = g_mount_get_icon (G_MOUNT (volume));
1798 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
1800 g_object_unref (icon);
1806 _gtk_file_system_volume_free (GtkFileSystemVolume *volume)
1808 /* Root volume doesn't need to be freed */
1809 if (IS_ROOT_VOLUME (volume))
1812 if (G_IS_MOUNT (volume) ||
1813 G_IS_VOLUME (volume) ||
1814 G_IS_DRIVE (volume))
1815 g_object_unref (volume);
1818 /* GFileInfo helper functions */
1820 _gtk_file_info_render_icon (GFileInfo *info,
1825 GdkPixbuf *pixbuf = NULL;
1826 const gchar *thumbnail_path;
1828 thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1831 pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
1832 icon_size, icon_size,
1837 icon = g_file_info_get_icon (info);
1840 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1844 /* Use general fallback for all files without icon */
1845 icon = g_themed_icon_new ("text-x-generic");
1846 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1847 g_object_unref (icon);
1855 _gtk_file_info_consider_as_directory (GFileInfo *info)
1857 GFileType type = g_file_info_get_file_type (info);
1859 return (type == G_FILE_TYPE_DIRECTORY ||
1860 type == G_FILE_TYPE_MOUNTABLE ||
1861 type == G_FILE_TYPE_SHORTCUT);