X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkfilesystem.c;h=2fdfa3df9dd448a1d14a08bb376e80c055e8a856;hb=HEAD;hp=b679f6c725901e5131bf518d18754fb040f56663;hpb=0a876523eba87e4ade1e18437b9cf47c3d54dfde;p=~andy%2Fgtk diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c index b679f6c72..2fdfa3df9 100644 --- a/gtk/gtkfilesystem.c +++ b/gtk/gtkfilesystem.c @@ -1,1208 +1,1288 @@ /* GTK - The GIMP Toolkit - * gtkfilesystem.c: Abstract file system interfaces + * gtkfilesystem.c: Filesystem abstraction functions. * Copyright (C) 2003, Red Hat, Inc. + * Copyright (C) 2007-2008 Carlos Garnacho * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . + * + * Authors: Carlos Garnacho */ -#include -#include +#include "config.h" + +#include + +#include + +#include "gtkfilechooser.h" #include "gtkfilesystem.h" #include "gtkicontheme.h" -#include "gtkmodules.h" -#include "gtkintl.h" -#include "gtkalias.h" +#include "gtkprivate.h" -#include +/* #define DEBUG_MODE */ +#ifdef DEBUG_MODE +#define DEBUG(x) g_debug (x); +#else +#define DEBUG(x) +#endif -struct _GtkFileInfo -{ - GtkFileTime modification_time; - gint64 size; - gchar *display_name; - gchar *display_key; - gchar *mime_type; - guint is_folder : 1; - guint is_hidden : 1; +#define FILES_PER_QUERY 100 + +/* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are + * really pointers to GDrive, GVolume or GMount objects. We need an extra + * token for the fake "File System" volume. So, we'll return a pointer to + * this particular string. + */ +static const gchar *root_volume_token = N_("File System"); +#define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token) + +enum { + PROP_0, + PROP_FILE, + PROP_ENUMERATOR, + PROP_ATTRIBUTES }; -static void gtk_file_system_base_init (gpointer g_class); -static void gtk_file_folder_base_init (gpointer g_class); +enum { + BOOKMARKS_CHANGED, + VOLUMES_CHANGED, + FS_LAST_SIGNAL +}; -GQuark -gtk_file_system_error_quark (void) -{ - static GQuark quark = 0; - if (quark == 0) - quark = g_quark_from_static_string ("gtk-file-system-error-quark"); - return quark; -} +enum { + FILES_ADDED, + FILES_REMOVED, + FILES_CHANGED, + FINISHED_LOADING, + DELETED, + FOLDER_LAST_SIGNAL +}; + +static guint fs_signals [FS_LAST_SIGNAL] = { 0, }; + +typedef struct AsyncFuncData AsyncFuncData; -/***************************************** - * GtkFileInfo * - *****************************************/ -GType -gtk_file_info_get_type (void) +struct GtkFileSystemPrivate { - static GType our_type = 0; - - if (our_type == 0) - our_type = g_boxed_type_register_static (I_("GtkFileInfo"), - (GBoxedCopyFunc) gtk_file_info_copy, - (GBoxedFreeFunc) gtk_file_info_free); + GVolumeMonitor *volume_monitor; - return our_type; -} + /* This list contains elements that can be + * of type GDrive, GVolume and GMount + */ + GSList *volumes; + + /* This list contains GtkFileSystemBookmark structs */ + GSList *bookmarks; -GtkFileInfo * -gtk_file_info_new (void) + GFileMonitor *bookmarks_monitor; +}; + +struct AsyncFuncData { - GtkFileInfo *info; - - info = g_new0 (GtkFileInfo, 1); + GtkFileSystem *file_system; + GFile *file; + GCancellable *cancellable; + gchar *attributes; - return info; -} + gpointer callback; + gpointer data; +}; -GtkFileInfo * -gtk_file_info_copy (GtkFileInfo *info) +struct GtkFileSystemBookmark { - GtkFileInfo *new_info; + GFile *file; + gchar *label; +}; - g_return_val_if_fail (info != NULL, NULL); +G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT) - new_info = g_memdup (info, sizeof (GtkFileInfo)); - if (new_info->display_name) - new_info->display_name = g_strdup (new_info->display_name); - if (new_info->mime_type) - new_info->mime_type = g_strdup (new_info->mime_type); - return new_info; +/* GtkFileSystemBookmark methods */ +void +_gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark) +{ + g_object_unref (bookmark->file); + g_free (bookmark->label); + g_slice_free (GtkFileSystemBookmark, bookmark); } -void -gtk_file_info_free (GtkFileInfo *info) +/* GtkFileSystem methods */ +static void +volumes_changed (GVolumeMonitor *volume_monitor, + gpointer volume, + gpointer user_data) { - g_return_if_fail (info != NULL); + GtkFileSystem *file_system; - if (info->display_name) - g_free (info->display_name); - if (info->mime_type) - g_free (info->mime_type); - if (info->display_key) - g_free (info->display_key); + gdk_threads_enter (); - g_free (info); + file_system = GTK_FILE_SYSTEM (user_data); + g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume); + gdk_threads_leave (); } -G_CONST_RETURN gchar * -gtk_file_info_get_display_name (const GtkFileInfo *info) +static void +gtk_file_system_dispose (GObject *object) { - g_return_val_if_fail (info != NULL, NULL); - - return info->display_name; -} + GtkFileSystem *file_system = GTK_FILE_SYSTEM (object); + GtkFileSystemPrivate *priv = file_system->priv; -/** - * gtk_file_info_get_display_key: - * @info: a #GtkFileInfo - * - * Returns for the collation key for the display name for @info. - * This is useful when sorting a bunch of #GtkFileInfo structures - * since the collate key will be only computed once. - * - * Return value: The collate key for the display name, or %NULL - * if the display name hasn't been set. - **/ -G_CONST_RETURN gchar * -gtk_file_info_get_display_key (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); + DEBUG ("dispose"); - if (!info->display_key && info->display_name) + if (priv->volumes) { - /* Since info->display_key is only a cache, we cast off the const - */ - ((GtkFileInfo *)info)->display_key = g_utf8_collate_key_for_filename (info->display_name, -1); + g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL); + g_slist_free (priv->volumes); + priv->volumes = NULL; + } + + if (priv->volume_monitor) + { + g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object); + g_object_unref (priv->volume_monitor); + priv->volume_monitor = NULL; } - - return info->display_key; + + G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object); } -void -gtk_file_info_set_display_name (GtkFileInfo *info, - const gchar *display_name) +static void +gtk_file_system_finalize (GObject *object) { - g_return_if_fail (info != NULL); + GtkFileSystem *file_system = GTK_FILE_SYSTEM (object); + GtkFileSystemPrivate *priv = file_system->priv; - if (display_name == info->display_name) - return; + DEBUG ("finalize"); + + if (priv->bookmarks_monitor) + g_object_unref (priv->bookmarks_monitor); - if (info->display_name) - g_free (info->display_name); - if (info->display_key) + if (priv->bookmarks) { - g_free (info->display_key); - info->display_key = NULL; + g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL); + g_slist_free (priv->bookmarks); } - info->display_name = g_strdup (display_name); + G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object); } -gboolean -gtk_file_info_get_is_folder (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, FALSE); - - return info->is_folder; +static void +_gtk_file_system_class_init (GtkFileSystemClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = gtk_file_system_dispose; + object_class->finalize = gtk_file_system_finalize; + + fs_signals[BOOKMARKS_CHANGED] = + g_signal_new ("bookmarks-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + fs_signals[VOLUMES_CHANGED] = + g_signal_new ("volumes-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate)); } -void -gtk_file_info_set_is_folder (GtkFileInfo *info, - gboolean is_folder) +static GFile * +get_legacy_bookmarks_file (void) { - g_return_if_fail (info != NULL); + GFile *file; + gchar *filename; - info->is_folder = is_folder != FALSE; -} + filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL); + file = g_file_new_for_path (filename); + g_free (filename); -gboolean -gtk_file_info_get_is_hidden (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, FALSE); - - return info->is_hidden; + return file; } -void -gtk_file_info_set_is_hidden (GtkFileInfo *info, - gboolean is_hidden) +static GFile * +get_bookmarks_file (void) { - g_return_if_fail (info != NULL); + GFile *file; + gchar *filename; - info->is_hidden = is_hidden != FALSE; -} + filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL); + file = g_file_new_for_path (filename); + g_free (filename); -G_CONST_RETURN gchar * -gtk_file_info_get_mime_type (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - - return info->mime_type; + return file; } -void -gtk_file_info_set_mime_type (GtkFileInfo *info, - const gchar *mime_type) +static GSList * +read_bookmarks (GFile *file) { - g_return_if_fail (info != NULL); - - if (info->mime_type) - g_free (info->mime_type); + gchar *contents; + gchar **lines, *space; + GSList *bookmarks = NULL; + gint i; - info->mime_type = g_strdup (mime_type); -} + if (!g_file_load_contents (file, NULL, &contents, + NULL, NULL, NULL)) + return NULL; -GtkFileTime -gtk_file_info_get_modification_time (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, 0); + lines = g_strsplit (contents, "\n", -1); + + for (i = 0; lines[i]; i++) + { + GtkFileSystemBookmark *bookmark; + + if (!*lines[i]) + continue; + + if (!g_utf8_validate (lines[i], -1, NULL)) + continue; + + bookmark = g_slice_new0 (GtkFileSystemBookmark); + + if ((space = strchr (lines[i], ' ')) != NULL) + { + space[0] = '\0'; + bookmark->label = g_strdup (space + 1); + } + + bookmark->file = g_file_new_for_uri (lines[i]); + bookmarks = g_slist_prepend (bookmarks, bookmark); + } - return info->modification_time; + bookmarks = g_slist_reverse (bookmarks); + g_strfreev (lines); + g_free (contents); + + return bookmarks; } -void -gtk_file_info_set_modification_time (GtkFileInfo *info, - GtkFileTime modification_time) +static void +save_bookmarks (GFile *bookmarks_file, + GSList *bookmarks) { - g_return_if_fail (info != NULL); - - info->modification_time = modification_time; + GError *error = NULL; + GString *contents; + GSList *l; + GFile *parent_file; + gchar *path; + + contents = g_string_new (""); + + for (l = bookmarks; l; l = l->next) + { + GtkFileSystemBookmark *bookmark = l->data; + gchar *uri; + + uri = g_file_get_uri (bookmark->file); + if (!uri) + continue; + + g_string_append (contents, uri); + + if (bookmark->label) + g_string_append_printf (contents, " %s", bookmark->label); + + g_string_append_c (contents, '\n'); + g_free (uri); + } + + parent_file = g_file_get_parent (bookmarks_file); + path = g_file_get_path (parent_file); + if (g_mkdir_with_parents (path, 0700) == 0) + { + if (!g_file_replace_contents (bookmarks_file, + contents->str, + strlen (contents->str), + NULL, FALSE, 0, NULL, + NULL, &error)) + { + g_critical ("%s", error->message); + g_error_free (error); + } + } + g_free (path); + g_object_unref (parent_file); + g_string_free (contents, TRUE); } -gint64 -gtk_file_info_get_size (const GtkFileInfo *info) +static void +bookmarks_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer data) { - g_return_val_if_fail (info != NULL, 0); - - return info->size; + GtkFileSystem *file_system = GTK_FILE_SYSTEM (data); + GtkFileSystemPrivate *priv = file_system->priv; + + switch (event) + { + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_DELETED: + g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL); + g_slist_free (priv->bookmarks); + + priv->bookmarks = read_bookmarks (file); + + gdk_threads_enter (); + g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0); + gdk_threads_leave (); + break; + default: + /* ignore at the moment */ + break; + } } -void -gtk_file_info_set_size (GtkFileInfo *info, - gint64 size) +static gboolean +mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount) { - g_return_if_fail (info != NULL); - g_return_if_fail (size >= 0); - - info->size = size; -} + GList *l; + GFile *mount_root; + gboolean ret; + ret = FALSE; -/***************************************** - * GtkFileSystem * - *****************************************/ -GType -gtk_file_system_get_type (void) -{ - static GType file_system_type = 0; + mount_root = g_mount_get_root (mount); - if (!file_system_type) + for (l = volumes; l != NULL; l = l->next) { - static const GTypeInfo file_system_info = - { - sizeof (GtkFileSystemIface), /* class_size */ - gtk_file_system_base_init, /* base_init */ - NULL, /* base_finalize */ - }; - - file_system_type = g_type_register_static (G_TYPE_INTERFACE, - I_("GtkFileSystem"), - &file_system_info, 0); - - g_type_interface_add_prerequisite (file_system_type, G_TYPE_OBJECT); + GVolume *volume = G_VOLUME (l->data); + GFile *volume_activation_root; + + volume_activation_root = g_volume_get_activation_root (volume); + if (volume_activation_root != NULL) + { + if (g_file_has_prefix (volume_activation_root, mount_root)) + { + ret = TRUE; + g_object_unref (volume_activation_root); + break; + } + g_object_unref (volume_activation_root); + } } - return file_system_type; + g_object_unref (mount_root); + return ret; } static void -gtk_file_system_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; +get_volumes_list (GtkFileSystem *file_system) +{ + GtkFileSystemPrivate *priv = file_system->priv; + GList *l, *ll; + GList *drives; + GList *volumes; + GList *mounts; + GDrive *drive; + GVolume *volume; + GMount *mount; + + if (priv->volumes) + { + g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL); + g_slist_free (priv->volumes); + priv->volumes = NULL; + } + + /* first go through all connected drives */ + drives = g_volume_monitor_get_connected_drives (priv->volume_monitor); + + for (l = drives; l != NULL; l = l->next) + { + drive = l->data; + volumes = g_drive_get_volumes (drive); + + if (volumes) + { + for (ll = volumes; ll != NULL; ll = ll->next) + { + volume = ll->data; + mount = g_volume_get_mount (volume); + + if (mount) + { + /* Show mounted volume */ + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount)); + g_object_unref (mount); + } + else + { + /* Do show the unmounted volumes in the sidebar; + * this is so the user can mount it (in case automounting + * is off). + * + * Also, even if automounting is enabled, this gives a visual + * cue that the user should remember to yank out the media if + * he just unmounted it. + */ + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume)); + } + + g_object_unref (volume); + } - if (!initialized) + g_list_free (volumes); + } + else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive)) + { + /* If the drive has no mountable volumes and we cannot detect media change.. we + * display the drive in the sidebar so the user can manually poll the drive by + * right clicking and selecting "Rescan..." + * + * This is mainly for drives like floppies where media detection doesn't + * work.. but it's also for human beings who like to turn off media detection + * in the OS to save battery juice. + */ + + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive)); + } + + g_object_unref (drive); + } + + g_list_free (drives); + + /* add all volumes that is not associated with a drive */ + volumes = g_volume_monitor_get_volumes (priv->volume_monitor); + + for (l = volumes; l != NULL; l = l->next) { - GType iface_type = G_TYPE_FROM_INTERFACE (g_class); - - g_signal_new (I_("volumes-changed"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileSystemIface, volumes_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - g_signal_new (I_("bookmarks-changed"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileSystemIface, bookmarks_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - initialized = TRUE; + volume = l->data; + drive = g_volume_get_drive (volume); + + if (drive) + { + g_object_unref (drive); + continue; + } + + mount = g_volume_get_mount (volume); + + if (mount) + { + /* show this mount */ + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount)); + g_object_unref (mount); + } + else + { + /* see comment above in why we add an icon for a volume */ + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume)); + } + + g_object_unref (volume); } -} -GSList * -gtk_file_system_list_volumes (GtkFileSystem *file_system) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (priv->volume_monitor); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_volumes (file_system); -} + for (l = mounts; l != NULL; l = l->next) + { + mount = l->data; + volume = g_mount_get_volume (mount); -GtkFileFolder * -gtk_file_system_get_folder (GtkFileSystem *file_system, - const GtkFilePath *path, - GtkFileInfoType types, - GError **error) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + if (volume) + { + g_object_unref (volume); + continue; + } - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, error); -} + /* if there's exists one or more volumes with an activation root inside the mount, + * don't display the mount + */ + if (mount_referenced_by_volume_activation_root (volumes, mount)) + { + g_object_unref (mount); + continue; + } + + /* show this mount */ + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount)); + g_object_unref (mount); + } -gboolean -gtk_file_system_create_folder(GtkFileSystem *file_system, - const GtkFilePath *path, - GError **error) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_list_free (volumes); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, error); + g_list_free (mounts); } -/** - * gtk_file_system_get_volume_for_path: - * @file_system: a #GtkFileSystem - * @path: a #GtkFilePath - * - * Queries the file system volume that corresponds to a specific path. - * There might not be a volume for all paths (consinder for instance remote - * shared), so this can return NULL. - * - * Return value: the #GtkFileSystemVolume that corresponds to the specified - * @path, or NULL if there is no such volume. You should free this value with - * gtk_file_system_volume_free(). - **/ -GtkFileSystemVolume * -gtk_file_system_get_volume_for_path (GtkFileSystem *file_system, - const GtkFilePath *path) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, NULL); +static void +_gtk_file_system_init (GtkFileSystem *file_system) +{ + GtkFileSystemPrivate *priv; + GFile *bookmarks_file; + GError *error = NULL; + + DEBUG ("init"); + + file_system->priv = G_TYPE_INSTANCE_GET_PRIVATE (file_system, + GTK_TYPE_FILE_SYSTEM, + GtkFileSystemPrivate); + priv = file_system->priv; + + /* Volumes */ + priv->volume_monitor = g_volume_monitor_get (); + + g_signal_connect (priv->volume_monitor, "mount-added", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "mount-removed", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "mount-changed", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "volume-added", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "volume-removed", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "volume-changed", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "drive-connected", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "drive-disconnected", + G_CALLBACK (volumes_changed), file_system); + g_signal_connect (priv->volume_monitor, "drive-changed", + G_CALLBACK (volumes_changed), file_system); + + /* Bookmarks */ + bookmarks_file = get_bookmarks_file (); + priv->bookmarks = read_bookmarks (bookmarks_file); + if (!priv->bookmarks) + { + GFile *legacy_bookmarks_file; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_volume_for_path (file_system, path); -} + /* Read the legacy one and write it to the new one */ + legacy_bookmarks_file = get_legacy_bookmarks_file (); + priv->bookmarks = read_bookmarks (legacy_bookmarks_file); + save_bookmarks (bookmarks_file, priv->bookmarks); -/** - * gtk_file_system_volume_free: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * - * Frees a #GtkFileSystemVolume structure as returned by - * gtk_file_system_list_volumes(). - **/ -void -gtk_file_system_volume_free (GtkFileSystem *file_system, - GtkFileSystemVolume *volume) -{ - g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system)); - g_return_if_fail (volume != NULL); + g_object_unref (legacy_bookmarks_file); + } - GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_free (file_system, volume); + priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file, + G_FILE_MONITOR_NONE, + NULL, &error); + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + else + g_signal_connect (priv->bookmarks_monitor, "changed", + G_CALLBACK (bookmarks_file_changed), file_system); + + g_object_unref (bookmarks_file); } -/** - * gtk_file_system_volume_get_base_path: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * - * Queries the base path for a volume. For example, a CD-ROM device may yield a - * path of "/mnt/cdrom". - * - * Return value: a #GtkFilePath with the base mount path of the specified - * @volume. - **/ -GtkFilePath * -gtk_file_system_volume_get_base_path (GtkFileSystem *file_system, - GtkFileSystemVolume *volume) +/* GtkFileSystem public methods */ +GtkFileSystem * +_gtk_file_system_new (void) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (volume != NULL, NULL); - - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_base_path (file_system, volume); + return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL); } -/** - * gtk_file_system_volume_get_is_mounted: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * - * Queries whether a #GtkFileSystemVolume is mounted or not. If it is not, it - * can be mounted with gtk_file_system_volume_mount(). - * - * Return value: TRUE if the @volume is mounted, FALSE otherwise. - **/ -gboolean -gtk_file_system_volume_get_is_mounted (GtkFileSystem *file_system, - GtkFileSystemVolume *volume) +GSList * +_gtk_file_system_list_volumes (GtkFileSystem *file_system) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (volume != NULL, FALSE); + GtkFileSystemPrivate *priv = file_system->priv; + GSList *list; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_is_mounted (file_system, volume); -} + DEBUG ("list_volumes"); -/** - * gtk_file_system_volume_mount: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * @error: location to store error, or %NULL - * - * Tries to mount an unmounted volume. This may cause the "volumes-changed" - * signal in the @file_system to be emitted. - * - * Return value: TRUE if the @volume was mounted successfully, FALSE otherwise. - **/ -gboolean -gtk_file_system_volume_mount (GtkFileSystem *file_system, - GtkFileSystemVolume *volume, - GError **error) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (volume != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + get_volumes_list (file_system); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_mount (file_system, volume, error); -} + list = g_slist_copy (priv->volumes); -/** - * gtk_file_system_volume_get_display_name: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * - * Queries the human-readable name for a @volume. This string can be displayed - * in a list of volumes that can be accessed, for example. - * - * Return value: A string with the human-readable name for a #GtkFileSystemVolume. - **/ -char * -gtk_file_system_volume_get_display_name (GtkFileSystem *file_system, - GtkFileSystemVolume *volume) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (volume != NULL, NULL); +#ifndef G_OS_WIN32 + /* Prepend root volume */ + list = g_slist_prepend (list, (gpointer) root_volume_token); +#endif - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_display_name (file_system, volume); + return list; } -/** - * gtk_file_system_volume_render_icon: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * @widget: Reference widget to render icons. - * @pixel_size: Size of the icon. - * @error: location to store error, or %NULL - * - * Renders an icon suitable for a file #GtkFileSystemVolume. - * - * Return value: A #GdkPixbuf containing an icon, or NULL if the icon could not - * be rendered. In the latter case, the @error value will be set as - * appropriate. - **/ -GdkPixbuf * -gtk_file_system_volume_render_icon (GtkFileSystem *file_system, - GtkFileSystemVolume *volume, - GtkWidget *widget, - gint pixel_size, - GError **error) +GSList * +_gtk_file_system_list_bookmarks (GtkFileSystem *file_system) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (volume != NULL, NULL); - g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - g_return_val_if_fail (pixel_size > 0, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_render_icon (file_system, - volume, - widget, - pixel_size, - error); -} + GtkFileSystemPrivate *priv = file_system->priv; + GSList *bookmarks, *files = NULL; -/** - * gtk_file_system_get_parent: - * @file_system: a #GtkFileSystem - * @path: base path name - * @parent: location to store parent path name - * @error: location to store error, or %NULL - * - * Gets the name of the parent folder of a path. If the path has no parent, as when - * you request the parent of a file system root, then @parent will be set to %NULL. - * - * Return value: %TRUE if the operation was successful: @parent will be set to - * the name of the @path's parent, or to %NULL if @path is already a file system - * root. If the operation fails, this function returns %FALSE, sets @parent to - * NULL, and sets the @error value if it is specified. - **/ -gboolean -gtk_file_system_get_parent (GtkFileSystem *file_system, - const GtkFilePath *path, - GtkFilePath **parent, - GError **error) -{ - gboolean result; - - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (parent != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + DEBUG ("list_bookmarks"); + + bookmarks = priv->bookmarks; + + while (bookmarks) + { + GtkFileSystemBookmark *bookmark; - *parent = NULL; + bookmark = bookmarks->data; + bookmarks = bookmarks->next; - result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, path, parent, error); - g_assert (result || *parent == NULL); + files = g_slist_prepend (files, g_object_ref (bookmark->file)); + } - return result; + return g_slist_reverse (files); } -GtkFilePath * -gtk_file_system_make_path (GtkFileSystem *file_system, - const GtkFilePath *base_path, - const gchar *display_name, - GError **error) +static void +free_async_data (AsyncFuncData *async_data) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (base_path != NULL, NULL); - g_return_val_if_fail (display_name != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_object_unref (async_data->file_system); + g_object_unref (async_data->file); + g_object_unref (async_data->cancellable); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_path (file_system, base_path, display_name, error); + g_free (async_data->attributes); + g_free (async_data); } -/** - * gtk_file_system_parse: - * @file_system: a #GtkFileSystem - * @base_path: reference folder with respect to which relative - * paths should be interpreted. - * @str: the string to parse - * @folder: location to store folder portion of result, or %NULL - * @file_part: location to store file portion of result, or %NULL - * @error: location to store error, or %NULL - * - * Given a string entered by a user, parse it (possibly using - * heuristics) into a folder path and a UTF-8 encoded - * filename part. (Suitable for passing to gtk_file_system_make_path()) - * - * Note that the returned filename point may point to a subfolder - * of the returned folder. Adding a trailing path separator is needed - * to enforce the interpretation as a folder name. - * - * If parsing fails because the syntax of @str is not understood, - * and error of type GTK_FILE_SYSTEM_ERROR_BAD_FILENAME will - * be set in @error and %FALSE returned. - * - * If parsing fails because a path was encountered that doesn't - * exist on the filesystem, then an error of type - * %GTK_FILE_SYSTEM_ERROR_NONEXISTENT will be set in @error - * and %FALSE returned. (This only applies to parsing relative paths, - * not to interpretation of @file_part. No check is made as - * to whether @file_part exists.) - * - * Return value: %TRUE if the parsing succeeds, otherwise, %FALSE. - **/ -gboolean -gtk_file_system_parse (GtkFileSystem *file_system, - const GtkFilePath *base_path, - const gchar *str, - GtkFilePath **folder, - gchar **file_part, - GError **error) +static void +query_info_callback (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - GtkFilePath *tmp_folder = NULL; - gchar *tmp_file_part = NULL; - gboolean result; - - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (base_path != NULL, FALSE); - g_return_val_if_fail (str != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->parse (file_system, base_path, str, - &tmp_folder, &tmp_file_part, - error); - g_assert (result || (tmp_folder == NULL && tmp_file_part == NULL)); - - if (folder) - *folder = tmp_folder; - else - gtk_file_path_free (tmp_folder); + AsyncFuncData *async_data; + GError *error = NULL; + GFileInfo *file_info; + GFile *file; - if (file_part) - *file_part = tmp_file_part; - else - g_free (tmp_file_part); + DEBUG ("query_info_callback"); - return result; -} + file = G_FILE (source_object); + async_data = (AsyncFuncData *) user_data; + file_info = g_file_query_info_finish (file, result, &error); + if (async_data->callback) + { + gdk_threads_enter (); + ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable, + file_info, error, async_data->data); + gdk_threads_leave (); + } -gchar * -gtk_file_system_path_to_uri (GtkFileSystem *file_system, - const GtkFilePath *path) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, NULL); - - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_uri (file_system, path); -} + if (file_info) + g_object_unref (file_info); -gchar * -gtk_file_system_path_to_filename (GtkFileSystem *file_system, - const GtkFilePath *path) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, NULL); - - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_filename (file_system, path); -} + if (error) + g_error_free (error); -GtkFilePath * -gtk_file_system_uri_to_path (GtkFileSystem *file_system, - const gchar *uri) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (uri != NULL, NULL); - - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->uri_to_path (file_system, uri); + free_async_data (async_data); } -GtkFilePath * -gtk_file_system_filename_to_path (GtkFileSystem *file_system, - const gchar *filename) +GCancellable * +_gtk_file_system_get_info (GtkFileSystem *file_system, + GFile *file, + const gchar *attributes, + GtkFileSystemGetInfoCallback callback, + gpointer data) { + GCancellable *cancellable; + AsyncFuncData *async_data; + g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->filename_to_path (file_system, filename); -} + cancellable = g_cancellable_new (); -/** - * gtk_file_system_path_is_local: - * @filesystem: a #GtkFileSystem - * @path: A #GtkFilePath from that filesystem - * - * Checks whether a #GtkFilePath is local; that is whether - * gtk_file_system_path_to_filename would return non-%NULL. - * - * Return value: %TRUE if the path is loca - **/ -gboolean -gtk_file_system_path_is_local (GtkFileSystem *file_system, - const GtkFilePath *path) -{ - gchar *filename; - gboolean result; - - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); + async_data = g_new0 (AsyncFuncData, 1); + async_data->file_system = g_object_ref (file_system); + async_data->file = g_object_ref (file); + async_data->cancellable = g_object_ref (cancellable); - filename = gtk_file_system_path_to_filename (file_system, path); - result = filename != NULL; - g_free (filename); + async_data->callback = callback; + async_data->data = data; + + g_file_query_info_async (file, + attributes, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + cancellable, + query_info_callback, + async_data); - return result; + return cancellable; } -GdkPixbuf * -gtk_file_system_render_icon (GtkFileSystem *file_system, - const GtkFilePath *path, - GtkWidget *widget, - gint pixel_size, - GError **error) +static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, NULL); - g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - g_return_val_if_fail (pixel_size > 0, NULL); + AsyncFuncData *async_data; + GError *error = NULL; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->render_icon (file_system, path, widget, pixel_size, error); + g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error); + async_data = (AsyncFuncData *) user_data; + + gdk_threads_enter (); + ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, + (GtkFileSystemVolume *) source_object, + error, async_data->data); + gdk_threads_leave (); + + if (error) + g_error_free (error); } -/** - * gtk_file_system_insert_bookmark: - * @file_system: a #GtkFileSystem - * @path: path of the bookmark to add - * @position: index in the bookmarks list at which the @path should be inserted; use 0 - * for the beginning, and -1 or the number of bookmarks itself for the end of the list. - * @error: location to store error, or %NULL - * - * Adds a path for a folder to the user's bookmarks list. If the operation - * succeeds, the "bookmarks_changed" signal will be emitted. Bookmark paths are - * unique; if you try to insert a @path that already exists, the operation will - * fail and the @error will be set to #GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS. To - * reorder the list of bookmarks, use gtk_file_system_remove_bookmark() to - * remove the path in question, and call gtk_file_system_insert_bookmark() with - * the new position for the path. - * - * Return value: TRUE if the operation succeeds, FALSE otherwise. In the latter case, - * the @error value will be set. - **/ -gboolean -gtk_file_system_insert_bookmark (GtkFileSystem *file_system, - const GtkFilePath *path, - gint position, - GError **error) +static void +volume_mount_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); + AsyncFuncData *async_data; + GError *error = NULL; + + g_volume_mount_finish (G_VOLUME (source_object), result, &error); + async_data = (AsyncFuncData *) user_data; + + gdk_threads_enter (); + ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, + (GtkFileSystemVolume *) source_object, + error, async_data->data); + gdk_threads_leave (); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->insert_bookmark (file_system, path, position, error); + if (error) + g_error_free (error); } -/** - * gtk_file_system_remove_bookmark: - * @file_system: a #GtkFileSystem - * @path: path of the bookmark to remove - * @error: location to store error, or %NULL - * - * Removes a bookmark folder from the user's bookmarks list. If the operation - * succeeds, the "bookmarks_changed" signal will be emitted. If you try to remove - * a @path which does not exist in the bookmarks list, the operation will fail - * and the @error will be set to GTK_FILE_SYSTEM_ERROR_NONEXISTENT. - * - * Return value: TRUE if the operation succeeds, FALSE otherwise. In the latter - * case, the @error value will be set. - **/ -gboolean -gtk_file_system_remove_bookmark (GtkFileSystem *file_system, - const GtkFilePath *path, - GError **error) +GCancellable * +_gtk_file_system_mount_volume (GtkFileSystem *file_system, + GtkFileSystemVolume *volume, + GMountOperation *mount_operation, + GtkFileSystemVolumeMountCallback callback, + gpointer data) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); + GCancellable *cancellable; + AsyncFuncData *async_data; + gboolean handled = FALSE; + + DEBUG ("volume_mount"); + + cancellable = g_cancellable_new (); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->remove_bookmark (file_system, path, error); + async_data = g_new0 (AsyncFuncData, 1); + async_data->file_system = g_object_ref (file_system); + async_data->cancellable = g_object_ref (cancellable); + + async_data->callback = callback; + async_data->data = data; + + if (G_IS_DRIVE (volume)) + { + /* this path happens for drives that are not polled by the OS and where the last media + * check indicated that no media was available. So the thing to do here is to + * invoke poll_for_media() on the drive + */ + g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data); + handled = TRUE; + } + else if (G_IS_VOLUME (volume)) + { + g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data); + handled = TRUE; + } + + if (!handled) + free_async_data (async_data); + + return cancellable; } -/** - * gtk_file_system_list_bookmarks: - * @file_system: a #GtkFileSystem - * - * Queries the list of bookmarks in the file system. - * - * Return value: A list of #GtkFilePath, or NULL if there are no configured - * bookmarks. You should use gtk_file_paths_free() to free this list. - * - * See also: gtk_file_system_get_supports_bookmarks() - **/ -GSList * -gtk_file_system_list_bookmarks (GtkFileSystem *file_system) +static void +enclosing_volume_mount_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); + GtkFileSystemVolume *volume; + AsyncFuncData *async_data; + GError *error = NULL; + + async_data = (AsyncFuncData *) user_data; + g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error); + volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object)); + + /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */ + /* Better than doing query_info with additional I/O every time. */ + if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED)) + g_clear_error (&error); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_bookmarks (file_system); + gdk_threads_enter (); + ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume, + error, async_data->data); + gdk_threads_leave (); + + if (error) + g_error_free (error); + + _gtk_file_system_volume_unref (volume); } -/** - * gtk_file_system_get_bookmark_label: - * @file_system: a #GtkFileSystem - * @path: path of the bookmark - * - * Gets the label to display for a bookmark, or %NULL. - * - * Returns: the label for the bookmark @path - * - * Since: 2.8 - */ -gchar * -gtk_file_system_get_bookmark_label (GtkFileSystem *file_system, - const GtkFilePath *path) +GCancellable * +_gtk_file_system_mount_enclosing_volume (GtkFileSystem *file_system, + GFile *file, + GMountOperation *mount_operation, + GtkFileSystemVolumeMountCallback callback, + gpointer data) { - GtkFileSystemIface *iface; + GCancellable *cancellable; + AsyncFuncData *async_data; g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (G_IS_FILE (file), NULL); - iface = GTK_FILE_SYSTEM_GET_IFACE (file_system); - if (iface->get_bookmark_label) - return iface->get_bookmark_label (file_system, path); + DEBUG ("mount_enclosing_volume"); - return NULL; -} + cancellable = g_cancellable_new (); -/** - * gtk_file_system_set_bookmark_label: - * @file_system: a #GtkFileSystem - * @path: path of the bookmark - * @label: the label for the bookmark, or %NULL to display - * the path itself - * - * Sets the label to display for a bookmark. - * - * Since: 2.8 - */ -void -gtk_file_system_set_bookmark_label (GtkFileSystem *file_system, - const GtkFilePath *path, - const gchar *label) -{ - GtkFileSystemIface *iface; + async_data = g_new0 (AsyncFuncData, 1); + async_data->file_system = g_object_ref (file_system); + async_data->file = g_object_ref (file); + async_data->cancellable = g_object_ref (cancellable); - g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system)); - g_return_if_fail (path != NULL); + async_data->callback = callback; + async_data->data = data; - iface = GTK_FILE_SYSTEM_GET_IFACE (file_system); - if (iface->set_bookmark_label) - iface->set_bookmark_label (file_system, path, label); + g_file_mount_enclosing_volume (file, + G_MOUNT_MOUNT_NONE, + mount_operation, + cancellable, + enclosing_volume_mount_cb, + async_data); + return cancellable; } -/***************************************** - * GtkFileFolder * - *****************************************/ -GType -gtk_file_folder_get_type (void) +gboolean +_gtk_file_system_insert_bookmark (GtkFileSystem *file_system, + GFile *file, + gint position, + GError **error) { - static GType file_folder_type = 0; + GtkFileSystemPrivate *priv = file_system->priv; + GSList *bookmarks; + GtkFileSystemBookmark *bookmark; + gboolean result = TRUE; + GFile *bookmarks_file; + + bookmarks = priv->bookmarks; - if (!file_folder_type) + while (bookmarks) { - static const GTypeInfo file_folder_info = - { - sizeof (GtkFileFolderIface), /* class_size */ - gtk_file_folder_base_init, /* base_init */ - NULL, /* base_finalize */ - }; - - file_folder_type = g_type_register_static (G_TYPE_INTERFACE, - I_("GtkFileFolder"), - &file_folder_info, 0); - - g_type_interface_add_prerequisite (file_folder_type, G_TYPE_OBJECT); - } + bookmark = bookmarks->data; + bookmarks = bookmarks->next; - return file_folder_type; -} + if (g_file_equal (bookmark->file, file)) + { + /* File is already in bookmarks */ + result = FALSE; + break; + } + } -static void -gtk_file_folder_base_init (gpointer g_class) -{ - static gboolean initialized = FALSE; - - if (!initialized) + if (!result) { - GType iface_type = G_TYPE_FROM_INTERFACE (g_class); - - g_signal_new (I_("deleted"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileFolderIface, deleted), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - g_signal_new (I_("files-added"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileFolderIface, files_added), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - g_signal_new (I_("files-changed"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileFolderIface, files_changed), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - g_signal_new (I_("files-removed"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileFolderIface, files_removed), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - g_signal_new (I_("finished-loading"), - iface_type, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileFolderIface, finished_loading), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - initialized = TRUE; + gchar *uri = g_file_get_uri (file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, + "%s already exists in the bookmarks list", + uri); + + g_free (uri); + + return FALSE; } + + bookmark = g_slice_new0 (GtkFileSystemBookmark); + bookmark->file = g_object_ref (file); + + priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position); + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, priv->bookmarks); + g_object_unref (bookmarks_file); + + g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0); + + return TRUE; } gboolean -gtk_file_folder_list_children (GtkFileFolder *folder, - GSList **children, - GError **error) +_gtk_file_system_remove_bookmark (GtkFileSystem *file_system, + GFile *file, + GError **error) { - gboolean result; - GSList *tmp_children = NULL; - - g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + GtkFileSystemPrivate *priv = file_system->priv; + GtkFileSystemBookmark *bookmark; + GSList *bookmarks; + gboolean result = FALSE; + GFile *bookmarks_file; - result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error); - g_assert (result || tmp_children == NULL); + if (!priv->bookmarks) + return FALSE; - if (children) - *children = tmp_children; - else - gtk_file_paths_free (tmp_children); + bookmarks = priv->bookmarks; - return result; -} + while (bookmarks) + { + bookmark = bookmarks->data; -GtkFileInfo * -gtk_file_folder_get_info (GtkFileFolder *folder, - const GtkFilePath *path, - GError **error) -{ - g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + if (g_file_equal (bookmark->file, file)) + { + result = TRUE; + priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks); + _gtk_file_system_bookmark_free (bookmark); + g_slist_free_1 (bookmarks); + break; + } - return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, path, error); -} + bookmarks = bookmarks->next; + } -gboolean -gtk_file_folder_is_finished_loading (GtkFileFolder *folder) -{ - GtkFileFolderIface *iface; + if (!result) + { + gchar *uri = g_file_get_uri (file); - g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), TRUE); + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + "%s does not exist in the bookmarks list", + uri); - iface = GTK_FILE_FOLDER_GET_IFACE (folder); - if (!iface->is_finished_loading) - return TRUE; - else - return iface->is_finished_loading (folder); -} + g_free (uri); + return FALSE; + } -/***************************************** - * GtkFilePath modules * - *****************************************/ + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, priv->bookmarks); + g_object_unref (bookmarks_file); -/* We make these real functions in case either copy or free are implemented as macros - */ -static gpointer -gtk_file_path_real_copy (gpointer boxed) -{ - return gtk_file_path_copy ((GtkFilePath *) boxed); -} + g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0); -static void -gtk_file_path_real_free (gpointer boxed) -{ - gtk_file_path_free (boxed); + return TRUE; } -GType -gtk_file_path_get_type (void) +gchar * +_gtk_file_system_get_bookmark_label (GtkFileSystem *file_system, + GFile *file) { - static GType our_type = 0; - - if (our_type == 0) - our_type = g_boxed_type_register_static (I_("GtkFilePath"), - (GBoxedCopyFunc) gtk_file_path_real_copy, - (GBoxedFreeFunc) gtk_file_path_real_free); + GtkFileSystemPrivate *priv = file_system->priv; + GSList *bookmarks; + gchar *label = NULL; - return our_type; -} + DEBUG ("get_bookmark_label"); + bookmarks = priv->bookmarks; -GSList * -gtk_file_paths_sort (GSList *paths) -{ - return g_slist_sort (paths, (GCompareFunc)strcmp); + while (bookmarks) + { + GtkFileSystemBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + if (g_file_equal (file, bookmark->file)) + { + label = g_strdup (bookmark->label); + break; + } + } + + return label; } -/** - * gtk_file_paths_copy: - * @paths: A #GSList of #GtkFilePath structures. - * - * Copies a list of #GtkFilePath structures. - * - * Return value: A copy of @paths. Since the contents of the list are copied as - * well, you should use gtk_file_paths_free() to free the result. - **/ -GSList * -gtk_file_paths_copy (GSList *paths) +void +_gtk_file_system_set_bookmark_label (GtkFileSystem *file_system, + GFile *file, + const gchar *label) { - GSList *head, *tail, *l; + GtkFileSystemPrivate *priv = file_system->priv; + gboolean changed = FALSE; + GFile *bookmarks_file; + GSList *bookmarks; - head = tail = NULL; + DEBUG ("set_bookmark_label"); - for (l = paths; l; l = l->next) - { - GtkFilePath *path; - GSList *node; + bookmarks = priv->bookmarks; - path = l->data; - node = g_slist_alloc (); + while (bookmarks) + { + GtkFileSystemBookmark *bookmark; - if (tail) - tail->next = node; - else - head = node; + bookmark = bookmarks->data; + bookmarks = bookmarks->next; - node->data = gtk_file_path_copy (path); - tail = node; + if (g_file_equal (file, bookmark->file)) + { + g_free (bookmark->label); + bookmark->label = g_strdup (label); + changed = TRUE; + break; + } } - return head; + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, priv->bookmarks); + g_object_unref (bookmarks_file); + + if (changed) + g_signal_emit_by_name (file_system, "bookmarks-changed", 0); } -void -gtk_file_paths_free (GSList *paths) +GtkFileSystemVolume * +_gtk_file_system_get_volume_for_file (GtkFileSystem *file_system, + GFile *file) { - GSList *tmp_list; + GMount *mount; - for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next) - gtk_file_path_free (tmp_list->data); + DEBUG ("get_volume_for_file"); - g_slist_free (paths); -} + mount = g_file_find_enclosing_mount (file, NULL, NULL); -/***************************************** - * GtkFileSystem modules * - *****************************************/ + if (!mount && g_file_is_native (file)) + return (GtkFileSystemVolume *) root_volume_token; -typedef struct _GtkFileSystemModule GtkFileSystemModule; -typedef struct _GtkFileSystemModuleClass GtkFileSystemModuleClass; + return (GtkFileSystemVolume *) mount; +} -struct _GtkFileSystemModule +/* GtkFileSystemVolume public methods */ +gchar * +_gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume) { - GTypeModule parent_instance; - - GModule *library; + DEBUG ("volume_get_display_name"); - void (*init) (GTypeModule *module); - void (*exit) (void); - GtkFileSystem * (*create) (void); + if (IS_ROOT_VOLUME (volume)) + return g_strdup (_(root_volume_token)); + if (G_IS_DRIVE (volume)) + return g_drive_get_name (G_DRIVE (volume)); + else if (G_IS_MOUNT (volume)) + return g_mount_get_name (G_MOUNT (volume)); + else if (G_IS_VOLUME (volume)) + return g_volume_get_name (G_VOLUME (volume)); - gchar *path; -}; + return NULL; +} -struct _GtkFileSystemModuleClass +gboolean +_gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume) { - GTypeModuleClass parent_class; -}; + gboolean mounted; -G_DEFINE_TYPE (GtkFileSystemModule, _gtk_file_system_module, G_TYPE_TYPE_MODULE); -#define GTK_TYPE_FILE_SYSTEM_MODULE (_gtk_file_system_module_get_type ()) -#define GTK_FILE_SYSTEM_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), GTK_TYPE_FILE_SYSTEM_MODULE, GtkFileSystemModule)) + DEBUG ("volume_is_mounted"); + if (IS_ROOT_VOLUME (volume)) + return TRUE; -static GSList *loaded_file_systems; + mounted = FALSE; -static gboolean -gtk_file_system_module_load (GTypeModule *module) -{ - GtkFileSystemModule *fs_module = GTK_FILE_SYSTEM_MODULE (module); - - fs_module->library = g_module_open (fs_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); - if (!fs_module->library) + if (G_IS_MOUNT (volume)) + mounted = TRUE; + else if (G_IS_VOLUME (volume)) { - g_warning (g_module_error()); - return FALSE; + GMount *mount; + + mount = g_volume_get_mount (G_VOLUME (volume)); + + if (mount) + { + mounted = TRUE; + g_object_unref (mount); + } } - - /* extract symbols from the lib */ - if (!g_module_symbol (fs_module->library, "fs_module_init", - (gpointer *)&fs_module->init) || - !g_module_symbol (fs_module->library, "fs_module_exit", - (gpointer *)&fs_module->exit) || - !g_module_symbol (fs_module->library, "fs_module_create", - (gpointer *)&fs_module->create)) + + return mounted; +} + +GFile * +_gtk_file_system_volume_get_root (GtkFileSystemVolume *volume) +{ + GFile *file = NULL; + + DEBUG ("volume_get_base"); + + if (IS_ROOT_VOLUME (volume)) + return g_file_new_for_uri ("file:///"); + + if (G_IS_MOUNT (volume)) + file = g_mount_get_root (G_MOUNT (volume)); + else if (G_IS_VOLUME (volume)) { - g_warning (g_module_error()); - g_module_close (fs_module->library); - - return FALSE; + GMount *mount; + + mount = g_volume_get_mount (G_VOLUME (volume)); + + if (mount) + { + file = g_mount_get_root (mount); + g_object_unref (mount); + } } - - /* call the filesystems's init function to let it */ - /* setup anything it needs to set up. */ - fs_module->init (module); - return TRUE; + return file; } -static void -gtk_file_system_module_unload (GTypeModule *module) +static GdkPixbuf * +get_pixbuf_from_gicon (GIcon *icon, + GtkWidget *widget, + gint icon_size, + GError **error) { - GtkFileSystemModule *fs_module = GTK_FILE_SYSTEM_MODULE (module); - - fs_module->exit(); + GdkScreen *screen; + GtkIconTheme *icon_theme; + GtkIconInfo *icon_info; + GdkPixbuf *pixbuf; + + screen = gtk_widget_get_screen (GTK_WIDGET (widget)); + icon_theme = gtk_icon_theme_get_for_screen (screen); + + icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme, + icon, + icon_size, + GTK_ICON_LOOKUP_USE_BUILTIN); - g_module_close (fs_module->library); - fs_module->library = NULL; + if (!icon_info) + return NULL; - fs_module->init = NULL; - fs_module->exit = NULL; - fs_module->create = NULL; + pixbuf = gtk_icon_info_load_icon (icon_info, error); + g_object_unref (icon_info); + + return pixbuf; } -/* This only will ever be called if an error occurs during - * initialization - */ -static void -gtk_file_system_module_finalize (GObject *object) +GdkPixbuf * +_gtk_file_system_volume_render_icon (GtkFileSystemVolume *volume, + GtkWidget *widget, + gint icon_size, + GError **error) { - GtkFileSystemModule *module = GTK_FILE_SYSTEM_MODULE (object); + GIcon *icon = NULL; + GdkPixbuf *pixbuf; + + DEBUG ("volume_get_icon_name"); + + if (IS_ROOT_VOLUME (volume)) + icon = g_themed_icon_new ("drive-harddisk"); + else if (G_IS_DRIVE (volume)) + icon = g_drive_get_icon (G_DRIVE (volume)); + else if (G_IS_VOLUME (volume)) + icon = g_volume_get_icon (G_VOLUME (volume)); + else if (G_IS_MOUNT (volume)) + icon = g_mount_get_icon (G_MOUNT (volume)); - g_free (module->path); + if (!icon) + return NULL; - G_OBJECT_CLASS (_gtk_file_system_module_parent_class)->finalize (object); + pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error); + + g_object_unref (icon); + + return pixbuf; } -static void -_gtk_file_system_module_class_init (GtkFileSystemModuleClass *class) +GtkFileSystemVolume * +_gtk_file_system_volume_ref (GtkFileSystemVolume *volume) { - GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class); - GObjectClass *gobject_class = G_OBJECT_CLASS (class); - - module_class->load = gtk_file_system_module_load; - module_class->unload = gtk_file_system_module_unload; + if (IS_ROOT_VOLUME (volume)) + return volume; + + if (G_IS_MOUNT (volume) || + G_IS_VOLUME (volume) || + G_IS_DRIVE (volume)) + g_object_ref (volume); - gobject_class->finalize = gtk_file_system_module_finalize; + return volume; } -static void -_gtk_file_system_module_init (GtkFileSystemModule *fs_module) +void +_gtk_file_system_volume_unref (GtkFileSystemVolume *volume) { -} + /* Root volume doesn't need to be freed */ + if (IS_ROOT_VOLUME (volume)) + return; + if (G_IS_MOUNT (volume) || + G_IS_VOLUME (volume) || + G_IS_DRIVE (volume)) + g_object_unref (volume); +} -static GtkFileSystem * -_gtk_file_system_module_create (GtkFileSystemModule *fs_module) +/* GFileInfo helper functions */ +GdkPixbuf * +_gtk_file_info_render_icon (GFileInfo *info, + GtkWidget *widget, + gint icon_size) { - GtkFileSystem *fs; - - if (g_type_module_use (G_TYPE_MODULE (fs_module))) - { - fs = fs_module->create (); - g_type_module_unuse (G_TYPE_MODULE (fs_module)); - return fs; - } - return NULL; -} + GIcon *icon; + GdkPixbuf *pixbuf = NULL; + const gchar *thumbnail_path; + thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH); -GtkFileSystem * -_gtk_file_system_create (const char *file_system_name) -{ - GSList *l; - char *module_path; - GtkFileSystemModule *fs_module; - GtkFileSystem *fs; + if (thumbnail_path) + pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path, + icon_size, icon_size, + NULL); - for (l = loaded_file_systems; l != NULL; l = l->next) + if (!pixbuf) { - fs_module = l->data; - - if (strcmp (G_TYPE_MODULE (fs_module)->name, file_system_name) == 0) - return _gtk_file_system_module_create (fs_module); - } + icon = g_file_info_get_icon (info); - fs = NULL; - if (g_module_supported ()) - { - module_path = _gtk_find_module (file_system_name, "filesystems"); + if (icon) + pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL); - if (module_path) + if (!pixbuf) { - fs_module = g_object_new (GTK_TYPE_FILE_SYSTEM_MODULE, NULL); - - g_type_module_set_name (G_TYPE_MODULE (fs_module), file_system_name); - fs_module->path = g_strdup (module_path); - - loaded_file_systems = g_slist_prepend (loaded_file_systems, - fs_module); - - fs = _gtk_file_system_module_create (fs_module); + /* Use general fallback for all files without icon */ + icon = g_themed_icon_new ("text-x-generic"); + pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL); + g_object_unref (icon); } - - g_free (module_path); } + + return pixbuf; +} + +gboolean +_gtk_file_info_consider_as_directory (GFileInfo *info) +{ + GFileType type = g_file_info_get_file_type (info); - return fs; + return (type == G_FILE_TYPE_DIRECTORY || + type == G_FILE_TYPE_MOUNTABLE || + type == G_FILE_TYPE_SHORTCUT); } -#define __GTK_FILE_SYSTEM_C__ -#include "gtkaliasdef.c" +gboolean +_gtk_file_has_native_path (GFile *file) +{ + char *local_file_path; + gboolean has_native_path; + + /* Don't use g_file_is_native(), as we want to support FUSE paths if available */ + local_file_path = g_file_get_path (file); + has_native_path = (local_file_path != NULL); + g_free (local_file_path); + + return has_native_path; +}