X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkfilesystem.c;h=c462b3f444f1820fc49a31290e9f9d4d68bfd86f;hb=3f53433008769dd4bbe738b45c8fb2638461af84;hp=7e10039871e798381b6caed0d7ecf2dcde0073d1;hpb=3827704de6ad642bce62b9f132e88145464d3645;p=~andy%2Fgtk diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c index 7e1003987..c462b3f44 100644 --- a/gtk/gtkfilesystem.c +++ b/gtk/gtkfilesystem.c @@ -1,1407 +1,1736 @@ /* 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 program 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, + * This program 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 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. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * 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 "gtkprivate.h" + #include "gtkalias.h" -#include "gtkstock.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; - gchar *icon_name; - guint is_folder : 1; - guint is_hidden : 1; +#define GTK_FILE_SYSTEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FILE_SYSTEM, GtkFileSystemPrivate)) +#define GTK_FOLDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FOLDER, GtkFolderPrivate)) +#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 +}; + +enum { + BOOKMARKS_CHANGED, + VOLUMES_CHANGED, + FS_LAST_SIGNAL }; -static void gtk_file_system_base_init (gpointer g_class); -static void gtk_file_folder_base_init (gpointer g_class); +enum { + FILES_ADDED, + FILES_REMOVED, + FILES_CHANGED, + FINISHED_LOADING, + DELETED, + FOLDER_LAST_SIGNAL +}; + +static guint fs_signals [FS_LAST_SIGNAL] = { 0, }; +static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, }; -GQuark -gtk_file_system_error_quark (void) +typedef struct GtkFileSystemPrivate GtkFileSystemPrivate; +typedef struct GtkFolderPrivate GtkFolderPrivate; +typedef struct AsyncFuncData AsyncFuncData; + +struct GtkFileSystemPrivate { - return g_quark_from_static_string ("gtk-file-system-error-quark"); -} + GVolumeMonitor *volume_monitor; + + /* This list contains elements that can be + * of type GDrive, GVolume and GMount + */ + GSList *volumes; + + /* This list contains GtkFileSystemBookmark structs */ + GSList *bookmarks; + + GFileMonitor *bookmarks_monitor; +}; -/***************************************** - * GtkFileInfo * - *****************************************/ -GType -gtk_file_info_get_type (void) +struct GtkFolderPrivate { - 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); + GFile *folder_file; + GHashTable *children; + GFileMonitor *directory_monitor; + GFileEnumerator *enumerator; + GCancellable *cancellable; + gchar *attributes; - return our_type; -} + guint finished_loading : 1; +}; -GtkFileInfo * -gtk_file_info_new (void) +struct AsyncFuncData { - GtkFileInfo *info; - - info = g_new0 (GtkFileInfo, 1); + GtkFileSystem *file_system; + GFile *file; + GtkFolder *folder; + GCancellable *cancellable; + gchar *attributes; - return info; -} + gpointer callback; + gpointer data; +}; -GtkFileInfo * -gtk_file_info_copy (GtkFileInfo *info) +struct GtkFileSystemBookmark { - GtkFileInfo *new_info; - - g_return_val_if_fail (info != NULL, NULL); - - 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->display_key) - new_info->display_key = g_strdup (new_info->display_key); - if (new_info->mime_type) - new_info->mime_type = g_strdup (new_info->mime_type); - if (new_info->icon_name) - new_info->icon_name = g_strdup (new_info->icon_name); - if (new_info->display_key) - new_info->display_key = g_strdup (new_info->display_key); - - return new_info; -} + GFile *file; + gchar *label; +}; +G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT) + +G_DEFINE_TYPE (GtkFolder, _gtk_folder, G_TYPE_OBJECT) + + +static void gtk_folder_set_finished_loading (GtkFolder *folder, + gboolean finished_loading); +static void gtk_folder_add_file (GtkFolder *folder, + GFile *file, + GFileInfo *info); + + +/* GtkFileSystemBookmark methods */ void -gtk_file_info_free (GtkFileInfo *info) +_gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark) { - g_return_if_fail (info != NULL); - - 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); - if (info->icon_name) - g_free (info->icon_name); - - g_free (info); + g_object_unref (bookmark->file); + g_free (bookmark->label); + g_slice_free (GtkFileSystemBookmark, bookmark); } -G_CONST_RETURN gchar * -gtk_file_info_get_display_name (const GtkFileInfo *info) +/* GtkFileSystem methods */ +static void +volumes_changed (GVolumeMonitor *volume_monitor, + gpointer volume, + gpointer user_data) { - g_return_val_if_fail (info != NULL, NULL); - - return info->display_name; + GtkFileSystem *file_system; + + gdk_threads_enter (); + + file_system = GTK_FILE_SYSTEM (user_data); + g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume); + gdk_threads_leave (); } -/** - * 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) +static void +gtk_file_system_dispose (GObject *object) { - g_return_val_if_fail (info != NULL, NULL); + GtkFileSystemPrivate *priv; + + DEBUG ("dispose"); + + priv = GTK_FILE_SYSTEM_GET_PRIVATE (object); - 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); + GtkFileSystemPrivate *priv; - if (display_name == info->display_name) - return; + DEBUG ("finalize"); - if (info->display_name) - g_free (info->display_name); - if (info->display_key) + priv = GTK_FILE_SYSTEM_GET_PRIVATE (object); + + if (priv->bookmarks_monitor) + g_object_unref (priv->bookmarks_monitor); + + 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) +static void +_gtk_file_system_class_init (GtkFileSystemClass *class) { - g_return_val_if_fail (info != NULL, FALSE); - - return info->is_folder; -} + GObjectClass *object_class = G_OBJECT_CLASS (class); -void -gtk_file_info_set_is_folder (GtkFileInfo *info, - gboolean is_folder) -{ - g_return_if_fail (info != NULL); + object_class->dispose = gtk_file_system_dispose; + object_class->finalize = gtk_file_system_finalize; - info->is_folder = is_folder != FALSE; -} + 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); -gboolean -gtk_file_info_get_is_hidden (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, FALSE); - - return info->is_hidden; + 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_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_home_dir (), ".gtk-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); - return info->modification_time; -} + for (i = 0; lines[i]; i++) + { + GtkFileSystemBookmark *bookmark; -void -gtk_file_info_set_modification_time (GtkFileInfo *info, - GtkFileTime modification_time) -{ - g_return_if_fail (info != NULL); - - info->modification_time = modification_time; -} + if (!*lines[i]) + continue; -gint64 -gtk_file_info_get_size (const GtkFileInfo *info) -{ - g_return_val_if_fail (info != NULL, 0); - - return info->size; -} + bookmark = g_slice_new0 (GtkFileSystemBookmark); -void -gtk_file_info_set_size (GtkFileInfo *info, - gint64 size) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (size >= 0); - - info->size = size; + 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); + } + + bookmarks = g_slist_reverse (bookmarks); + g_strfreev (lines); + g_free (contents); + + return bookmarks; } -void -gtk_file_info_set_icon_name (GtkFileInfo *info, - const gchar *icon_name) +static void +save_bookmarks (GFile *bookmarks_file, + GSList *bookmarks) { - g_return_if_fail (info != NULL); - - if (info->icon_name) - g_free (info->icon_name); + GError *error = NULL; + GString *contents; + + contents = g_string_new (""); + + while (bookmarks) + { + GtkFileSystemBookmark *bookmark; + gchar *uri; + + bookmark = bookmarks->data; + uri = g_file_get_uri (bookmark->file); + g_string_append (contents, uri); + + if (bookmark->label) + g_string_append_printf (contents, " %s", bookmark->label); + + g_string_append_c (contents, '\n'); + bookmarks = bookmarks->next; + g_free (uri); + } - info->icon_name = g_strdup (icon_name); + if (!g_file_replace_contents (bookmarks_file, + contents->str, + strlen (contents->str), + NULL, FALSE, 0, NULL, + NULL, &error)) + { + g_critical (error->message); + g_error_free (error); + } + + g_string_free (contents, TRUE); } -G_CONST_RETURN gchar * -gtk_file_info_get_icon_name (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, NULL); - - return info->icon_name; + GtkFileSystemPrivate *priv; + + priv = GTK_FILE_SYSTEM_GET_PRIVATE (data); + + 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; + } } -GdkPixbuf * -gtk_file_info_render_icon (const GtkFileInfo *info, - GtkWidget *widget, - gint pixel_size, - GError **error) +static void +get_volumes_list (GtkFileSystem *file_system) { - GdkPixbuf *pixbuf = NULL; + GtkFileSystemPrivate *priv; + GList *l, *ll; + GList *drives; + GList *volumes; + GList *mounts; + GDrive *drive; + GVolume *volume; + GMount *mount; - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); - if (info->icon_name) + if (priv->volumes) { - if (g_path_is_absolute (info->icon_name)) - pixbuf = gdk_pixbuf_new_from_file_at_size (info->icon_name, - pixel_size, - pixel_size, - NULL); - else - { - GtkIconTheme *icon_theme; + 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); - icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)); - if (gtk_icon_theme_has_icon (icon_theme, info->icon_name)) - pixbuf = gtk_icon_theme_load_icon (icon_theme, info->icon_name, - pixel_size, 0, NULL); + 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); + } + + 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)); } } - if (!pixbuf) + 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) { - /* load a fallback icon */ - pixbuf = gtk_widget_render_icon (widget, - gtk_file_info_get_is_folder (info) - ? GTK_STOCK_DIRECTORY : GTK_STOCK_FILE, - GTK_ICON_SIZE_SMALL_TOOLBAR, - NULL); - if (!pixbuf && error) - g_set_error (error, - GTK_FILE_SYSTEM_ERROR, - GTK_FILE_SYSTEM_ERROR_FAILED, - _("Could not get a stock icon for %s\n"), - info->icon_name); + 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)); + } } - return pixbuf; -} + g_list_free (volumes); -/***************************************** - * GtkFileSystemHandle * - *****************************************/ + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (priv->volume_monitor); -enum -{ - PROP_0, - PROP_CANCELLED -}; + for (l = mounts; l != NULL; l = l->next) + { + mount = l->data; + volume = g_mount_get_volume (mount); -G_DEFINE_TYPE (GtkFileSystemHandle, gtk_file_system_handle, G_TYPE_OBJECT) + if (volume) + { + g_object_unref (volume); + continue; + } -static void -gtk_file_system_handle_init (GtkFileSystemHandle *handle) -{ - handle->file_system = NULL; - handle->cancelled = FALSE; + /* show this mount */ + priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount)); + } + + g_list_free (mounts); } static void -gtk_file_system_handle_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +_gtk_file_system_init (GtkFileSystem *file_system) +{ + GtkFileSystemPrivate *priv; + GFile *bookmarks_file; + GError *error = NULL; + + DEBUG ("init"); + + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + + /* 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); + priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file, + G_FILE_MONITOR_NONE, + NULL, &error); + if (error) + g_warning (error->message); + else + g_signal_connect (priv->bookmarks_monitor, "changed", + G_CALLBACK (bookmarks_file_changed), file_system); + + g_object_unref (bookmarks_file); +} + +/* GtkFileSystem public methods */ +GtkFileSystem * +_gtk_file_system_new (void) { - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL); } -static void -gtk_file_system_handle_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +GSList * +_gtk_file_system_list_volumes (GtkFileSystem *file_system) { - GtkFileSystemHandle *handle = GTK_FILE_SYSTEM_HANDLE (object); + GtkFileSystemPrivate *priv; + GSList *list; - switch (prop_id) - { - case PROP_CANCELLED: - g_value_set_boolean (value, handle->cancelled); - break; + DEBUG ("list_volumes"); - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} + g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); -static void -gtk_file_system_handle_class_init (GtkFileSystemHandleClass *klass) -{ - GObjectClass *o_class; - - o_class = (GObjectClass *)klass; - o_class->set_property = gtk_file_system_handle_set_property; - o_class->get_property = gtk_file_system_handle_get_property; - - g_object_class_install_property (o_class, - PROP_CANCELLED, - g_param_spec_boolean ("cancelled", - P_("Cancelled"), - P_("Whether or not the operation has been successfully cancelled"), - FALSE, - G_PARAM_READABLE)); + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + get_volumes_list (GTK_FILE_SYSTEM (file_system)); + + list = g_slist_copy (priv->volumes); + +#ifndef G_OS_WIN32 + /* Prepend root volume */ + list = g_slist_prepend (list, (gpointer) root_volume_token); +#endif + + return list; } -/***************************************** - * GtkFileSystem * - *****************************************/ -GType -gtk_file_system_get_type (void) +GSList * +_gtk_file_system_list_bookmarks (GtkFileSystem *file_system) { - static GType file_system_type = 0; + GtkFileSystemPrivate *priv; + GSList *bookmarks, *files = NULL; + + DEBUG ("list_bookmarks"); - if (!file_system_type) + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + bookmarks = priv->bookmarks; + + while (bookmarks) { - 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); + GtkFileSystemBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + files = g_slist_prepend (files, g_object_ref (bookmark->file)); } - return file_system_type; + return g_slist_reverse (files); } -static void -gtk_file_system_base_init (gpointer g_class) +gboolean +_gtk_file_system_parse (GtkFileSystem *file_system, + GFile *base_file, + const gchar *str, + GFile **folder, + gchar **file_part, + GError **error) { - static gboolean initialized = FALSE; - - if (!initialized) + GFile *file; + gboolean result = FALSE; + gboolean is_dir = FALSE; + gchar *last_slash = NULL; + + DEBUG ("parse"); + + if (str && *str) + is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR); + + last_slash = strrchr (str, G_DIR_SEPARATOR); + + if (str[0] == '~') + file = g_file_parse_name (str); + else + file = g_file_resolve_relative_path (base_file, str); + + if (g_file_equal (base_file, file)) { - 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; + /* this is when user types '.', could be the + * beginning of a hidden file, ./ or ../ + */ + *folder = g_object_ref (file); + *file_part = g_strdup (str); + result = TRUE; } -} + else if (is_dir) + { + /* it's a dir, or at least it ends with the dir separator */ + *folder = g_object_ref (file); + *file_part = g_strdup (""); + result = TRUE; + } + else + { + GFile *parent_file; -GSList * -gtk_file_system_list_volumes (GtkFileSystem *file_system) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); + parent_file = g_file_get_parent (file); + + if (!parent_file) + { + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + "Could not get parent file"); + *folder = NULL; + *file_part = NULL; + } + else + { + *folder = parent_file; + result = TRUE; + + if (last_slash) + *file_part = g_strdup (last_slash + 1); + else + *file_part = g_strdup (str); + } + } - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_volumes (file_system); + g_object_unref (file); + + return result; } -GtkFileSystemHandle * -gtk_file_system_get_folder (GtkFileSystem *file_system, - const GtkFilePath *path, - GtkFileInfoType types, - GtkFileSystemGetFolderCallback callback, - gpointer data) +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 (path != NULL, NULL); - g_return_val_if_fail (callback != 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)->get_folder (file_system, path, types, callback, data); + if (async_data->folder) + g_object_unref (async_data->folder); + + g_free (async_data->attributes); + g_free (async_data); } -GtkFileSystemHandle * -gtk_file_system_get_info (GtkFileSystem *file_system, - const GtkFilePath *path, - GtkFileInfoType types, - GtkFileSystemGetInfoCallback callback, - gpointer data) +static void +enumerate_children_callback (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 (callback != NULL, NULL); + GFileEnumerator *enumerator; + AsyncFuncData *async_data; + GtkFolder *folder = NULL; + GFile *file; + GError *error = NULL; + + file = G_FILE (source_object); + async_data = (AsyncFuncData *) user_data; + enumerator = g_file_enumerate_children_finish (file, result, &error); + + if (enumerator) + { + folder = g_object_new (GTK_TYPE_FOLDER, + "file", source_object, + "enumerator", enumerator, + "attributes", async_data->attributes, + NULL); + g_object_unref (enumerator); + } + + gdk_threads_enter (); + ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable, + folder, error, async_data->data); + gdk_threads_leave (); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_info (file_system, path, types, callback, data); + free_async_data (async_data); + + if (error) + g_error_free (error); } -GtkFileSystemHandle * -gtk_file_system_create_folder (GtkFileSystem *file_system, - const GtkFilePath *path, - GtkFileSystemCreateFolderCallback callback, - gpointer data) +GCancellable * +_gtk_file_system_get_folder (GtkFileSystem *file_system, + GFile *file, + const gchar *attributes, + GtkFileSystemGetFolderCallback 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 (path != NULL, NULL); - g_return_val_if_fail (callback != NULL, NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, callback, data); + cancellable = g_cancellable_new (); + + 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); + async_data->attributes = g_strdup (attributes); + + async_data->callback = callback; + async_data->data = data; + + g_file_enumerate_children_async (file, + attributes, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + cancellable, + enumerate_children_callback, + async_data); + return cancellable; } -void -gtk_file_system_cancel_operation (GtkFileSystemHandle *handle) +static void +query_info_callback (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - g_return_if_fail (GTK_IS_FILE_SYSTEM_HANDLE (handle)); + AsyncFuncData *async_data; + GError *error = NULL; + GFileInfo *file_info; + GFile *file; + + DEBUG ("query_info_callback"); + + 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 (); + } + + if (file_info) + g_object_unref (file_info); + + if (error) + g_error_free (error); - GTK_FILE_SYSTEM_GET_IFACE (handle->file_system)->cancel_operation (handle); + free_async_data (async_data); } -/** - * 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) +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 (path != NULL, NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + + cancellable = g_cancellable_new (); + + 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); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_volume_for_path (file_system, path); + 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 cancellable; } -/** - * 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) +static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system)); - g_return_if_fail (volume != NULL); + AsyncFuncData *async_data; + GError *error = NULL; - GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_free (file_system, volume); + 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_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) +static void +volume_mount_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 (volume != NULL, NULL); + AsyncFuncData *async_data; + GError *error = NULL; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_base_path (file_system, volume); + 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 (); + + if (error) + g_error_free (error); } -/** - * 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) +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 (volume != NULL, FALSE); + GCancellable *cancellable; + AsyncFuncData *async_data; + gboolean handled = FALSE; + + DEBUG ("volume_mount"); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_is_mounted (file_system, volume); + cancellable = g_cancellable_new (); + + 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_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. - **/ -/* FIXME XXX: update documentation above */ -GtkFileSystemHandle * -gtk_file_system_volume_mount (GtkFileSystem *file_system, - GtkFileSystemVolume *volume, - GtkFileSystemVolumeMountCallback callback, - gpointer data) +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); - g_return_val_if_fail (volume != NULL, NULL); - g_return_val_if_fail (callback != NULL, NULL); + GtkFileSystemVolume *volume; + AsyncFuncData *async_data; + GError *error = NULL; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_mount (file_system, volume, callback, data); + 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)); + + 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_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) +GCancellable * +_gtk_file_system_mount_enclosing_volume (GtkFileSystem *file_system, + GFile *file, + GMountOperation *mount_operation, + GtkFileSystemVolumeMountCallback 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 (volume != NULL, NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_display_name (file_system, volume); + DEBUG ("mount_enclosing_volume"); + + cancellable = g_cancellable_new (); + + 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); + + async_data->callback = callback; + async_data->data = data; + + g_file_mount_enclosing_volume (file, + G_MOUNT_MOUNT_NONE, + mount_operation, + cancellable, + enclosing_volume_mount_cb, + async_data); + return cancellable; } -/** - * 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) +gboolean +_gtk_file_system_insert_bookmark (GtkFileSystem *file_system, + GFile *file, + gint position, + GError **error) { - gchar *icon_name; - GdkPixbuf *pixbuf = NULL; + GtkFileSystemPrivate *priv; + GSList *bookmarks; + GtkFileSystemBookmark *bookmark; + gboolean result = TRUE; + GFile *bookmarks_file; - 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); - - icon_name = gtk_file_system_volume_get_icon_name (file_system, volume, - error); - if (icon_name) + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + bookmarks = priv->bookmarks; + + while (bookmarks) { - GtkIconTheme *icon_theme; + bookmark = bookmarks->data; + bookmarks = bookmarks->next; - icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)); - if (gtk_icon_theme_has_icon (icon_theme, icon_name)) - pixbuf = gtk_icon_theme_load_icon (icon_theme, - icon_name, pixel_size, 0, NULL); - g_free (icon_name); + if (g_file_equal (bookmark->file, file)) + { + /* File is already in bookmarks */ + result = FALSE; + break; + } } - if (!pixbuf) - pixbuf = gtk_widget_render_icon (widget, - GTK_STOCK_HARDDISK, - GTK_ICON_SIZE_SMALL_TOOLBAR, - NULL); + if (!result) + { + gchar *uri = g_file_get_uri (file); - return pixbuf; -} + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, + "%s already exists in the bookmarks list", + uri); -/** - * gtk_file_system_volume_get_icon_name: - * @file_system: a #GtkFileSystem - * @volume: a #GtkFileSystemVolume - * @error: location to store error, or %NULL - * - * Gets an icon name suitable for a #GtkFileSystemVolume. - * - * Return value: An icon name which can be used for rendering an icon for - * this volume, or %NULL if no icon name could be found. In the latter - * case, the @error value will be set as appropriate. - **/ -gchar * -gtk_file_system_volume_get_icon_name (GtkFileSystem *file_system, - GtkFileSystemVolume *volume, - GError **error) -{ - 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 (error == NULL || *error == NULL, NULL); + 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); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_icon_name (file_system, - volume, - error); + g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0); + + return TRUE; } -/** - * 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) +_gtk_file_system_remove_bookmark (GtkFileSystem *file_system, + GFile *file, + 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); + GtkFileSystemPrivate *priv; + GtkFileSystemBookmark *bookmark; + GSList *bookmarks; + gboolean result = FALSE; + GFile *bookmarks_file; - *parent = NULL; + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); - result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, path, parent, error); - g_assert (result || *parent == NULL); + if (!priv->bookmarks) + return FALSE; - return result; -} + bookmarks = priv->bookmarks; -GtkFilePath * -gtk_file_system_make_path (GtkFileSystem *file_system, - const GtkFilePath *base_path, - const gchar *display_name, - GError **error) -{ - 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); + while (bookmarks) + { + bookmark = bookmarks->data; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_path (file_system, base_path, display_name, error); -} + 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; + } -/** - * 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) -{ - 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); + bookmarks = bookmarks->next; + } - if (file_part) - *file_part = tmp_file_part; - else - g_free (tmp_file_part); + if (!result) + { + gchar *uri = g_file_get_uri (file); - return result; -} + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + "%s does not exist in the bookmarks list", + uri); + g_free (uri); -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); + return FALSE; + } + + 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; } gchar * -gtk_file_system_path_to_filename (GtkFileSystem *file_system, - const GtkFilePath *path) +_gtk_file_system_get_bookmark_label (GtkFileSystem *file_system, + GFile *file) { - 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); -} + GtkFileSystemPrivate *priv; + GSList *bookmarks; + gchar *label = NULL; -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); -} + DEBUG ("get_bookmark_label"); -GtkFilePath * -gtk_file_system_filename_to_path (GtkFileSystem *file_system, - const gchar *filename) -{ - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (filename != NULL, NULL); + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + bookmarks = priv->bookmarks; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->filename_to_path (file_system, filename); -} + while (bookmarks) + { + GtkFileSystemBookmark *bookmark; -/** - * 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); + bookmark = bookmarks->data; + bookmarks = bookmarks->next; - filename = gtk_file_system_path_to_filename (file_system, path); - result = filename != NULL; - g_free (filename); + if (g_file_equal (file, bookmark->file)) + { + label = g_strdup (bookmark->label); + break; + } + } - return result; + return label; } -/** - * 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) +void +_gtk_file_system_set_bookmark_label (GtkFileSystem *file_system, + GFile *file, + const gchar *label) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); + GtkFileSystemPrivate *priv; + gboolean changed = FALSE; + GFile *bookmarks_file; + GSList *bookmarks; + + DEBUG ("set_bookmark_label"); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->insert_bookmark (file_system, path, position, error); + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + bookmarks = priv->bookmarks; + + while (bookmarks) + { + GtkFileSystemBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + if (g_file_equal (file, bookmark->file)) + { + g_free (bookmark->label); + bookmark->label = g_strdup (label); + changed = TRUE; + break; + } + } + + 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); } -/** - * 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) +GtkFileSystemVolume * +_gtk_file_system_get_volume_for_file (GtkFileSystem *file_system, + GFile *file) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE); - g_return_val_if_fail (path != NULL, FALSE); + GtkFileSystemPrivate *priv; + GMount *mount; + + DEBUG ("get_volume_for_file"); - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->remove_bookmark (file_system, path, error); + priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system); + mount = g_file_find_enclosing_mount (file, NULL, NULL); + + if (!mount && g_file_is_native (file)) + return (GtkFileSystemVolume *) root_volume_token; + + return (GtkFileSystemVolume *) mount; } -/** - * 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) +/* GtkFolder methods */ +static void +gtk_folder_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); + GtkFolderPrivate *priv; - return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_bookmarks (file_system); + priv = GTK_FOLDER_GET_PRIVATE (object); + + switch (prop_id) + { + case PROP_FILE: + priv->folder_file = g_value_dup_object (value); + break; + case PROP_ENUMERATOR: + priv->enumerator = g_value_dup_object (value); + break; + case PROP_ATTRIBUTES: + priv->attributes = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } -/** - * 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) +static void +gtk_folder_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - GtkFileSystemIface *iface; + GtkFolderPrivate *priv; - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (path != NULL, NULL); - - iface = GTK_FILE_SYSTEM_GET_IFACE (file_system); - if (iface->get_bookmark_label) - return iface->get_bookmark_label (file_system, path); + priv = GTK_FOLDER_GET_PRIVATE (object); - return NULL; + switch (prop_id) + { + case PROP_FILE: + g_value_set_object (value, priv->folder_file); + break; + case PROP_ENUMERATOR: + g_value_set_object (value, priv->enumerator); + break; + case PROP_ATTRIBUTES: + g_value_set_string (value, priv->attributes); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } -/** - * 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) +static void +query_created_file_info_callback (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - GtkFileSystemIface *iface; + GFile *file = G_FILE (source_object); + GError *error = NULL; + GFileInfo *info; + GtkFolder *folder; + GSList *files; - g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system)); - g_return_if_fail (path != NULL); + info = g_file_query_info_finish (file, result, &error); - iface = GTK_FILE_SYSTEM_GET_IFACE (file_system); - if (iface->set_bookmark_label) - iface->set_bookmark_label (file_system, path, label); + if (error) + { + g_error_free (error); + return; + } + + folder = GTK_FOLDER (user_data); + gtk_folder_add_file (folder, file, info); + + files = g_slist_prepend (NULL, file); + g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files); + g_slist_free (files); + + g_object_unref (info); } -/***************************************** - * GtkFileFolder * - *****************************************/ -GType -gtk_file_folder_get_type (void) +static void +directory_monitor_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer data) { - static GType file_folder_type = 0; + GtkFolderPrivate *priv; + GtkFolder *folder; + GSList *files; + + folder = GTK_FOLDER (data); + priv = GTK_FOLDER_GET_PRIVATE (folder); + files = g_slist_prepend (NULL, file); + + gdk_threads_enter (); - if (!file_folder_type) + switch (event) { - 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); + case G_FILE_MONITOR_EVENT_CREATED: + g_file_query_info_async (file, + priv->attributes, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + priv->cancellable, + query_created_file_info_callback, + folder); + break; + case G_FILE_MONITOR_EVENT_DELETED: + if (g_file_equal (file, priv->folder_file)) + g_signal_emit (folder, folder_signals[DELETED], 0); + else + g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files); + break; + default: + break; } - return file_folder_type; + gdk_threads_leave (); + + g_slist_free (files); } static void -gtk_file_folder_base_init (gpointer g_class) +enumerator_files_callback (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - static gboolean initialized = FALSE; - - if (!initialized) + GFileEnumerator *enumerator; + GtkFolderPrivate *priv; + GtkFolder *folder; + GError *error = NULL; + GSList *files = NULL; + GList *file_infos, *f; + + enumerator = G_FILE_ENUMERATOR (source_object); + file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error); + + if (error) + { + g_warning (error->message); + g_error_free (error); + return; + } + + folder = GTK_FOLDER (user_data); + priv = GTK_FOLDER_GET_PRIVATE (folder); + + if (!file_infos) + { + g_file_enumerator_close_async (enumerator, + G_PRIORITY_DEFAULT, + NULL, NULL, NULL); + + gtk_folder_set_finished_loading (folder, TRUE); + return; + } + + g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY, + G_PRIORITY_DEFAULT, + priv->cancellable, + enumerator_files_callback, + folder); + + for (f = file_infos; f; f = f->next) { - 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; + GFileInfo *info; + GFile *child_file; + + info = f->data; + child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info)); + gtk_folder_add_file (folder, child_file, info); + files = g_slist_prepend (files, child_file); } + + gdk_threads_enter (); + g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files); + gdk_threads_leave (); + + g_list_foreach (file_infos, (GFunc) g_object_unref, NULL); + g_list_free (file_infos); + + g_slist_foreach (files, (GFunc) g_object_unref, NULL); + g_slist_free (files); } -gboolean -gtk_file_folder_list_children (GtkFileFolder *folder, - GSList **children, - GError **error) +static void +gtk_folder_constructed (GObject *object) { - 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); + GtkFolderPrivate *priv; + GError *error = NULL; - result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error); - g_assert (result || tmp_children == NULL); + priv = GTK_FOLDER_GET_PRIVATE (object); + priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error); - if (children) - *children = tmp_children; + if (error) + g_warning (error->message); else - gtk_file_paths_free (tmp_children); + g_signal_connect (priv->directory_monitor, "changed", + G_CALLBACK (directory_monitor_changed), object); - return result; + g_file_enumerator_next_files_async (priv->enumerator, + FILES_PER_QUERY, + G_PRIORITY_DEFAULT, + priv->cancellable, + enumerator_files_callback, + object); + /* This isn't needed anymore */ + g_object_unref (priv->enumerator); + priv->enumerator = NULL; } -GtkFileInfo * -gtk_file_folder_get_info (GtkFileFolder *folder, - const GtkFilePath *path, - GError **error) +static void +gtk_folder_finalize (GObject *object) { - g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + GtkFolderPrivate *priv; - return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, path, error); -} + priv = GTK_FOLDER_GET_PRIVATE (object); -gboolean -gtk_file_folder_is_finished_loading (GtkFileFolder *folder) -{ - GtkFileFolderIface *iface; + g_hash_table_unref (priv->children); - g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), TRUE); + if (priv->folder_file) + g_object_unref (priv->folder_file); - iface = GTK_FILE_FOLDER_GET_IFACE (folder); - if (!iface->is_finished_loading) - return TRUE; - else - return iface->is_finished_loading (folder); -} + if (priv->directory_monitor) + g_object_unref (priv->directory_monitor); + g_cancellable_cancel (priv->cancellable); + g_object_unref (priv->cancellable); + g_free (priv->attributes); -/***************************************** - * GtkFilePath modules * - *****************************************/ + G_OBJECT_CLASS (_gtk_folder_parent_class)->finalize (object); +} -/* 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); +static void +_gtk_folder_class_init (GtkFolderClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->set_property = gtk_folder_set_property; + object_class->get_property = gtk_folder_get_property; + object_class->constructed = gtk_folder_constructed; + object_class->finalize = gtk_folder_finalize; + + g_object_class_install_property (object_class, + PROP_FILE, + g_param_spec_object ("file", + "File", + "GFile for the folder", + G_TYPE_FILE, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_ENUMERATOR, + g_param_spec_object ("enumerator", + "Enumerator", + "GFileEnumerator to list files", + G_TYPE_FILE_ENUMERATOR, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_ATTRIBUTES, + g_param_spec_string ("attributes", + "Attributes", + "Attributes to query for", + NULL, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + folder_signals[FILES_ADDED] = + g_signal_new ("files-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFolderClass, files_added), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + folder_signals[FILES_REMOVED] = + g_signal_new ("files-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFolderClass, files_removed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + folder_signals[FILES_CHANGED] = + g_signal_new ("files-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFolderClass, files_changed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + folder_signals[FINISHED_LOADING] = + g_signal_new ("finished-loading", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFolderClass, finished_loading), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + folder_signals[DELETED] = + g_signal_new ("deleted", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFolderClass, deleted), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (object_class, sizeof (GtkFolderPrivate)); } static void -gtk_file_path_real_free (gpointer boxed) +_gtk_folder_init (GtkFolder *folder) { - gtk_file_path_free (boxed); + GtkFolderPrivate *priv; + + priv = GTK_FOLDER_GET_PRIVATE (folder); + + priv->children = g_hash_table_new_full (g_file_hash, + (GEqualFunc) g_file_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) g_object_unref); + priv->cancellable = g_cancellable_new (); } -GType -gtk_file_path_get_type (void) +static void +gtk_folder_set_finished_loading (GtkFolder *folder, + gboolean finished_loading) { - 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); + GtkFolderPrivate *priv; - return our_type; -} + priv = GTK_FOLDER_GET_PRIVATE (folder); + priv->finished_loading = (finished_loading == TRUE); + gdk_threads_enter (); + g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0); + gdk_threads_leave (); +} -GSList * -gtk_file_paths_sort (GSList *paths) +static void +gtk_folder_add_file (GtkFolder *folder, + GFile *file, + GFileInfo *info) { -#ifndef G_OS_WIN32 - return g_slist_sort (paths, (GCompareFunc)strcmp); -#else - return g_slist_sort (paths, (GCompareFunc)_gtk_file_system_win32_path_compare); -#endif + GtkFolderPrivate *priv; + + priv = GTK_FOLDER_GET_PRIVATE (folder); + + g_hash_table_insert (priv->children, + g_object_ref (file), + g_object_ref (info)); } -/** - * 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) +_gtk_folder_list_children (GtkFolder *folder) { - GSList *head, *tail, *l; + GtkFolderPrivate *priv; + GList *files, *elem; + GSList *children = NULL; - head = tail = NULL; + priv = GTK_FOLDER_GET_PRIVATE (folder); + files = g_hash_table_get_keys (priv->children); + children = NULL; - for (l = paths; l; l = l->next) - { - GtkFilePath *path; - GSList *node; + for (elem = files; elem; elem = elem->next) + children = g_slist_prepend (children, g_object_ref (elem->data)); - path = l->data; - node = g_slist_alloc (); + g_list_free (files); - if (tail) - tail->next = node; - else - head = node; - - node->data = gtk_file_path_copy (path); - tail = node; - } - - return head; + return children; } -void -gtk_file_paths_free (GSList *paths) +GFileInfo * +_gtk_folder_get_info (GtkFolder *folder, + GFile *file) { - GSList *tmp_list; + GtkFolderPrivate *priv; + GFileInfo *info; - for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next) - gtk_file_path_free (tmp_list->data); + priv = GTK_FOLDER_GET_PRIVATE (folder); + info = g_hash_table_lookup (priv->children, file); - g_slist_free (paths); + if (!info) + return NULL; + + return g_object_ref (info); } -/***************************************** - * GtkFileSystem modules * - *****************************************/ +gboolean +_gtk_folder_is_finished_loading (GtkFolder *folder) +{ + GtkFolderPrivate *priv; -typedef struct _GtkFileSystemModule GtkFileSystemModule; -typedef struct _GtkFileSystemModuleClass GtkFileSystemModuleClass; + priv = GTK_FOLDER_GET_PRIVATE (folder); -struct _GtkFileSystemModule + return priv->finished_loading; +} + +/* 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) - { - g_warning (g_module_error()); - return FALSE; - } - - /* 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)) + if (G_IS_MOUNT (volume)) + mounted = TRUE; + 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) + { + mounted = TRUE; + 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 mounted; } -static void -gtk_file_system_module_unload (GTypeModule *module) +GFile * +_gtk_file_system_volume_get_root (GtkFileSystemVolume *volume) { - GtkFileSystemModule *fs_module = GTK_FILE_SYSTEM_MODULE (module); - - fs_module->exit(); + GFile *file = NULL; - g_module_close (fs_module->library); - fs_module->library = NULL; + DEBUG ("volume_get_base"); - fs_module->init = NULL; - fs_module->exit = NULL; - fs_module->create = NULL; -} + if (IS_ROOT_VOLUME (volume)) + return g_file_new_for_uri ("file:///"); -/* This only will ever be called if an error occurs during - * initialization - */ -static void -gtk_file_system_module_finalize (GObject *object) -{ - GtkFileSystemModule *module = GTK_FILE_SYSTEM_MODULE (object); + if (G_IS_MOUNT (volume)) + file = g_mount_get_root (G_MOUNT (volume)); + else if (G_IS_VOLUME (volume)) + { + GMount *mount; - g_free (module->path); + mount = g_volume_get_mount (G_VOLUME (volume)); + + if (mount) + { + file = g_mount_get_root (mount); + g_object_unref (mount); + } + } - G_OBJECT_CLASS (_gtk_file_system_module_parent_class)->finalize (object); + return file; } -static void -_gtk_file_system_module_class_init (GtkFileSystemModuleClass *class) +static GdkPixbuf * +get_pixbuf_from_gicon (GIcon *icon, + GtkWidget *widget, + gint icon_size, + GError **error) { - 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; + 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); - gobject_class->finalize = gtk_file_system_module_finalize; + if (!icon_info) + return NULL; + + pixbuf = gtk_icon_info_load_icon (icon_info, error); + gtk_icon_info_free (icon_info); + + return pixbuf; } -static void -_gtk_file_system_module_init (GtkFileSystemModule *fs_module) +GdkPixbuf * +_gtk_file_system_volume_render_icon (GtkFileSystemVolume *volume, + GtkWidget *widget, + gint icon_size, + GError **error) { -} + GIcon *icon = NULL; + GdkPixbuf *pixbuf; + DEBUG ("volume_get_icon_name"); -static GtkFileSystem * -_gtk_file_system_module_create (GtkFileSystemModule *fs_module) -{ - 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; -} + 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)); + if (!icon) + return NULL; -GtkFileSystem * -_gtk_file_system_create (const char *file_system_name) + pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error); + + g_object_unref (icon); + + return pixbuf; +} + +void +_gtk_file_system_volume_free (GtkFileSystemVolume *volume) { - GSList *l; - char *module_path; - GtkFileSystemModule *fs_module; - GtkFileSystem *fs; + /* Root volume doesn't need to be freed */ + if (IS_ROOT_VOLUME (volume)) + return; - for (l = loaded_file_systems; l != NULL; l = l->next) - { - fs_module = l->data; - - if (strcmp (G_TYPE_MODULE (fs_module)->name, file_system_name) == 0) - return _gtk_file_system_module_create (fs_module); - } + if (G_IS_MOUNT (volume) || + G_IS_VOLUME (volume) || + G_IS_DRIVE (volume)) + g_object_unref (volume); +} - fs = NULL; - if (g_module_supported ()) - { - module_path = _gtk_find_module (file_system_name, "filesystems"); +/* GFileInfo helper functions */ +GdkPixbuf * +_gtk_file_info_render_icon (GFileInfo *info, + GtkWidget *widget, + gint icon_size) +{ + GIcon *icon; + GdkPixbuf *pixbuf = NULL; + const gchar *thumbnail_path; - if (module_path) - { - fs_module = g_object_new (GTK_TYPE_FILE_SYSTEM_MODULE, NULL); + thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH); - g_type_module_set_name (G_TYPE_MODULE (fs_module), file_system_name); - fs_module->path = g_strdup (module_path); + if (thumbnail_path) + pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path, + icon_size, icon_size, + NULL); - loaded_file_systems = g_slist_prepend (loaded_file_systems, - fs_module); + if (!pixbuf) + { + icon = g_file_info_get_icon (info); - fs = _gtk_file_system_module_create (fs_module); - } - - g_free (module_path); + if (icon) + pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL); } - - return fs; -} -#define __GTK_FILE_SYSTEM_C__ -#include "gtkaliasdef.c" + return pixbuf; +}