forked from AuroraMiddleware/gtk
93bfec3ac5
Add a helper function that says whether a location should be considered remote. To determine this, we look at the filesystem type reported by gvfs, and say 'remote' for sftp, webdav, ftp, nfs and cifs.
955 lines
24 KiB
C
955 lines
24 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
||
*
|
||
* Authors: Carlos Garnacho <carlos@imendio.com>
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <string.h>
|
||
|
||
#include <glib/gi18n-lib.h>
|
||
|
||
#include "gtkfilechooser.h"
|
||
#include "gtkfilesystem.h"
|
||
#include "gtkicontheme.h"
|
||
#include "gtkprivate.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_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;
|
||
}
|
||
|
||
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 ("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);
|
||
}
|
||
|
||
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_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
|
||
g_slist_free (priv->volumes);
|
||
priv->volumes = NULL;
|
||
}
|
||
|
||
/* first go through all connected drives */
|
||
drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
|
||
|
||
for (l = drives; l != NULL; l = l->next)
|
||
{
|
||
drive = l->data;
|
||
volumes = g_drive_get_volumes (drive);
|
||
|
||
if (volumes)
|
||
{
|
||
for (ll = volumes; ll != NULL; ll = ll->next)
|
||
{
|
||
volume = ll->data;
|
||
mount = g_volume_get_mount (volume);
|
||
|
||
if (mount)
|
||
{
|
||
/* Show mounted volume */
|
||
priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
|
||
g_object_unref (mount);
|
||
}
|
||
else
|
||
{
|
||
/* Do show the unmounted volumes in the sidebar;
|
||
* this is so the user can mount it (in case automounting
|
||
* is off).
|
||
*
|
||
* Also, even if automounting is enabled, this gives a visual
|
||
* cue that the user should remember to yank out the media if
|
||
* he just unmounted it.
|
||
*/
|
||
priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
|
||
}
|
||
|
||
g_object_unref (volume);
|
||
}
|
||
|
||
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)
|
||
{
|
||
GdkScreen *screen;
|
||
GtkIconTheme *icon_theme;
|
||
GtkIconInfo *icon_info;
|
||
GdkPixbuf *pixbuf;
|
||
cairo_surface_t *surface;
|
||
|
||
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_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,
|
||
gtk_widget_get_style_context (widget),
|
||
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;
|
||
}
|
||
|
||
cairo_surface_t *
|
||
_gtk_file_system_volume_render_symbolic_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-symbolic");
|
||
else if (G_IS_DRIVE (volume))
|
||
icon = g_drive_get_symbolic_icon (G_DRIVE (volume));
|
||
else if (G_IS_VOLUME (volume))
|
||
icon = g_volume_get_symbolic_icon (G_VOLUME (volume));
|
||
else if (G_IS_MOUNT (volume))
|
||
icon = g_mount_get_symbolic_icon (G_MOUNT (volume));
|
||
|
||
if (!icon)
|
||
return NULL;
|
||
|
||
surface = get_surface_from_gicon (icon, widget, icon_size, error);
|
||
|
||
g_object_unref (icon);
|
||
|
||
return surface;
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
cairo_surface_t *
|
||
_gtk_file_info_render_symbolic_icon (GFileInfo *info,
|
||
GtkWidget *widget,
|
||
gint icon_size)
|
||
{
|
||
return _gtk_file_info_render_icon_internal (info, widget, icon_size, TRUE);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
static const gchar * const remote_types[] = {
|
||
"sftp",
|
||
"webdav",
|
||
"ftp",
|
||
"nfs",
|
||
"cifs",
|
||
NULL
|
||
};
|
||
|
||
gboolean
|
||
_gtk_file_consider_as_remote (GFile *file)
|
||
{
|
||
GFileInfo *info;
|
||
gboolean is_remote;
|
||
|
||
info = g_file_query_filesystem_info (file, "filesystem::type", NULL, NULL);
|
||
if (info)
|
||
{
|
||
const gchar *type;
|
||
|
||
type = g_file_info_get_attribute_string (info, "filesystem::type");
|
||
is_remote = g_strv_contains (remote_types, type);
|
||
|
||
g_object_unref (info);
|
||
}
|
||
else
|
||
is_remote = FALSE;
|
||
|
||
return is_remote;
|
||
}
|