forked from AuroraMiddleware/gtk
a8a56e8a0e
If we set c_marshaller manually, then g_signal_newv() will not setup a va_marshaller for us. However, if we provide c_marshaller as NULL, it will setup both the c_marshaller (to g_cclosure_marshal_VOID__VOID) and va_marshaller (to g_cclosure_marshal_VOID__VOIDv) for us.
923 lines
24 KiB
C
923 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 "gtkfilesystem.h"
|
||
|
||
#include <string.h>
|
||
#include <glib/gi18n-lib.h>
|
||
|
||
#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;
|
||
}
|