Updating branch refs/heads/nick/new-shortcuts-pane-model to a0f743d61115cf90a48d6273f9b569e2b7dad63f (commit) from e96bd793fbed41667d3fd8bbfc2ed231af71559d (commit)
commit a0f743d61115cf90a48d6273f9b569e2b7dad63f Author: Nick Schermer <n...@xfce.org> Date: Sun Oct 7 00:10:30 2012 +0200 Add ThunarDevice and ThunarDeviceMonitor. This is an shell around GVolumeMonitor and GVolume/GMount and possibly in the future GDrive. The idea is that the models and views don't need to know what type they handle. If a volume/drive/mount needs to be visible to the user, it is added with device-added, if something changed, device-removed is triggered and when the device need to be invisible or is removed device-removed is called. It also takes care of the mount/unmount/eject feature, independent of the type. thunar/Makefile.am | 4 + thunar/thunar-device-monitor.c | 639 ++++++++++++++++++++++++++++++++++++ thunar/thunar-device-monitor.h | 44 +++ thunar/thunar-device.c | 711 ++++++++++++++++++++++++++++++++++++++++ thunar/thunar-device.h | 88 +++++ 5 files changed, 1486 insertions(+), 0 deletions(-) diff --git a/thunar/Makefile.am b/thunar/Makefile.am index 3bafe9d..bc12573 100644 --- a/thunar/Makefile.am +++ b/thunar/Makefile.am @@ -71,6 +71,10 @@ thunar_SOURCES = \ thunar-details-view.h \ thunar-dialogs.c \ thunar-dialogs.h \ + thunar-device.c \ + thunar-device.h \ + thunar-device-monitor.c \ + thunar-device-monitor.h \ thunar-dnd.c \ thunar-dnd.h \ thunar-emblem-chooser.c \ diff --git a/thunar/thunar-device-monitor.c b/thunar/thunar-device-monitor.c new file mode 100644 index 0000000..c70ff7a --- /dev/null +++ b/thunar/thunar-device-monitor.c @@ -0,0 +1,639 @@ +/*- + * Copyright (c) 2009-2010 Jannis Pohlmann <jan...@xfce.org> + * Copyright (c) 2012 Nick Schermer <n...@xfce.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 General Public License for + * more details. + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gio/gio.h> +#ifdef HAVE_GIO_UNIX +#include <gio/gunixmounts.h> +#endif + +#include <thunar/thunar-device-monitor.h> +#include <thunar/thunar-private.h> + + + +/* signal identifiers */ +enum +{ + DEVICE_ADDED, + DEVICE_REMOVED, + DEVICE_CHANGED, + LAST_SIGNAL +}; + + + +static void thunar_device_monitor_finalize (GObject *object); +static void thunar_device_monitor_volume_added (GVolumeMonitor *volume_monitor, + GVolume *volume, + ThunarDeviceMonitor *monitor); +static void thunar_device_monitor_volume_removed (GVolumeMonitor *volume_monitor, + GVolume *volume, + ThunarDeviceMonitor *monitor); +static void thunar_device_monitor_volume_changed (GVolumeMonitor *volume_monitor, + GVolume *volume, + ThunarDeviceMonitor *monitor); +static void thunar_device_monitor_mount_added (GVolumeMonitor *volume_monitor, + GMount *mount, + ThunarDeviceMonitor *monitor); +static void thunar_device_monitor_mount_removed (GVolumeMonitor *volume_monitor, + GMount *mount, + ThunarDeviceMonitor *monitor); +static void thunar_device_monitor_mount_changed (GVolumeMonitor *volume_monitor, + GMount *mount, + ThunarDeviceMonitor *monitor); + + + +struct _ThunarDeviceMonitorClass +{ + GObjectClass __parent__; + + /* signals */ + void (*device_added) (ThunarDeviceMonitor *monitor, + ThunarDevice *device); + void (*device_removed) (ThunarDeviceMonitor *monitor, + ThunarDevice *device); + void (*device_changed) (ThunarDeviceMonitor *monitor, + ThunarDevice *device); +}; + +struct _ThunarDeviceMonitor +{ + GObject __parent__; + + GVolumeMonitor *volume_monitor; + + /* GVolume/GMount -> ThunarDevice */ + GHashTable *devices; + + GList *hidden_volumes; +}; + +struct _ThunarDevice +{ + +}; + + + +static guint device_monitor_signals[LAST_SIGNAL]; + + + +G_DEFINE_TYPE (ThunarDeviceMonitor, thunar_device_monitor, G_TYPE_OBJECT) + + + +static void +thunar_device_monitor_class_init (ThunarDeviceMonitorClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_device_monitor_finalize; + + device_monitor_signals[DEVICE_ADDED] = + g_signal_new (I_("device-added"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ThunarDeviceMonitorClass, device_added), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + device_monitor_signals[DEVICE_REMOVED] = + g_signal_new (I_("device-removed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ThunarDeviceMonitorClass, device_removed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + device_monitor_signals[DEVICE_CHANGED] = + g_signal_new (I_("device-changed"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ThunarDeviceMonitorClass, device_changed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + + + +static void +thunar_device_monitor_init (ThunarDeviceMonitor *monitor) +{ + GList *list; + GList *lp; + + /* table for GVolume/GMount (key) -> ThunarDevice (value) */ + monitor->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_object_unref); + + /* gio volume monitor */ + monitor->volume_monitor = g_volume_monitor_get (); + + /* load all volumes */ + list = g_volume_monitor_get_volumes (monitor->volume_monitor); + for (lp = list; lp != NULL; lp = lp->next) + { + thunar_device_monitor_volume_added (monitor->volume_monitor, lp->data, monitor); + g_object_unref (G_OBJECT (lp->data)); + } + g_list_free (list); + + /* load all mount */ + list = g_volume_monitor_get_mounts (monitor->volume_monitor); + for (lp = list; lp != NULL; lp = lp->next) + { + thunar_device_monitor_mount_added (monitor->volume_monitor, lp->data, monitor); + g_object_unref (G_OBJECT (lp->data)); + } + g_list_free (list); + + /* watch changes */ + g_signal_connect (monitor->volume_monitor, "volume-added", G_CALLBACK (thunar_device_monitor_volume_added), monitor); + g_signal_connect (monitor->volume_monitor, "volume-removed", G_CALLBACK (thunar_device_monitor_volume_removed), monitor); + g_signal_connect (monitor->volume_monitor, "volume-changed", G_CALLBACK (thunar_device_monitor_volume_changed), monitor); + g_signal_connect (monitor->volume_monitor, "mount-added", G_CALLBACK (thunar_device_monitor_mount_added), monitor); + g_signal_connect (monitor->volume_monitor, "mount-removed", G_CALLBACK (thunar_device_monitor_mount_removed), monitor); + g_signal_connect (monitor->volume_monitor, "mount-changed", G_CALLBACK (thunar_device_monitor_mount_changed), monitor); +} + + + +static void +thunar_device_monitor_finalize (GObject *object) +{ + ThunarDeviceMonitor *monitor = THUNAR_DEVICE_MONITOR (object); + + /* detatch from the monitor */ + g_signal_handlers_disconnect_matched (monitor->volume_monitor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, monitor); + g_object_unref (monitor->volume_monitor); + + /* clear list of devices */ + g_hash_table_destroy (monitor->devices); + + /* clear list of hidden volumes */ + g_list_free_full (monitor->hidden_volumes, g_object_unref); + + (*G_OBJECT_CLASS (thunar_device_monitor_parent_class)->finalize) (object); +} + + + +#ifdef HAVE_GIO_UNIX +static gboolean +thunar_device_monitor_mount_is_internal (GMount *mount) +{ + const gchar *point_mount_path; + gboolean is_internal = FALSE; + GFile *root; + GList *lp; + GList *mount_points; + gchar *mount_path; + + _thunar_return_val_if_fail (G_IS_MOUNT (mount), FALSE); + + /* determine the mount path */ + root = g_mount_get_root (mount); + mount_path = g_file_get_path (root); + g_object_unref (root); + + /* assume non-internal if we cannot determine the path */ + if (mount_path == NULL) + return FALSE; + + if (g_unix_is_mount_path_system_internal (mount_path)) + { + /* mark as internal */ + is_internal = TRUE; + } + else + { + /* get a list of all mount points */ + mount_points = g_unix_mount_points_get (NULL); + + /* search for the mount point associated with the mount entry */ + for (lp = mount_points; !is_internal && lp != NULL; lp = lp->next) + { + point_mount_path = g_unix_mount_point_get_mount_path (lp->data); + + /* check if this is the mount point we are looking for */ + if (g_strcmp0 (mount_path, point_mount_path) == 0) + { + /* mark as internal if the user cannot mount this device */ + if (!g_unix_mount_point_is_user_mountable (lp->data)) + is_internal = TRUE; + } + + /* free the mount point, we no longer need it */ + g_unix_mount_point_free (lp->data); + } + + /* free the mount point list */ + g_list_free (mount_points); + } + + g_free (mount_path); + + return is_internal; +} +#endif + + + +static gboolean +thunar_device_monitor_has_location (ThunarDeviceMonitor *monitor, + GFile *location) +{ + + _thunar_return_val_if_fail (G_IS_FILE (location), FALSE); + _thunar_return_val_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor), FALSE); + + return FALSE; +} + + + +static gboolean +thunar_device_monitor_mount_is_hidden (GMount *mount, + ThunarDeviceMonitor *monitor) +{ + GVolume *volume; + GFile *location; + + _thunar_return_val_if_fail (G_IS_MOUNT (mount), TRUE); + _thunar_return_val_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor), TRUE); + + /* never show shadowed mounts */ + if (g_mount_is_shadowed (mount)) + return TRUE; + + /* skip mounts with a volume, we prefer a volume as device */ + volume = g_mount_get_volume (mount); + if (volume != NULL) + { + g_object_unref (volume); + return TRUE; + } + + location = g_mount_get_root (mount); + + /* skip ghoto locations, since those also have a volume + * and igore locations already in the device list */ + if (g_file_has_uri_scheme (location, "gphoto2") + || thunar_device_monitor_has_location (monitor, location)) + { + g_object_unref (location); + return TRUE; + } + + return FALSE; +} + + + +static gboolean +thunar_device_monitor_volume_is_visible (GVolume *volume) +{ + gboolean can_eject = FALSE; + gboolean can_mount = FALSE; + gboolean can_unmount = FALSE; + gboolean is_removable = FALSE; + gboolean is_internal = FALSE; + GDrive *drive; + GMount *mount; + + _thunar_return_val_if_fail (G_IS_VOLUME (volume), TRUE); + + /* determine the mount for the volume (if it is mounted at all) */ + mount = g_volume_get_mount (volume); + if (mount != NULL) + { +#ifdef HAVE_GIO_UNIX + is_internal = thunar_device_monitor_mount_is_internal (mount); +#endif + + /* check if the volume can be unmounted */ + can_unmount = g_mount_can_unmount (mount); + + /* release the mount */ + g_object_unref (mount); + } + + /* don't show internal volumes */ + if (is_internal) + return FALSE; + + /* check if the volume can be ejected */ + can_eject = g_volume_can_eject (volume); + + /* determine the drive for the volume */ + drive = g_volume_get_drive (volume); + if (drive != NULL) + { + /* check if the drive media can be removed */ + is_removable = g_drive_is_media_removable (drive); + + /* release the drive */ + g_object_unref (drive); + } + + /* determine whether the device can be mounted */ + can_mount = g_volume_can_mount (volume); + + return (can_eject || can_unmount || is_removable || can_mount); +} + + + +static void +thunar_device_monitor_volume_added (GVolumeMonitor *volume_monitor, + GVolume *volume, + ThunarDeviceMonitor *monitor) +{ + _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor)); + _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor)); + _thunar_return_if_fail (monitor->volume_monitor == volume_monitor); + _thunar_return_if_fail (G_IS_VOLUME (volume)); + + /* add to internal list */ + monitor->hidden_volumes = g_list_prepend (monitor->hidden_volumes, g_object_ref (volume)); + + /* change visibility in changed */ + thunar_device_monitor_volume_changed (volume_monitor, volume, monitor); +} + + + +static void +thunar_device_monitor_volume_removed (GVolumeMonitor *volume_monitor, + GVolume *volume, + ThunarDeviceMonitor *monitor) +{ + ThunarDevice *device; + GList *lp; + + _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor)); + _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor)); + _thunar_return_if_fail (monitor->volume_monitor == volume_monitor); + _thunar_return_if_fail (G_IS_VOLUME (volume)); + + /* remove the volume */ + lp = g_list_find (monitor->hidden_volumes, volume); + if (lp != NULL) + { + /* silently drop it */ + monitor->hidden_volumes = g_list_delete_link (monitor->hidden_volumes, lp); + + /* release ref from hidden list */ + g_object_unref (G_OBJECT (volume)); + } + else + { + /* find device */ + device = g_hash_table_lookup (monitor->devices, volume); + + /* meh */ + _thunar_return_if_fail (THUNAR_IS_DEVICE (device)); + if (G_UNLIKELY (device == NULL)) + return; + + /* the device is not visble for the user */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_REMOVED], 0, device); + + /* drop it */ + g_hash_table_remove (monitor->devices, volume); + } +} + + + +static void +thunar_device_monitor_volume_changed (GVolumeMonitor *volume_monitor, + GVolume *volume, + ThunarDeviceMonitor *monitor) +{ + GList *lp; + ThunarDevice *device; + + _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor)); + _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor)); + _thunar_return_if_fail (monitor->volume_monitor == volume_monitor); + _thunar_return_if_fail (G_IS_VOLUME (volume)); + + lp = g_list_find (monitor->hidden_volumes, volume); + if (lp != NULL) + { + /* check if the volume should be visible again */ + if (thunar_device_monitor_volume_is_visible (volume)) + { + /* remove from the hidden list */ + monitor->hidden_volumes = g_list_delete_link (monitor->hidden_volumes, lp); + + /* create a new device for this volume */ + device = g_object_new (THUNAR_TYPE_DEVICE, + "device", volume, + "kind", THUNAR_DEVICE_KIND_VOLUME, + NULL); + + /* insert to list (takes ref from hidden list) */ + g_hash_table_insert (monitor->devices, volume, device); + + /* notify */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_ADDED], 0, device); + } + } + else + { + /* find device */ + device = g_hash_table_lookup (monitor->devices, volume); + + /* meh */ + _thunar_return_if_fail (THUNAR_IS_DEVICE (device)); + if (G_UNLIKELY (device == NULL)) + return; + + if (!thunar_device_monitor_volume_is_visible (volume)) + { + /* remove from table */ + g_hash_table_steal (monitor->devices, volume); + + /* insert volume in hidden table, take ref from table */ + monitor->hidden_volumes = g_list_prepend (monitor->hidden_volumes, volume); + + /* the device is not visble for the user */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_REMOVED], 0, device); + + /* destroy device */ + g_object_unref (G_OBJECT (device)); + } + else + { + /* the device changed */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_CHANGED], 0, device); + } + } +} + + + +static void +thunar_device_monitor_mount_added (GVolumeMonitor *volume_monitor, + GMount *mount, + ThunarDeviceMonitor *monitor) +{ + ThunarDevice *device; + GFile *location; + ThunarDeviceKind kind = THUNAR_DEVICE_KIND_MOUNT_LOCAL; + + _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor)); + _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor)); + _thunar_return_if_fail (monitor->volume_monitor == volume_monitor); + _thunar_return_if_fail (G_IS_MOUNT (mount)); + + if (!thunar_device_monitor_mount_is_hidden (mount, monitor)) + { + location = g_mount_get_root (mount); + if (G_LIKELY (location != NULL)) + { + if (g_file_has_uri_scheme (location, "file") + || g_file_has_uri_scheme (location, "archive")) + kind = THUNAR_DEVICE_KIND_MOUNT_LOCAL; + else + kind = THUNAR_DEVICE_KIND_MOUNT_REMOTE; + + g_object_unref (location); + } + + /* create a new device for this mount */ + device = g_object_new (THUNAR_TYPE_DEVICE, + "device", mount, + "kind", kind, + NULL); + + /* insert to list */ + g_hash_table_insert (monitor->devices, g_object_ref (mount), device); + + /* notify */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_ADDED], 0, device); + } +} + + + +static void +thunar_device_monitor_mount_removed (GVolumeMonitor *volume_monitor, + GMount *mount, + ThunarDeviceMonitor *monitor) +{ + ThunarDevice *device; + + _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor)); + _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor)); + _thunar_return_if_fail (monitor->volume_monitor == volume_monitor); + _thunar_return_if_fail (G_IS_MOUNT (mount)); + + /* check if we have a device for this mount */ + device = g_hash_table_lookup (monitor->devices, mount); + if (device != NULL) + { + /* notify */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_REMOVED], 0, device); + + /* drop it */ + g_hash_table_remove (monitor->devices, mount); + } +} + + + +static void +thunar_device_monitor_mount_changed (GVolumeMonitor *volume_monitor, + GMount *mount, + ThunarDeviceMonitor *monitor) +{ + ThunarDevice *device; + + _thunar_return_if_fail (G_IS_VOLUME_MONITOR (volume_monitor)); + _thunar_return_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor)); + _thunar_return_if_fail (monitor->volume_monitor == volume_monitor); + _thunar_return_if_fail (G_IS_MOUNT (mount)); + + /* check if we have a device for this mount */ + device = g_hash_table_lookup (monitor->devices, mount); + if (device != NULL) + { + /* notify */ + g_signal_emit (G_OBJECT (monitor), device_monitor_signals[DEVICE_CHANGED], 0, device); + } +} + + + +static void +thunar_device_monitor_list_prepend (gpointer key, + gpointer value, + gpointer user_data) +{ + GList **list = user_data; + + _thunar_return_if_fail (THUNAR_IS_DEVICE (value)); + *list = g_list_prepend (*list, g_object_ref (value)); +} + + + +ThunarDeviceMonitor * +thunar_device_monitor_get (void) +{ +static ThunarDeviceMonitor *monitor = NULL; + + if (G_UNLIKELY (monitor == NULL)) + { + monitor = g_object_new (THUNAR_TYPE_DEVICE_MONITOR, NULL); + g_object_add_weak_pointer (G_OBJECT (monitor), (gpointer) &monitor); + } + else + { + g_object_ref (G_OBJECT (monitor)); + } + + return monitor; +} + + + +GList * +thunar_device_monitor_get_devices (ThunarDeviceMonitor *monitor) +{ + GList *list = NULL; + + _thunar_return_val_if_fail (THUNAR_IS_DEVICE_MONITOR (monitor), NULL); + + g_hash_table_foreach (monitor->devices, thunar_device_monitor_list_prepend, &list); + + return list; +} diff --git a/thunar/thunar-device-monitor.h b/thunar/thunar-device-monitor.h new file mode 100644 index 0000000..4932c41 --- /dev/null +++ b/thunar/thunar-device-monitor.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2012 Nick Schermer <n...@xfce.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 General Public License for + * more details. + * + * 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 + */ + +#ifndef __THUNAR_DEVICE_MONITOR_H__ +#define __THUNAR_DEVICE_MONITOR_H__ + +#include <thunar/thunar-device.h> + +G_BEGIN_DECLS + +typedef struct _ThunarDeviceMonitorClass ThunarDeviceMonitorClass; +typedef struct _ThunarDeviceMonitor ThunarDeviceMonitor; + +#define THUNAR_TYPE_DEVICE_MONITOR (thunar_device_monitor_get_type ()) +#define THUNAR_DEVICE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_DEVICE_MONITOR, ThunarDeviceMonitor)) +#define THUNAR_DEVICE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_DEVICE_MONITOR, ThunarDeviceMonitorClass)) +#define THUNAR_IS_DEVICE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_DEVICE_MONITOR)) +#define THUNAR_IS_DEVICE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), THUNAR_TYPE_DEVICE_MONITOR)) +#define THUNAR_DEVICE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_DEVICE_MONITOR, ThunarDeviceMonitorClass)) + +GType thunar_device_monitor_get_type (void) G_GNUC_CONST; + +ThunarDeviceMonitor *thunar_device_monitor_get (void); + +GList *thunar_device_monitor_get_devices (ThunarDeviceMonitor *monitor); + +G_END_DECLS + +#endif /* !__THUNAR_DEVICE_MONITOR_H__ */ diff --git a/thunar/thunar-device.c b/thunar/thunar-device.c new file mode 100644 index 0000000..0178014 --- /dev/null +++ b/thunar/thunar-device.c @@ -0,0 +1,711 @@ +/*- + * Copyright (c) 2012 Nick Schermer <n...@xfce.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 General Public License for + * more details. + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_LIBNOTIFY +#include <thunar/thunar-notify.h> +#endif +#include <thunar/thunar-device.h> +#include <thunar/thunar-private.h> + + + +enum +{ + PROP_0, + PROP_DEVICE, + PROP_KIND +}; + + + +static void thunar_device_finalize (GObject *object); +static void thunar_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void thunar_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + + + +struct _ThunarDeviceClass +{ + GObjectClass __parent__; +}; + +struct _ThunarDevice +{ + GObject __parent__; + + /* a GVolume/GMount/GDrive */ + gpointer device; + + ThunarDeviceKind kind; +}; + +typedef struct +{ + ThunarDevice *device; + ThunarDeviceCallback callback; + gpointer user_data; +} +ThunarDeviceOperation; + + + +G_DEFINE_TYPE (ThunarDevice, thunar_device, G_TYPE_OBJECT) + + + +static void +thunar_device_class_init (ThunarDeviceClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_device_finalize; + gobject_class->get_property = thunar_device_get_property; + gobject_class->set_property = thunar_device_set_property; + + g_object_class_install_property (gobject_class, + PROP_DEVICE, + g_param_spec_object ("device", + "device", + "device", + G_TYPE_OBJECT, + EXO_PARAM_READWRITE + | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (gobject_class, + PROP_KIND, + g_param_spec_uint ("kind", + "kind", + "kind", + THUNAR_DEVICE_KIND_VOLUME, + THUNAR_DEVICE_KIND_MOUNT_REMOTE, + THUNAR_DEVICE_KIND_VOLUME, + EXO_PARAM_READWRITE + | G_PARAM_CONSTRUCT_ONLY)); +} + + + +static void +thunar_device_init (ThunarDevice *device) +{ + device->kind = THUNAR_DEVICE_KIND_VOLUME; +} + + + +static void +thunar_device_finalize (GObject *object) +{ + ThunarDevice *device = THUNAR_DEVICE (object); + + if (device->device != NULL) + g_object_unref (G_OBJECT (device->device)); + + (*G_OBJECT_CLASS (thunar_device_parent_class)->finalize) (object); +} + + + +static void +thunar_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + ThunarDevice *device = THUNAR_DEVICE (object); + + switch (prop_id) + { + case PROP_DEVICE: + g_value_set_object (value, device->device); + break; + + case PROP_KIND: + g_value_set_uint (value, device->kind); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + +static void +thunar_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ThunarDevice *device = THUNAR_DEVICE (object); + + switch (prop_id) + { + case PROP_DEVICE: + device->device = g_value_dup_object (value); + _thunar_assert (G_IS_VOLUME (device->device) || G_IS_MOUNT (device->device)); + break; + + case PROP_KIND: + device->kind = g_value_get_uint (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + + + +static ThunarDeviceOperation * +thunar_device_operation_new (ThunarDevice *device, + ThunarDeviceCallback callback, + gpointer user_data) +{ + ThunarDeviceOperation *operation; + + operation = g_slice_new0 (ThunarDeviceOperation); + operation->device = g_object_ref (device); + operation->callback = callback; + operation->user_data = user_data; + + return operation; +} + + + +static void +thunar_device_operation_free (ThunarDeviceOperation *operation) +{ + g_object_unref (operation->device); + g_slice_free (ThunarDeviceOperation, operation); +} + + + +static void +thunar_device_mount_unmount_finish (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ThunarDeviceOperation *operation = user_data; + GError *error = NULL; + + _thunar_return_if_fail (G_IS_MOUNT (object)); + _thunar_return_if_fail (G_IS_ASYNC_RESULT (result)); + +#ifdef HAVE_LIBNOTIFY + thunar_notify_unmount_finish (G_MOUNT (object)); +#endif + + /* finish the unmount */ + if (!g_mount_unmount_with_operation_finish (G_MOUNT (object), result, &error)) + { + /* unset the error if a helper program has already interacted with the user */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) + g_clear_error (&error); + } + + /* callback */ + (operation->callback) (operation->device, error, operation->user_data); + + /* cleanup */ + if (error != NULL) + g_error_free (error); + thunar_device_operation_free (operation); +} + + + +static void +thunar_device_mount_eject_finish (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ThunarDeviceOperation *operation = user_data; + GError *error = NULL; + + _thunar_return_if_fail (G_IS_MOUNT (object)); + _thunar_return_if_fail (G_IS_ASYNC_RESULT (result)); + +#ifdef HAVE_LIBNOTIFY + thunar_notify_unmount_finish (G_MOUNT (object)); +#endif + + /* finish the eject */ + if (!g_mount_eject_with_operation_finish (G_MOUNT (object), result, &error)) + { + /* unset the error if a helper program has already interacted with the user */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) + g_clear_error (&error); + } + + /* callback */ + (operation->callback) (operation->device, error, operation->user_data); + + /* cleanup */ + if (error != NULL) + g_error_free (error); + thunar_device_operation_free (operation); +} + + + +static void +thunar_device_volume_eject_finish (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + ThunarDeviceOperation *operation = user_data; + GError *error = NULL; + + _thunar_return_if_fail (G_IS_VOLUME (object)); + _thunar_return_if_fail (G_IS_ASYNC_RESULT (result)); + +#ifdef HAVE_LIBNOTIFY + thunar_notify_eject_finish (G_VOLUME (object)); +#endif + + /* finish the eject */ + if (!g_volume_eject_with_operation_finish (G_VOLUME (object), result, &error)) + { + /* unset the error if a helper program has already interacted with the user */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) + g_clear_error (&error); + } + + /* callback */ + (operation->callback) (operation->device, error, operation->user_data); + + /* cleanup */ + if (error != NULL) + g_error_free (error); + thunar_device_operation_free (operation); +} + + + +gchar * +thunar_device_get_name (const ThunarDevice *device) +{ + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL); + + if (G_IS_VOLUME (device->device)) + return g_volume_get_name (device->device); + else if (G_IS_MOUNT (device->device)) + return g_mount_get_name (device->device); + else + _thunar_assert_not_reached (); + + return NULL; +} + + + +GIcon * +thunar_device_get_icon (const ThunarDevice *device) +{ + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL); + + if (G_IS_VOLUME (device->device)) + return g_volume_get_icon (device->device); + else if (G_IS_MOUNT (device->device)) + return g_mount_get_icon (device->device); + else + _thunar_assert_not_reached (); + + return NULL; +} + + + +ThunarDeviceKind +thunar_device_get_kind (const ThunarDevice *device) +{ + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), THUNAR_DEVICE_KIND_VOLUME); + return device->kind; +} + + + +/** + * thunar_device_can_eject: + * + * If the user should see the option to eject this device. + **/ +gboolean +thunar_device_can_eject (const ThunarDevice *device) +{ + gboolean can_eject = FALSE; + GMount *volume_mount; + + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE); + + if (G_IS_VOLUME (device->device)) + { + can_eject = g_volume_can_eject (device->device); + + if (!can_eject) + { + /* check if the mount can eject/unmount */ + volume_mount = g_volume_get_mount (device->device); + if (volume_mount != NULL) + { + can_eject = g_mount_can_eject (volume_mount) || g_mount_can_unmount (volume_mount); + g_object_unref (volume_mount); + } + } + } + else if (G_IS_MOUNT (device->device)) + { + /* eject or unmount because thats for the user the same + * because we prefer volumes over mounts as devices */ + can_eject = g_mount_can_eject (device->device) || g_mount_can_unmount (device->device); + } + else + _thunar_assert_not_reached (); + + return can_eject; +} + + + +/** + * thunar_device_can_mount: + * + * If the user should see the option to mount this device. + **/ +gboolean +thunar_device_can_mount (const ThunarDevice *device) +{ + gboolean can_mount = FALSE; + GMount *volume_mount; + + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE); + + if (G_IS_VOLUME (device->device)) + { + /* only volumes without a mountpoint and mount capability */ + volume_mount = g_volume_get_mount (device->device); + if (volume_mount == NULL) + can_mount = g_volume_can_mount (device->device); + else + g_object_unref (volume_mount); + } + else if (G_IS_MOUNT (device->device)) + { + /* a mount is already mounted... */ + can_mount = FALSE; + } + else + _thunar_assert_not_reached (); + + return can_mount; +} + + + +/** + * thunar_device_can_unmount: + * + * If the user should see the option to unmount this device. + **/ +gboolean +thunar_device_can_unmount (const ThunarDevice *device) +{ + gboolean can_unmount = FALSE; + GMount *volume_mount; + + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE); + + if (G_IS_VOLUME (device->device)) + { + /* only volumes with a mountpoint and unmount capability */ + volume_mount = g_volume_get_mount (device->device); + if (volume_mount != NULL) + { + can_unmount = g_mount_can_unmount (volume_mount); + g_object_unref (volume_mount); + } + } + else if (G_IS_MOUNT (device->device)) + { + /* check if the mount can unmount */ + can_unmount = g_mount_can_unmount (device->device); + } + else + _thunar_assert_not_reached (); + + return can_unmount; +} + + + +gboolean +thunar_device_is_mounted (const ThunarDevice *device) +{ + gboolean is_mounted = FALSE; + GMount *volume_mount; + + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE); + + if (G_IS_VOLUME (device->device)) + { + /* a volume with a mount point is mounted */ + volume_mount = g_volume_get_mount (device->device); + if (volume_mount != NULL) + { + is_mounted = TRUE; + g_object_unref (volume_mount); + } + } + else if (G_IS_MOUNT (device->device)) + { + /* mounter are always mounted... */ + is_mounted = TRUE; + } + else + _thunar_assert_not_reached (); + + return is_mounted; +} + + + +GFile * +thunar_device_get_root (const ThunarDevice *device) +{ + GFile *root = NULL; + GMount *volume_mount; + + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL); + + if (G_IS_VOLUME (device->device)) + { + volume_mount = g_volume_get_mount (device->device); + if (volume_mount != NULL) + { + root = g_mount_get_root (volume_mount); + g_object_unref (volume_mount); + } + } + else if (G_IS_MOUNT (device->device)) + { + root = g_mount_get_root (device->device); + } + else + _thunar_assert_not_reached (); + + return root; +} + + + +const gchar * +thunar_device_get_sort_key (const ThunarDevice *device) +{ + _thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL); + + if (G_IS_VOLUME (device->device)) + return g_volume_get_sort_key (device->device); + else if (G_IS_MOUNT (device->device)) + return g_mount_get_sort_key (device->device); + else + _thunar_assert_not_reached (); + + return NULL; +} + + + +void +thunar_device_mount (ThunarDevice *device, + GMountOperation *mount_operation, + GCancellable *cancellable, + ThunarDeviceCallback callback, + gpointer user_data) +{ + _thunar_return_if_fail (THUNAR_IS_DEVICE (device)); + _thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation)); + _thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + _thunar_return_if_fail (callback != NULL); +} + + + +/** + * thunar_device_unmount: + * + * Unmount a #ThunarDevice. Don't try to eject. + **/ +void +thunar_device_unmount (ThunarDevice *device, + GMountOperation *mount_operation, + GCancellable *cancellable, + ThunarDeviceCallback callback, + gpointer user_data) +{ + ThunarDeviceOperation *operation; + GMount *mount; + + _thunar_return_if_fail (THUNAR_IS_DEVICE (device)); + _thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation)); + _thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + _thunar_return_if_fail (callback != NULL); + + /* get the mount from the volume or use existing mount */ + if (G_IS_VOLUME (device->device)) + mount = g_volume_get_mount (device->device); + else if (G_IS_MOUNT (device->device)) + mount = g_object_ref (device->device); + else + mount = NULL; + + if (G_LIKELY (mount != NULL)) + { + /* only handle mounts that can be unmounted here */ + if (g_mount_can_unmount (mount)) + { +#ifdef HAVE_LIBNOTIFY + thunar_notify_unmount (mount); +#endif + + /* try unmounting the mount */ + operation = thunar_device_operation_new (device, callback, user_data); + g_mount_unmount_with_operation (mount, + G_MOUNT_UNMOUNT_NONE, + mount_operation, + cancellable, + thunar_device_mount_unmount_finish, + operation); + } + + g_object_unref (G_OBJECT (mount)); + } +} + + + +/** + * thunar_device_unmount: + * + * Try to eject a #ThunarDevice, fall-back to unmounting + **/ +void +thunar_device_eject (ThunarDevice *device, + GMountOperation *mount_operation, + GCancellable *cancellable, + ThunarDeviceCallback callback, + gpointer user_data) +{ + ThunarDeviceOperation *operation; + GMount *mount = NULL; + GVolume *volume; + + _thunar_return_if_fail (THUNAR_IS_DEVICE (device)); + _thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation)); + _thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + _thunar_return_if_fail (callback != NULL); + + + if (G_IS_VOLUME (device->device)) + { + volume = device->device; + + if (g_volume_can_eject (volume)) + { +#ifdef HAVE_LIBNOTIFY + thunar_notify_eject (volume); +#endif + + /* try ejecting the volume */ + operation = thunar_device_operation_new (device, callback, user_data); + g_volume_eject_with_operation (volume, + G_MOUNT_UNMOUNT_NONE, + mount_operation, + cancellable, + thunar_device_volume_eject_finish, + operation); + + /* done */ + return; + } + else + { + /* get the mount and fall-through */ + mount = g_volume_get_mount (volume); + } + } + else if (G_IS_MOUNT (device->device)) + { + /* set the mount and fall-through */ + mount = g_object_ref (device->device); + } + + /* handle mounts */ + if (mount != NULL) + { + /* distinguish between ejectable and unmountable mounts */ + if (g_mount_can_eject (mount)) + { +#ifdef HAVE_LIBNOTIFY + thunar_notify_unmount (mount); +#endif + + /* try ejecting the mount */ + operation = thunar_device_operation_new (device, callback, user_data); + g_mount_eject_with_operation (mount, + G_MOUNT_UNMOUNT_NONE, + mount_operation, + cancellable, + thunar_device_mount_eject_finish, + operation); + } + else if (g_mount_can_unmount (mount)) + { +#ifdef HAVE_LIBNOTIFY + thunar_notify_unmount (mount); +#endif + + /* try unmounting the mount */ + operation = thunar_device_operation_new (device, callback, user_data); + g_mount_unmount_with_operation (mount, + G_MOUNT_UNMOUNT_NONE, + mount_operation, + cancellable, + thunar_device_mount_unmount_finish, + operation); + } + + g_object_unref (G_OBJECT (mount)); + } +} diff --git a/thunar/thunar-device.h b/thunar/thunar-device.h new file mode 100644 index 0000000..f4de610 --- /dev/null +++ b/thunar/thunar-device.h @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 2012 Nick Schermer <n...@xfce.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU 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 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 General Public License for + * more details. + * + * 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 + */ + +#ifndef __THUNAR_DEVICE_H__ +#define __THUNAR_DEVICE_H__ + +#include <thunar/thunar-file.h> + +G_BEGIN_DECLS + +typedef struct _ThunarDeviceClass ThunarDeviceClass; +typedef struct _ThunarDevice ThunarDevice; +typedef enum _ThunarDeviceKind ThunarDeviceKind; + +typedef void (*ThunarDeviceCallback) (ThunarDevice *device, + const GError *error, + gpointer user_data); + +enum _ThunarDeviceKind +{ + THUNAR_DEVICE_KIND_VOLUME, + THUNAR_DEVICE_KIND_MOUNT_LOCAL, + THUNAR_DEVICE_KIND_MOUNT_REMOTE +}; + +#define THUNAR_TYPE_DEVICE (thunar_device_get_type ()) +#define THUNAR_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_DEVICE, ThunarDevice)) +#define THUNAR_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_DEVICE, ThunarDeviceClass)) +#define THUNAR_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_DEVICE)) +#define THUNAR_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), THUNAR_TYPE_DEVICE)) +#define THUNAR_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_DEVICE, ThunarDeviceClass)) + +GType thunar_device_get_type (void) G_GNUC_CONST; + +gchar *thunar_device_get_name (const ThunarDevice *device) G_GNUC_MALLOC; + +GIcon *thunar_device_get_icon (const ThunarDevice *device); + +ThunarDeviceKind thunar_device_get_kind (const ThunarDevice *device) G_GNUC_PURE; + +gboolean thunar_device_can_eject (const ThunarDevice *device); + +gboolean thunar_device_can_mount (const ThunarDevice *device); + +gboolean thunar_device_can_unmount (const ThunarDevice *device); + +gboolean thunar_device_is_mounted (const ThunarDevice *device); + +GFile *thunar_device_get_root (const ThunarDevice *device); + +const gchar *thunar_device_get_sort_key (const ThunarDevice *device); + +void thunar_device_mount (ThunarDevice *device, + GMountOperation *mount_operation, + GCancellable *cancellable, + ThunarDeviceCallback callback, + gpointer user_data); + +void thunar_device_unmount (ThunarDevice *device, + GMountOperation *mount_operation, + GCancellable *cancellable, + ThunarDeviceCallback callback, + gpointer user_data); + +void thunar_device_eject (ThunarDevice *device, + GMountOperation *mount_operation, + GCancellable *cancellable, + ThunarDeviceCallback callback, + gpointer user_data); + +G_END_DECLS + +#endif /* !__THUNAR_DEVICE_H__ */ _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org https://mail.xfce.org/mailman/listinfo/xfce4-commits