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"
34 /* #define DEBUG_MODE */
36 #define DEBUG(x) g_debug (x);
41 #define FILES_PER_QUERY 100
43 /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
44 * really pointers to GDrive, GVolume or GMount objects. We need an extra
45 * token for the fake "File System" volume. So, we'll return a pointer to
46 * this particular string.
48 static const gchar *root_volume_token = N_("File System");
49 #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
73 static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
74 static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, };
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,
135 /* GtkFileSystemBookmark methods */
137 _gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
139 g_object_unref (bookmark->file);
140 g_free (bookmark->label);
141 g_slice_free (GtkFileSystemBookmark, bookmark);
144 /* GtkFileSystem methods */
146 volumes_changed (GVolumeMonitor *volume_monitor,
150 GtkFileSystem *file_system;
152 gdk_threads_enter ();
154 file_system = GTK_FILE_SYSTEM (user_data);
155 g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
156 gdk_threads_leave ();
160 gtk_file_system_dispose (GObject *object)
162 GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
163 GtkFileSystemPrivate *priv = file_system->priv;
169 g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
170 g_slist_free (priv->volumes);
171 priv->volumes = NULL;
174 if (priv->volume_monitor)
176 g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
177 g_object_unref (priv->volume_monitor);
178 priv->volume_monitor = NULL;
181 G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
185 gtk_file_system_finalize (GObject *object)
187 GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
188 GtkFileSystemPrivate *priv = file_system->priv;
192 if (priv->bookmarks_monitor)
193 g_object_unref (priv->bookmarks_monitor);
197 g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
198 g_slist_free (priv->bookmarks);
201 G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object);
205 _gtk_file_system_class_init (GtkFileSystemClass *class)
207 GObjectClass *object_class = G_OBJECT_CLASS (class);
209 object_class->dispose = gtk_file_system_dispose;
210 object_class->finalize = gtk_file_system_finalize;
212 fs_signals[BOOKMARKS_CHANGED] =
213 g_signal_new ("bookmarks-changed",
214 G_TYPE_FROM_CLASS (object_class),
216 G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
218 g_cclosure_marshal_VOID__VOID,
221 fs_signals[VOLUMES_CHANGED] =
222 g_signal_new ("volumes-changed",
223 G_TYPE_FROM_CLASS (object_class),
225 G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
227 g_cclosure_marshal_VOID__VOID,
230 g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
234 get_bookmarks_file (void)
239 filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
240 file = g_file_new_for_path (filename);
247 read_bookmarks (GFile *file)
250 gchar **lines, *space;
251 GSList *bookmarks = NULL;
254 if (!g_file_load_contents (file, NULL, &contents,
258 lines = g_strsplit (contents, "\n", -1);
260 for (i = 0; lines[i]; i++)
262 GtkFileSystemBookmark *bookmark;
267 if (!g_utf8_validate (lines[i], -1, NULL))
270 bookmark = g_slice_new0 (GtkFileSystemBookmark);
272 if ((space = strchr (lines[i], ' ')) != NULL)
275 bookmark->label = g_strdup (space + 1);
278 bookmark->file = g_file_new_for_uri (lines[i]);
279 bookmarks = g_slist_prepend (bookmarks, bookmark);
282 bookmarks = g_slist_reverse (bookmarks);
290 save_bookmarks (GFile *bookmarks_file,
293 GError *error = NULL;
297 contents = g_string_new ("");
299 for (l = bookmarks; l; l = l->next)
301 GtkFileSystemBookmark *bookmark = l->data;
304 uri = g_file_get_uri (bookmark->file);
308 g_string_append (contents, uri);
311 g_string_append_printf (contents, " %s", bookmark->label);
313 g_string_append_c (contents, '\n');
317 if (!g_file_replace_contents (bookmarks_file,
319 strlen (contents->str),
320 NULL, FALSE, 0, NULL,
323 g_critical ("%s", error->message);
324 g_error_free (error);
327 g_string_free (contents, TRUE);
331 bookmarks_file_changed (GFileMonitor *monitor,
334 GFileMonitorEvent event,
337 GtkFileSystem *file_system = GTK_FILE_SYSTEM (data);
338 GtkFileSystemPrivate *priv = file_system->priv;
342 case G_FILE_MONITOR_EVENT_CHANGED:
343 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
344 case G_FILE_MONITOR_EVENT_CREATED:
345 case G_FILE_MONITOR_EVENT_DELETED:
346 g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
347 g_slist_free (priv->bookmarks);
349 priv->bookmarks = read_bookmarks (file);
351 gdk_threads_enter ();
352 g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
353 gdk_threads_leave ();
356 /* ignore at the moment */
362 mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
370 mount_root = g_mount_get_root (mount);
372 for (l = volumes; l != NULL; l = l->next)
374 GVolume *volume = G_VOLUME (l->data);
375 GFile *volume_activation_root;
377 volume_activation_root = g_volume_get_activation_root (volume);
378 if (volume_activation_root != NULL)
380 if (g_file_has_prefix (volume_activation_root, mount_root))
383 g_object_unref (volume_activation_root);
386 g_object_unref (volume_activation_root);
390 g_object_unref (mount_root);
395 get_volumes_list (GtkFileSystem *file_system)
397 GtkFileSystemPrivate *priv = file_system->priv;
408 g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
409 g_slist_free (priv->volumes);
410 priv->volumes = NULL;
413 /* first go through all connected drives */
414 drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
416 for (l = drives; l != NULL; l = l->next)
419 volumes = g_drive_get_volumes (drive);
423 for (ll = volumes; ll != NULL; ll = ll->next)
426 mount = g_volume_get_mount (volume);
430 /* Show mounted volume */
431 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
432 g_object_unref (mount);
436 /* Do show the unmounted volumes in the sidebar;
437 * this is so the user can mount it (in case automounting
440 * Also, even if automounting is enabled, this gives a visual
441 * cue that the user should remember to yank out the media if
442 * he just unmounted it.
444 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
447 g_object_unref (volume);
450 g_list_free (volumes);
452 else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
454 /* If the drive has no mountable volumes and we cannot detect media change.. we
455 * display the drive in the sidebar so the user can manually poll the drive by
456 * right clicking and selecting "Rescan..."
458 * This is mainly for drives like floppies where media detection doesn't
459 * work.. but it's also for human beings who like to turn off media detection
460 * in the OS to save battery juice.
463 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
466 g_object_unref (drive);
469 g_list_free (drives);
471 /* add all volumes that is not associated with a drive */
472 volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
474 for (l = volumes; l != NULL; l = l->next)
477 drive = g_volume_get_drive (volume);
481 g_object_unref (drive);
485 mount = g_volume_get_mount (volume);
489 /* show this mount */
490 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
491 g_object_unref (mount);
495 /* see comment above in why we add an icon for a volume */
496 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
499 g_object_unref (volume);
502 /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
503 mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
505 for (l = mounts; l != NULL; l = l->next)
508 volume = g_mount_get_volume (mount);
512 g_object_unref (volume);
516 /* if there's exists one or more volumes with an activation root inside the mount,
517 * don't display the mount
519 if (mount_referenced_by_volume_activation_root (volumes, mount))
521 g_object_unref (mount);
525 /* show this mount */
526 priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
527 g_object_unref (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 file_system->priv = G_TYPE_INSTANCE_GET_PRIVATE (file_system,
545 GTK_TYPE_FILE_SYSTEM,
546 GtkFileSystemPrivate);
547 priv = file_system->priv;
550 priv->volume_monitor = g_volume_monitor_get ();
552 g_signal_connect (priv->volume_monitor, "mount-added",
553 G_CALLBACK (volumes_changed), file_system);
554 g_signal_connect (priv->volume_monitor, "mount-removed",
555 G_CALLBACK (volumes_changed), file_system);
556 g_signal_connect (priv->volume_monitor, "mount-changed",
557 G_CALLBACK (volumes_changed), file_system);
558 g_signal_connect (priv->volume_monitor, "volume-added",
559 G_CALLBACK (volumes_changed), file_system);
560 g_signal_connect (priv->volume_monitor, "volume-removed",
561 G_CALLBACK (volumes_changed), file_system);
562 g_signal_connect (priv->volume_monitor, "volume-changed",
563 G_CALLBACK (volumes_changed), file_system);
564 g_signal_connect (priv->volume_monitor, "drive-connected",
565 G_CALLBACK (volumes_changed), file_system);
566 g_signal_connect (priv->volume_monitor, "drive-disconnected",
567 G_CALLBACK (volumes_changed), file_system);
568 g_signal_connect (priv->volume_monitor, "drive-changed",
569 G_CALLBACK (volumes_changed), file_system);
572 bookmarks_file = get_bookmarks_file ();
573 priv->bookmarks = read_bookmarks (bookmarks_file);
574 priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
579 g_warning ("%s", error->message);
580 g_error_free (error);
583 g_signal_connect (priv->bookmarks_monitor, "changed",
584 G_CALLBACK (bookmarks_file_changed), file_system);
586 g_object_unref (bookmarks_file);
589 /* GtkFileSystem public methods */
591 _gtk_file_system_new (void)
593 return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
597 _gtk_file_system_list_volumes (GtkFileSystem *file_system)
599 GtkFileSystemPrivate *priv = file_system->priv;
602 DEBUG ("list_volumes");
604 get_volumes_list (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 = file_system->priv;
620 GSList *bookmarks, *files = NULL;
622 DEBUG ("list_bookmarks");
624 bookmarks = priv->bookmarks;
628 GtkFileSystemBookmark *bookmark;
630 bookmark = bookmarks->data;
631 bookmarks = bookmarks->next;
633 files = g_slist_prepend (files, g_object_ref (bookmark->file));
636 return g_slist_reverse (files);
640 is_valid_scheme_character (char c)
642 return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
646 has_uri_scheme (const char *str)
652 if (!is_valid_scheme_character (*p))
657 while (is_valid_scheme_character (*p));
659 return (strncmp (p, "://", 3) == 0);
663 _gtk_file_system_parse (GtkFileSystem *file_system,
671 gboolean result = FALSE;
672 gboolean is_dir = FALSE;
673 gchar *last_slash = NULL;
679 is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR);
681 last_slash = strrchr (str, G_DIR_SEPARATOR);
683 is_uri = has_uri_scheme (str);
688 const char *slash_after_hostname;
690 colon = strchr (str, ':');
691 g_assert (colon != NULL);
692 g_assert (strncmp (colon, "://", 3) == 0);
694 slash_after_hostname = strchr (colon + 3, '/');
696 if (slash_after_hostname == NULL)
698 /* We don't have a full hostname yet. So, don't switch the folder
699 * until we have seen a full hostname. Otherwise, completion will
700 * happen for every character the user types for the hostname.
706 GTK_FILE_CHOOSER_ERROR,
707 GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME,
708 "Incomplete hostname");
713 if (str[0] == '~' || g_path_is_absolute (str) || is_uri)
714 file = g_file_parse_name (str);
718 file = g_file_resolve_relative_path (base_file, str);
724 GTK_FILE_CHOOSER_ERROR,
725 GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
731 if (base_file && g_file_equal (base_file, file))
733 /* this is when user types '.', could be the
734 * beginning of a hidden file, ./ or ../
736 *folder = g_object_ref (file);
737 *file_part = g_strdup (str);
742 /* it's a dir, or at least it ends with the dir separator */
743 *folder = g_object_ref (file);
744 *file_part = g_strdup ("");
751 parent_file = g_file_get_parent (file);
756 GTK_FILE_CHOOSER_ERROR,
757 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
758 "Could not get parent file");
764 *folder = parent_file;
768 *file_part = g_strdup (last_slash + 1);
770 *file_part = g_strdup (str);
774 g_object_unref (file);
780 free_async_data (AsyncFuncData *async_data)
782 g_object_unref (async_data->file_system);
783 g_object_unref (async_data->file);
784 g_object_unref (async_data->cancellable);
786 if (async_data->folder)
787 g_object_unref (async_data->folder);
789 g_free (async_data->attributes);
794 enumerate_children_callback (GObject *source_object,
795 GAsyncResult *result,
798 GFileEnumerator *enumerator;
799 AsyncFuncData *async_data;
800 GtkFolder *folder = NULL;
802 GError *error = NULL;
804 file = G_FILE (source_object);
805 async_data = (AsyncFuncData *) user_data;
806 enumerator = g_file_enumerate_children_finish (file, result, &error);
810 folder = g_object_new (GTK_TYPE_FOLDER,
811 "file", source_object,
812 "enumerator", enumerator,
813 "attributes", async_data->attributes,
815 g_object_unref (enumerator);
818 gdk_threads_enter ();
819 ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable,
820 folder, error, async_data->data);
821 gdk_threads_leave ();
823 free_async_data (async_data);
826 g_error_free (error);
830 _gtk_file_system_get_folder (GtkFileSystem *file_system,
832 const gchar *attributes,
833 GtkFileSystemGetFolderCallback callback,
836 GCancellable *cancellable;
837 AsyncFuncData *async_data;
839 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
840 g_return_val_if_fail (G_IS_FILE (file), NULL);
842 cancellable = g_cancellable_new ();
844 async_data = g_new0 (AsyncFuncData, 1);
845 async_data->file_system = g_object_ref (file_system);
846 async_data->file = g_object_ref (file);
847 async_data->cancellable = g_object_ref (cancellable);
848 async_data->attributes = g_strdup (attributes);
850 async_data->callback = callback;
851 async_data->data = data;
853 g_file_enumerate_children_async (file,
855 G_FILE_QUERY_INFO_NONE,
858 enumerate_children_callback,
864 query_info_callback (GObject *source_object,
865 GAsyncResult *result,
868 AsyncFuncData *async_data;
869 GError *error = NULL;
870 GFileInfo *file_info;
873 DEBUG ("query_info_callback");
875 file = G_FILE (source_object);
876 async_data = (AsyncFuncData *) user_data;
877 file_info = g_file_query_info_finish (file, result, &error);
879 if (async_data->callback)
881 gdk_threads_enter ();
882 ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
883 file_info, error, async_data->data);
884 gdk_threads_leave ();
888 g_object_unref (file_info);
891 g_error_free (error);
893 free_async_data (async_data);
897 _gtk_file_system_get_info (GtkFileSystem *file_system,
899 const gchar *attributes,
900 GtkFileSystemGetInfoCallback callback,
903 GCancellable *cancellable;
904 AsyncFuncData *async_data;
906 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
907 g_return_val_if_fail (G_IS_FILE (file), NULL);
909 cancellable = g_cancellable_new ();
911 async_data = g_new0 (AsyncFuncData, 1);
912 async_data->file_system = g_object_ref (file_system);
913 async_data->file = g_object_ref (file);
914 async_data->cancellable = g_object_ref (cancellable);
916 async_data->callback = callback;
917 async_data->data = data;
919 g_file_query_info_async (file,
921 G_FILE_QUERY_INFO_NONE,
931 drive_poll_for_media_cb (GObject *source_object,
932 GAsyncResult *result,
935 AsyncFuncData *async_data;
936 GError *error = NULL;
938 g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
939 async_data = (AsyncFuncData *) user_data;
941 gdk_threads_enter ();
942 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
943 (GtkFileSystemVolume *) source_object,
944 error, async_data->data);
945 gdk_threads_leave ();
948 g_error_free (error);
952 volume_mount_cb (GObject *source_object,
953 GAsyncResult *result,
956 AsyncFuncData *async_data;
957 GError *error = NULL;
959 g_volume_mount_finish (G_VOLUME (source_object), result, &error);
960 async_data = (AsyncFuncData *) user_data;
962 gdk_threads_enter ();
963 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
964 (GtkFileSystemVolume *) source_object,
965 error, async_data->data);
966 gdk_threads_leave ();
969 g_error_free (error);
973 _gtk_file_system_mount_volume (GtkFileSystem *file_system,
974 GtkFileSystemVolume *volume,
975 GMountOperation *mount_operation,
976 GtkFileSystemVolumeMountCallback callback,
979 GCancellable *cancellable;
980 AsyncFuncData *async_data;
981 gboolean handled = FALSE;
983 DEBUG ("volume_mount");
985 cancellable = g_cancellable_new ();
987 async_data = g_new0 (AsyncFuncData, 1);
988 async_data->file_system = g_object_ref (file_system);
989 async_data->cancellable = g_object_ref (cancellable);
991 async_data->callback = callback;
992 async_data->data = data;
994 if (G_IS_DRIVE (volume))
996 /* this path happens for drives that are not polled by the OS and where the last media
997 * check indicated that no media was available. So the thing to do here is to
998 * invoke poll_for_media() on the drive
1000 g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
1003 else if (G_IS_VOLUME (volume))
1005 g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
1010 free_async_data (async_data);
1016 enclosing_volume_mount_cb (GObject *source_object,
1017 GAsyncResult *result,
1020 GtkFileSystemVolume *volume;
1021 AsyncFuncData *async_data;
1022 GError *error = NULL;
1024 async_data = (AsyncFuncData *) user_data;
1025 g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
1026 volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
1028 /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
1029 /* Better than doing query_info with additional I/O every time. */
1030 if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
1031 g_clear_error (&error);
1033 gdk_threads_enter ();
1034 ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
1035 error, async_data->data);
1036 gdk_threads_leave ();
1039 g_error_free (error);
1041 _gtk_file_system_volume_unref (volume);
1045 _gtk_file_system_mount_enclosing_volume (GtkFileSystem *file_system,
1047 GMountOperation *mount_operation,
1048 GtkFileSystemVolumeMountCallback callback,
1051 GCancellable *cancellable;
1052 AsyncFuncData *async_data;
1054 g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
1055 g_return_val_if_fail (G_IS_FILE (file), NULL);
1057 DEBUG ("mount_enclosing_volume");
1059 cancellable = g_cancellable_new ();
1061 async_data = g_new0 (AsyncFuncData, 1);
1062 async_data->file_system = g_object_ref (file_system);
1063 async_data->file = g_object_ref (file);
1064 async_data->cancellable = g_object_ref (cancellable);
1066 async_data->callback = callback;
1067 async_data->data = data;
1069 g_file_mount_enclosing_volume (file,
1073 enclosing_volume_mount_cb,
1079 _gtk_file_system_insert_bookmark (GtkFileSystem *file_system,
1084 GtkFileSystemPrivate *priv = file_system->priv;
1086 GtkFileSystemBookmark *bookmark;
1087 gboolean result = TRUE;
1088 GFile *bookmarks_file;
1090 bookmarks = priv->bookmarks;
1094 bookmark = bookmarks->data;
1095 bookmarks = bookmarks->next;
1097 if (g_file_equal (bookmark->file, file))
1099 /* File is already in bookmarks */
1107 gchar *uri = g_file_get_uri (file);
1110 GTK_FILE_CHOOSER_ERROR,
1111 GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
1112 "%s already exists in the bookmarks list",
1120 bookmark = g_slice_new0 (GtkFileSystemBookmark);
1121 bookmark->file = g_object_ref (file);
1123 priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
1125 bookmarks_file = get_bookmarks_file ();
1126 save_bookmarks (bookmarks_file, priv->bookmarks);
1127 g_object_unref (bookmarks_file);
1129 g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1135 _gtk_file_system_remove_bookmark (GtkFileSystem *file_system,
1139 GtkFileSystemPrivate *priv = file_system->priv;
1140 GtkFileSystemBookmark *bookmark;
1142 gboolean result = FALSE;
1143 GFile *bookmarks_file;
1145 if (!priv->bookmarks)
1148 bookmarks = priv->bookmarks;
1152 bookmark = bookmarks->data;
1154 if (g_file_equal (bookmark->file, file))
1157 priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
1158 _gtk_file_system_bookmark_free (bookmark);
1159 g_slist_free_1 (bookmarks);
1163 bookmarks = bookmarks->next;
1168 gchar *uri = g_file_get_uri (file);
1171 GTK_FILE_CHOOSER_ERROR,
1172 GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
1173 "%s does not exist in the bookmarks list",
1181 bookmarks_file = get_bookmarks_file ();
1182 save_bookmarks (bookmarks_file, priv->bookmarks);
1183 g_object_unref (bookmarks_file);
1185 g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1191 _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
1194 GtkFileSystemPrivate *priv = file_system->priv;
1196 gchar *label = NULL;
1198 DEBUG ("get_bookmark_label");
1200 bookmarks = priv->bookmarks;
1204 GtkFileSystemBookmark *bookmark;
1206 bookmark = bookmarks->data;
1207 bookmarks = bookmarks->next;
1209 if (g_file_equal (file, bookmark->file))
1211 label = g_strdup (bookmark->label);
1220 _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
1224 GtkFileSystemPrivate *priv = file_system->priv;
1225 gboolean changed = FALSE;
1226 GFile *bookmarks_file;
1229 DEBUG ("set_bookmark_label");
1231 bookmarks = priv->bookmarks;
1235 GtkFileSystemBookmark *bookmark;
1237 bookmark = bookmarks->data;
1238 bookmarks = bookmarks->next;
1240 if (g_file_equal (file, bookmark->file))
1242 g_free (bookmark->label);
1243 bookmark->label = g_strdup (label);
1249 bookmarks_file = get_bookmarks_file ();
1250 save_bookmarks (bookmarks_file, priv->bookmarks);
1251 g_object_unref (bookmarks_file);
1254 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1257 GtkFileSystemVolume *
1258 _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
1263 DEBUG ("get_volume_for_file");
1265 mount = g_file_find_enclosing_mount (file, NULL, NULL);
1267 if (!mount && g_file_is_native (file))
1268 return (GtkFileSystemVolume *) root_volume_token;
1270 return (GtkFileSystemVolume *) mount;
1273 /* GtkFolder methods */
1275 gtk_folder_set_property (GObject *object,
1277 const GValue *value,
1280 GtkFolder *folder = GTK_FOLDER (object);
1281 GtkFolderPrivate *priv = folder->priv;
1286 priv->folder_file = g_value_dup_object (value);
1288 case PROP_ENUMERATOR:
1289 priv->enumerator = g_value_dup_object (value);
1291 case PROP_ATTRIBUTES:
1292 priv->attributes = g_value_dup_string (value);
1295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1301 gtk_folder_get_property (GObject *object,
1306 GtkFolder *folder = GTK_FOLDER (object);
1307 GtkFolderPrivate *priv = folder->priv;
1312 g_value_set_object (value, priv->folder_file);
1314 case PROP_ENUMERATOR:
1315 g_value_set_object (value, priv->enumerator);
1317 case PROP_ATTRIBUTES:
1318 g_value_set_string (value, priv->attributes);
1321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1327 query_created_file_info_callback (GObject *source_object,
1328 GAsyncResult *result,
1331 GFile *file = G_FILE (source_object);
1332 GError *error = NULL;
1337 info = g_file_query_info_finish (file, result, &error);
1341 g_error_free (error);
1345 gdk_threads_enter ();
1347 folder = GTK_FOLDER (user_data);
1348 gtk_folder_add_file (folder, file, info);
1350 files = g_slist_prepend (NULL, file);
1351 g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1352 g_slist_free (files);
1354 g_object_unref (info);
1355 gdk_threads_leave ();
1359 directory_monitor_changed (GFileMonitor *monitor,
1362 GFileMonitorEvent event,
1365 GtkFolder *folder = GTK_FOLDER (data);
1366 GtkFolderPrivate *priv = folder->priv;
1369 files = g_slist_prepend (NULL, file);
1371 gdk_threads_enter ();
1375 case G_FILE_MONITOR_EVENT_CREATED:
1376 g_file_query_info_async (file,
1378 G_FILE_QUERY_INFO_NONE,
1381 query_created_file_info_callback,
1384 case G_FILE_MONITOR_EVENT_DELETED:
1385 if (g_file_equal (file, priv->folder_file))
1386 g_signal_emit (folder, folder_signals[DELETED], 0);
1388 g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files);
1394 gdk_threads_leave ();
1396 g_slist_free (files);
1400 enumerator_files_callback (GObject *source_object,
1401 GAsyncResult *result,
1404 GtkFolder *folder = GTK_FOLDER (user_data);
1405 GtkFolderPrivate *priv = folder->priv;
1406 GFileEnumerator *enumerator;
1407 GError *error = NULL;
1408 GSList *files = NULL;
1409 GList *file_infos, *f;
1411 enumerator = G_FILE_ENUMERATOR (source_object);
1412 file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error);
1416 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1417 g_warning ("%s", error->message);
1419 g_error_free (error);
1425 g_file_enumerator_close_async (enumerator,
1429 gtk_folder_set_finished_loading (folder, TRUE);
1433 g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY,
1436 enumerator_files_callback,
1439 for (f = file_infos; f; f = f->next)
1445 child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info));
1446 gtk_folder_add_file (folder, child_file, info);
1447 files = g_slist_prepend (files, child_file);
1450 gdk_threads_enter ();
1451 g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1452 gdk_threads_leave ();
1454 g_list_foreach (file_infos, (GFunc) g_object_unref, NULL);
1455 g_list_free (file_infos);
1457 g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1458 g_slist_free (files);
1462 gtk_folder_constructed (GObject *object)
1464 GtkFolder *folder = GTK_FOLDER (object);
1465 GtkFolderPrivate *priv = folder->priv;
1466 GError *error = NULL;
1468 priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error);
1472 g_warning ("%s", error->message);
1473 g_error_free (error);
1476 g_signal_connect (priv->directory_monitor, "changed",
1477 G_CALLBACK (directory_monitor_changed), object);
1479 g_file_enumerator_next_files_async (priv->enumerator,
1483 enumerator_files_callback,
1485 /* This isn't needed anymore */
1486 g_object_unref (priv->enumerator);
1487 priv->enumerator = NULL;
1491 gtk_folder_finalize (GObject *object)
1493 GtkFolder *folder = GTK_FOLDER (object);
1494 GtkFolderPrivate *priv = folder->priv;
1496 g_hash_table_unref (priv->children);
1498 if (priv->folder_file)
1499 g_object_unref (priv->folder_file);
1501 if (priv->directory_monitor)
1502 g_object_unref (priv->directory_monitor);
1504 g_cancellable_cancel (priv->cancellable);
1505 g_object_unref (priv->cancellable);
1506 g_free (priv->attributes);
1508 G_OBJECT_CLASS (_gtk_folder_parent_class)->finalize (object);
1512 _gtk_folder_class_init (GtkFolderClass *class)
1514 GObjectClass *object_class = G_OBJECT_CLASS (class);
1516 object_class->set_property = gtk_folder_set_property;
1517 object_class->get_property = gtk_folder_get_property;
1518 object_class->constructed = gtk_folder_constructed;
1519 object_class->finalize = gtk_folder_finalize;
1521 g_object_class_install_property (object_class,
1523 g_param_spec_object ("file",
1525 "GFile for the folder",
1527 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1528 g_object_class_install_property (object_class,
1530 g_param_spec_object ("enumerator",
1532 "GFileEnumerator to list files",
1533 G_TYPE_FILE_ENUMERATOR,
1534 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1535 g_object_class_install_property (object_class,
1537 g_param_spec_string ("attributes",
1539 "Attributes to query for",
1541 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1542 folder_signals[FILES_ADDED] =
1543 g_signal_new ("files-added",
1544 G_TYPE_FROM_CLASS (object_class),
1546 G_STRUCT_OFFSET (GtkFolderClass, files_added),
1548 g_cclosure_marshal_VOID__POINTER,
1549 G_TYPE_NONE, 1, G_TYPE_POINTER);
1550 folder_signals[FILES_REMOVED] =
1551 g_signal_new ("files-removed",
1552 G_TYPE_FROM_CLASS (object_class),
1554 G_STRUCT_OFFSET (GtkFolderClass, files_removed),
1556 g_cclosure_marshal_VOID__POINTER,
1557 G_TYPE_NONE, 1, G_TYPE_POINTER);
1558 folder_signals[FILES_CHANGED] =
1559 g_signal_new ("files-changed",
1560 G_TYPE_FROM_CLASS (object_class),
1562 G_STRUCT_OFFSET (GtkFolderClass, files_changed),
1564 g_cclosure_marshal_VOID__POINTER,
1565 G_TYPE_NONE, 1, G_TYPE_POINTER);
1566 folder_signals[FINISHED_LOADING] =
1567 g_signal_new ("finished-loading",
1568 G_TYPE_FROM_CLASS (object_class),
1570 G_STRUCT_OFFSET (GtkFolderClass, finished_loading),
1572 g_cclosure_marshal_VOID__VOID,
1574 folder_signals[DELETED] =
1575 g_signal_new ("deleted",
1576 G_TYPE_FROM_CLASS (object_class),
1578 G_STRUCT_OFFSET (GtkFolderClass, deleted),
1580 g_cclosure_marshal_VOID__VOID,
1583 g_type_class_add_private (object_class, sizeof (GtkFolderPrivate));
1587 _gtk_folder_init (GtkFolder *folder)
1589 GtkFolderPrivate *priv;
1591 folder->priv = G_TYPE_INSTANCE_GET_PRIVATE (folder,
1594 priv = folder->priv;
1595 priv->children = g_hash_table_new_full (g_file_hash,
1596 (GEqualFunc) g_file_equal,
1597 (GDestroyNotify) g_object_unref,
1598 (GDestroyNotify) g_object_unref);
1599 priv->cancellable = g_cancellable_new ();
1603 gtk_folder_set_finished_loading (GtkFolder *folder,
1604 gboolean finished_loading)
1606 GtkFolderPrivate *priv = folder->priv;
1608 priv->finished_loading = (finished_loading == TRUE);
1610 gdk_threads_enter ();
1611 g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0);
1612 gdk_threads_leave ();
1616 gtk_folder_add_file (GtkFolder *folder,
1620 GtkFolderPrivate *priv = folder->priv;
1622 g_hash_table_insert (priv->children,
1623 g_object_ref (file),
1624 g_object_ref (info));
1628 _gtk_folder_list_children (GtkFolder *folder)
1630 GtkFolderPrivate *priv = folder->priv;
1631 GList *files, *elem;
1632 GSList *children = NULL;
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 = folder->priv;
1652 info = g_hash_table_lookup (priv->children, file);
1657 return g_object_ref (info);
1661 _gtk_folder_is_finished_loading (GtkFolder *folder)
1663 return folder->priv->finished_loading;
1666 /* GtkFileSystemVolume public methods */
1668 _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
1670 DEBUG ("volume_get_display_name");
1672 if (IS_ROOT_VOLUME (volume))
1673 return g_strdup (_(root_volume_token));
1674 if (G_IS_DRIVE (volume))
1675 return g_drive_get_name (G_DRIVE (volume));
1676 else if (G_IS_MOUNT (volume))
1677 return g_mount_get_name (G_MOUNT (volume));
1678 else if (G_IS_VOLUME (volume))
1679 return g_volume_get_name (G_VOLUME (volume));
1685 _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
1689 DEBUG ("volume_is_mounted");
1691 if (IS_ROOT_VOLUME (volume))
1696 if (G_IS_MOUNT (volume))
1698 else if (G_IS_VOLUME (volume))
1702 mount = g_volume_get_mount (G_VOLUME (volume));
1707 g_object_unref (mount);
1715 _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
1719 DEBUG ("volume_get_base");
1721 if (IS_ROOT_VOLUME (volume))
1722 return g_file_new_for_uri ("file:///");
1724 if (G_IS_MOUNT (volume))
1725 file = g_mount_get_root (G_MOUNT (volume));
1726 else if (G_IS_VOLUME (volume))
1730 mount = g_volume_get_mount (G_VOLUME (volume));
1734 file = g_mount_get_root (mount);
1735 g_object_unref (mount);
1743 get_pixbuf_from_gicon (GIcon *icon,
1749 GtkIconTheme *icon_theme;
1750 GtkIconInfo *icon_info;
1753 screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1754 icon_theme = gtk_icon_theme_get_for_screen (screen);
1756 icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1759 GTK_ICON_LOOKUP_USE_BUILTIN);
1764 pixbuf = gtk_icon_info_load_icon (icon_info, error);
1765 gtk_icon_info_free (icon_info);
1771 _gtk_file_system_volume_render_icon (GtkFileSystemVolume *volume,
1779 DEBUG ("volume_get_icon_name");
1781 if (IS_ROOT_VOLUME (volume))
1782 icon = g_themed_icon_new ("drive-harddisk");
1783 else if (G_IS_DRIVE (volume))
1784 icon = g_drive_get_icon (G_DRIVE (volume));
1785 else if (G_IS_VOLUME (volume))
1786 icon = g_volume_get_icon (G_VOLUME (volume));
1787 else if (G_IS_MOUNT (volume))
1788 icon = g_mount_get_icon (G_MOUNT (volume));
1793 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
1795 g_object_unref (icon);
1800 GtkFileSystemVolume *
1801 _gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
1803 if (IS_ROOT_VOLUME (volume))
1806 if (G_IS_MOUNT (volume) ||
1807 G_IS_VOLUME (volume) ||
1808 G_IS_DRIVE (volume))
1809 g_object_ref (volume);
1815 _gtk_file_system_volume_unref (GtkFileSystemVolume *volume)
1817 /* Root volume doesn't need to be freed */
1818 if (IS_ROOT_VOLUME (volume))
1821 if (G_IS_MOUNT (volume) ||
1822 G_IS_VOLUME (volume) ||
1823 G_IS_DRIVE (volume))
1824 g_object_unref (volume);
1827 /* GFileInfo helper functions */
1829 _gtk_file_info_render_icon (GFileInfo *info,
1834 GdkPixbuf *pixbuf = NULL;
1835 const gchar *thumbnail_path;
1837 thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1840 pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
1841 icon_size, icon_size,
1846 icon = g_file_info_get_icon (info);
1849 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1853 /* Use general fallback for all files without icon */
1854 icon = g_themed_icon_new ("text-x-generic");
1855 pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1856 g_object_unref (icon);
1864 _gtk_file_info_consider_as_directory (GFileInfo *info)
1866 GFileType type = g_file_info_get_file_type (info);
1868 return (type == G_FILE_TYPE_DIRECTORY ||
1869 type == G_FILE_TYPE_MOUNTABLE ||
1870 type == G_FILE_TYPE_SHORTCUT);