/* GTK - The GIMP Toolkit
 * 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 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see .
 *
 * Authors: Carlos Garnacho 
 */
#include "config.h"
#include "gtkfilesystem.h"
#include 
#include 
#include "gtkfilechooser.h"
#include "gtkcssiconthemevalueprivate.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtkstylecontextprivate.h"
/* #define DEBUG_MODE */
#ifdef DEBUG_MODE
#define DEBUG(x) g_debug (x);
#else
#define DEBUG(x)
#endif
#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 {
  VOLUMES_CHANGED,
  FS_LAST_SIGNAL
};
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;
struct GtkFileSystemPrivate
{
  GVolumeMonitor *volume_monitor;
  /* This list contains elements that can be
   * of type GDrive, GVolume and GMount
   */
  GSList *volumes;
};
struct AsyncFuncData
{
  GtkFileSystem *file_system;
  GFile *file;
  GCancellable *cancellable;
  gpointer callback;
  gpointer data;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
/* GtkFileSystem methods */
static void
volumes_changed (GVolumeMonitor *volume_monitor,
		 gpointer        volume,
		 gpointer        user_data)
{
  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 ();
}
static void
gtk_file_system_dispose (GObject *object)
{
  GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
  GtkFileSystemPrivate *priv = file_system->priv;
  DEBUG ("dispose");
  if (priv->volumes)
    {
      g_slist_free_full (priv->volumes, g_object_unref);
      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;
    }
  G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
}
static void
_gtk_file_system_class_init (GtkFileSystemClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);
  object_class->dispose = gtk_file_system_dispose;
  fs_signals[VOLUMES_CHANGED] =
    g_signal_new (I_("volumes-changed"),
		  G_TYPE_FROM_CLASS (object_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
		  NULL, NULL,
		  NULL,
		  G_TYPE_NONE, 0);
}
static gboolean
mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
{
  GList *l;
  GFile *mount_root;
  gboolean ret;
  ret = FALSE;
  mount_root = g_mount_get_root (mount);
  for (l = volumes; l != NULL; l = l->next)
    {
      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);
        }
    }
  g_object_unref (mount_root);
  return ret;
}
static void
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_free_full (priv->volumes, g_object_unref);
      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);
            }
  
           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)
    {
      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);
    }
  /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
  mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
  for (l = mounts; l != NULL; l = l->next)
    {
      mount = l->data;
      volume = g_mount_get_volume (mount);
      if (volume)
        {
          g_object_unref (volume);
          continue;
        }
      /* 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);
    }
  g_list_free (volumes);
  g_list_free (mounts);
}
static void
_gtk_file_system_init (GtkFileSystem *file_system)
{
  GtkFileSystemPrivate *priv;
  DEBUG ("init");
  file_system->priv = priv = _gtk_file_system_get_instance_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);
}
/* GtkFileSystem public methods */
GtkFileSystem *
_gtk_file_system_new (void)
{
  return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
}
GSList *
_gtk_file_system_list_volumes (GtkFileSystem *file_system)
{
  GtkFileSystemPrivate *priv = file_system->priv;
  GSList *list;
  DEBUG ("list_volumes");
  get_volumes_list (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;
}
static void
free_async_data (AsyncFuncData *async_data)
{
  g_object_unref (async_data->file_system);
  g_object_unref (async_data->file);
  g_object_unref (async_data->cancellable);
  g_free (async_data);
}
static void
query_info_callback (GObject      *source_object,
		     GAsyncResult *result,
		     gpointer      user_data)
{
  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);
  free_async_data (async_data);
}
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 (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);
  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;
}
static void
drive_poll_for_media_cb (GObject      *source_object,
                         GAsyncResult *result,
                         gpointer      user_data)
{
  AsyncFuncData *async_data;
  GError *error = NULL;
  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);
}
static void
volume_mount_cb (GObject      *source_object,
		 GAsyncResult *result,
		 gpointer      user_data)
{
  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 ();
  if (error)
    g_error_free (error);
}
GCancellable *
_gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
			       GtkFileSystemVolume              *volume,
			       GMountOperation                  *mount_operation,
			       GtkFileSystemVolumeMountCallback  callback,
			       gpointer                          data)
{
  GCancellable *cancellable;
  AsyncFuncData *async_data;
  gboolean handled = FALSE;
  DEBUG ("volume_mount");
  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;
}
static void
enclosing_volume_mount_cb (GObject      *source_object,
			   GAsyncResult *result,
			   gpointer      user_data)
{
  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);
  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);
}
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 (G_IS_FILE (file), NULL);
  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;
}
GtkFileSystemVolume *
_gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
				      GFile         *file)
{
  GMount *mount;
  DEBUG ("get_volume_for_file");
  mount = g_file_find_enclosing_mount (file, NULL, NULL);
  if (!mount && g_file_is_native (file))
    return (GtkFileSystemVolume *) root_volume_token;
  return (GtkFileSystemVolume *) mount;
}
/* GtkFileSystemVolume public methods */
gchar *
_gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
{
  DEBUG ("volume_get_display_name");
  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));
  return NULL;
}
gboolean
_gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
{
  gboolean mounted;
  DEBUG ("volume_is_mounted");
  if (IS_ROOT_VOLUME (volume))
    return TRUE;
  mounted = FALSE;
  if (G_IS_MOUNT (volume))
    mounted = TRUE;
  else if (G_IS_VOLUME (volume))
    {
      GMount *mount;
      mount = g_volume_get_mount (G_VOLUME (volume));
      if (mount)
        {
          mounted = TRUE;
          g_object_unref (mount);
        }
    }
  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))
    {
      GMount *mount;
      mount = g_volume_get_mount (G_VOLUME (volume));
      if (mount)
	{
	  file = g_mount_get_root (mount);
	  g_object_unref (mount);
	}
    }
  return file;
}
static cairo_surface_t *
get_surface_from_gicon (GIcon      *icon,
			GtkWidget  *widget,
			gint        icon_size,
			GError    **error)
{
  GtkStyleContext *context;
  GtkIconTheme *icon_theme;
  GtkIconInfo *icon_info;
  GdkPixbuf *pixbuf;
  cairo_surface_t *surface;
  context = gtk_widget_get_style_context (widget);
  icon_theme = gtk_css_icon_theme_value_get_icon_theme
    (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_THEME));
  icon_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme,
                                                        icon,
                                                        icon_size,
                                                        gtk_widget_get_scale_factor (widget),
                                                        GTK_ICON_LOOKUP_USE_BUILTIN);
  if (!icon_info)
    return NULL;
  pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
                                                    context,
                                                    NULL,
                                                    error);
  g_object_unref (icon_info);
  if (pixbuf == NULL)
    return NULL;
  surface = gdk_cairo_surface_create_from_pixbuf (pixbuf,
                                                  gtk_widget_get_scale_factor (widget),
					          gtk_widget_get_window (widget));
  g_object_unref (pixbuf);
  return surface;
}
cairo_surface_t *
_gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
				     GtkWidget            *widget,
				     gint                  icon_size,
				     GError              **error)
{
  GIcon *icon = NULL;
  cairo_surface_t *surface;
  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;
  surface = get_surface_from_gicon (icon, widget, icon_size, error);
  g_object_unref (icon);
  return surface;
}
GIcon *
_gtk_file_system_volume_get_symbolic_icon (GtkFileSystemVolume *volume)
{
  if (IS_ROOT_VOLUME (volume))
    return g_themed_icon_new ("drive-harddisk-symbolic");
  else if (G_IS_DRIVE (volume))
    return g_drive_get_symbolic_icon (G_DRIVE (volume));
  else if (G_IS_VOLUME (volume))
    return g_volume_get_symbolic_icon (G_VOLUME (volume));
  else if (G_IS_MOUNT (volume))
    return g_mount_get_symbolic_icon (G_MOUNT (volume));
  else
    return NULL;
}
GtkFileSystemVolume *
_gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
{
  if (IS_ROOT_VOLUME (volume))
    return volume;
  if (G_IS_MOUNT (volume)  ||
      G_IS_VOLUME (volume) ||
      G_IS_DRIVE (volume))
    g_object_ref (volume);
  return volume;
}
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);
}
/* GFileInfo helper functions */
static cairo_surface_t *
_gtk_file_info_render_icon_internal (GFileInfo *info,
			             GtkWidget *widget,
			             gint       icon_size,
                                     gboolean   symbolic)
{
  GIcon *icon;
  GdkPixbuf *pixbuf;
  const gchar *thumbnail_path;
  cairo_surface_t *surface = NULL;
  int scale;
  thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
  if (thumbnail_path)
    {
      scale = gtk_widget_get_scale_factor (widget);
      pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
						 icon_size*scale, icon_size*scale,
						 NULL);
      if (pixbuf != NULL)
        {
          surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale,
                                                          gtk_widget_get_window (widget));
          g_object_unref (pixbuf);
        }
    }
  if (!surface)
    {
      if (symbolic)
        icon = g_file_info_get_symbolic_icon (info);
      else
        icon = g_file_info_get_icon (info);
      if (icon)
	surface = get_surface_from_gicon (icon, widget, icon_size, NULL);
      if (!surface)
	{
	   /* Use general fallback for all files without icon */
          if (symbolic)
	    icon = g_themed_icon_new ("text-x-generic-symbolic");
          else
	    icon = g_themed_icon_new ("text-x-generic");
	  surface = get_surface_from_gicon (icon, widget, icon_size, NULL);
	  g_object_unref (icon);
	}
    }
  return surface;
}
cairo_surface_t *
_gtk_file_info_render_icon (GFileInfo *info,
			    GtkWidget *widget,
			    gint       icon_size)
{
  return _gtk_file_info_render_icon_internal (info, widget, icon_size, FALSE);
}
gboolean
_gtk_file_info_consider_as_directory (GFileInfo *info)
{
  GFileType type = g_file_info_get_file_type (info);
  
  return (type == G_FILE_TYPE_DIRECTORY ||
          type == G_FILE_TYPE_MOUNTABLE ||
          type == G_FILE_TYPE_SHORTCUT);
}
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;
}
gboolean
_gtk_file_consider_as_remote (GFile *file)
{
  GFileInfo *info;
  gboolean is_remote;
  info = g_file_query_filesystem_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, NULL, NULL);
  if (info)
    {
      is_remote = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE);
      g_object_unref (info);
    }
  else
    is_remote = FALSE;
  return is_remote;
}