Blob Blame History Raw
From cf9d3778748c701d115c9a1c213a13e16b50c4c5 Mon Sep 17 00:00:00 2001
From: Cosimo Cecchi <cosimoc@gnome.org>
Date: Tue, 15 May 2012 16:12:37 -0400
Subject: [PATCH] places-sidebar: add a notification while ejecting volumes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This is a workarond solution until we come up with something better
designed for GNOME 3.6 (by that time we'll also have a proper API in GIO
for this, see [1].

Based on an initial patch by Tomáš Bžatek <tbzatek@redhat.com>

[1] https://bugzilla.gnome.org/show_bug.cgi?id=676111

https://bugzilla.redhat.com/show_bug.cgi?id=819492
https://bugzilla.gnome.org/show_bug.cgi?id=619665
---
 src/nautilus-places-sidebar.c |  194 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 172 insertions(+), 22 deletions(-)

diff --git a/src/nautilus-places-sidebar.c b/src/nautilus-places-sidebar.c
index 26dd93e..ddcaeae 100644
--- a/src/nautilus-places-sidebar.c
+++ b/src/nautilus-places-sidebar.c
@@ -53,6 +53,8 @@
 #include "nautilus-window.h"
 #include "nautilus-window-slot.h"
 
+#include <libnotify/notify.h>
+
 #define DEBUG_FLAG NAUTILUS_DEBUG_PLACES
 #include <libnautilus-private/nautilus-debug.h>
 
@@ -2128,22 +2130,107 @@ unmount_shortcut_cb (GtkMenuItem           *item,
 	do_unmount_selection (sidebar);
 }
 
+typedef struct {
+	NautilusWindow *window;
+	GMountOperation *mount_op;
+	guint timeout_id;
+	NotifyNotification *notify;
+	gchar *device_name;
+	gboolean should_display;
+} EjectOpData;
+
+static void
+eject_notification_pop_down (EjectOpData *data)
+{
+	if (data->timeout_id > 0) {
+		g_source_remove (data->timeout_id);
+		data->timeout_id = 0;
+	}
+
+	if (data->notify != NULL) {
+		notify_notification_close (data->notify, NULL);
+		g_clear_object (&data->notify);
+	}
+}
+
+static void
+free_eject_op_data (EjectOpData *data)
+{
+	gchar *header;
+	NotifyNotification *unplug;
+
+	if (data->timeout_id > 0)
+		g_source_remove (data->timeout_id);
+
+	if (data->notify != NULL) {
+		eject_notification_pop_down (data);
+
+		if (data->should_display) {
+			/* Notification was shown, let's update it we're finished */
+			header = g_strdup_printf (_("You can now unplug %s"), data->device_name);
+			unplug = notify_notification_new (header, NULL, "media-removable");
+
+			notify_notification_show (unplug, NULL);
+			g_object_unref (unplug);
+		}
+	}
+
+	g_free (data->device_name);
+	g_clear_object (&data->window);
+	g_object_unref (data->mount_op);
+
+	g_slice_free (EjectOpData, data);
+}
+
+static void
+eject_notification_pop_up (EjectOpData *data)
+{
+	gchar *header;
+
+	if (!data->should_display) {
+		return;
+	}
+
+	header = g_strdup_printf (_("Writing data to %s"), data->device_name);
+	data->notify = notify_notification_new (header, _("Don't unplug until finished"),
+						"media-removable");
+	g_free (header);
+
+	notify_notification_set_hint (data->notify, "transient", g_variant_new_boolean (TRUE));
+	notify_notification_set_urgency (data->notify, NOTIFY_URGENCY_CRITICAL);
+
+	notify_notification_show (data->notify, NULL);
+}
+
+static void
+eject_mount_op_reply (GMountOperation       *op,
+		      GMountOperationResult  result,
+		      gpointer               user_data)
+{
+	EjectOpData *data = user_data;
+	gint choice;
+
+	g_object_get (op, "choice", &choice, NULL);
+	if (choice == 0) {
+		eject_notification_pop_up (data);
+	}
+}
+
 static void
 drive_eject_cb (GObject *source_object,
 		GAsyncResult *res,
 		gpointer user_data)
 {
-	NautilusWindow *window;
+	EjectOpData *data = user_data;
 	GError *error;
 	char *primary;
 	char *name;
 
-	window = user_data;
-	g_object_unref (window);
-
 	error = NULL;
 	if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error)) {
 		if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+			eject_notification_pop_down (data);
+
 			name = g_drive_get_name (G_DRIVE (source_object));
 			primary = g_strdup_printf (_("Unable to eject %s"), name);
 			g_free (name);
@@ -2154,6 +2241,8 @@ drive_eject_cb (GObject *source_object,
 		}
 		g_error_free (error);
 	}
+
+	free_eject_op_data (data);
 }
 
 static void
@@ -2161,17 +2250,16 @@ volume_eject_cb (GObject *source_object,
 		GAsyncResult *res,
 		gpointer user_data)
 {
-	NautilusWindow *window;
+	EjectOpData *data = user_data;
 	GError *error;
 	char *primary;
 	char *name;
 
-	window = user_data;
-	g_object_unref (window);
-
 	error = NULL;
 	if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error)) {
 		if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+			eject_notification_pop_down (data);
+
 			name = g_volume_get_name (G_VOLUME (source_object));
 			primary = g_strdup_printf (_("Unable to eject %s"), name);
 			g_free (name);
@@ -2182,6 +2270,8 @@ volume_eject_cb (GObject *source_object,
 		}
 		g_error_free (error);
 	}
+
+	free_eject_op_data (data);
 }
 
 static void
@@ -2189,17 +2279,16 @@ mount_eject_cb (GObject *source_object,
 		GAsyncResult *res,
 		gpointer user_data)
 {
-	NautilusWindow *window;
+	EjectOpData *data = user_data;
 	GError *error;
 	char *primary;
 	char *name;
 
-	window = user_data;
-	g_object_unref (window);
-
 	error = NULL;
 	if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error)) {
 		if (error->code != G_IO_ERROR_FAILED_HANDLED) {
+			eject_notification_pop_down (data);
+
 			name = g_mount_get_name (G_MOUNT (source_object));
 			primary = g_strdup_printf (_("Unable to eject %s"), name);
 			g_free (name);
@@ -2210,6 +2299,51 @@ mount_eject_cb (GObject *source_object,
 		}
 		g_error_free (error);
 	}
+
+	free_eject_op_data (data);
+}
+
+static gboolean
+eject_timeout (gpointer user_data)
+{
+	EjectOpData *data = user_data;
+
+	data->timeout_id = 0;
+	eject_notification_pop_up (data);
+
+	return FALSE;
+}
+
+static gboolean
+eject_notification_should_display (GMount *mount,
+				   GVolume *volume,
+				   GDrive *drive)
+{
+	gchar *device_id = NULL;
+	gboolean retval = TRUE;
+
+	if (mount != NULL) {
+		volume = g_mount_get_volume (mount);
+	} else if (volume != NULL) {
+		volume = g_object_ref (volume);
+	}
+
+	if (volume != NULL) {
+		device_id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+	} else if (drive != NULL) {
+		device_id = g_drive_get_identifier (drive, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+	}
+
+	if (device_id != NULL) {
+		retval = !g_str_has_prefix (device_id, "/dev/sr");
+		g_free (device_id);
+	}
+
+	if (volume != NULL) {
+		g_object_unref (volume);
+	}
+
+	return retval;
 }
 
 static void
@@ -2218,20 +2352,36 @@ do_eject (GMount *mount,
 	  GDrive *drive,
 	  NautilusPlacesSidebar *sidebar)
 {
-	GMountOperation *mount_op;
+	EjectOpData *data;
+
+	if (mount == NULL && drive == NULL && volume == NULL) {
+		return;
+	}
+
+	data = g_slice_new0 (EjectOpData);
+	data->window = g_object_ref (sidebar->window);
+	data->mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+	data->should_display = eject_notification_should_display (mount, volume, drive);
 
-	mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
 	if (mount != NULL) {
-		g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb,
-					      g_object_ref (sidebar->window));
+		data->device_name = g_mount_get_name (mount);
+		g_mount_eject_with_operation (mount, 0, data->mount_op, NULL, mount_eject_cb, data);
 	} else if (volume != NULL) {
-		g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb,
-					      g_object_ref (sidebar->window));
+		data->device_name = g_volume_get_name (volume);
+		g_volume_eject_with_operation (volume, 0, data->mount_op, NULL, volume_eject_cb, data);
 	} else if (drive != NULL) {
-		g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb,
-					      g_object_ref (sidebar->window));
-	}
-	g_object_unref (mount_op);
+		data->device_name = g_drive_get_name (drive);
+		g_drive_eject_with_operation (drive, 0, data->mount_op, NULL, drive_eject_cb, data);
+	}
+
+	/* Display a notification of ongoing operation after a timeout */
+	data->timeout_id = g_timeout_add_seconds (1, eject_timeout, data);
+	g_signal_connect_swapped (data->mount_op, "show-processes",
+				  G_CALLBACK (eject_notification_pop_down), data);
+	g_signal_connect_swapped (data->mount_op, "aborted",
+				  G_CALLBACK (eject_notification_pop_down), data);
+	g_signal_connect (data->mount_op, "reply",
+			  G_CALLBACK (eject_mount_op_reply), data);
 }
 
 static void
-- 
1.7.10.2