Blob Blame History Raw
From 7fde1d8c46badad36c25d2444bd0aba760169b38 Mon Sep 17 00:00:00 2001
From: Cosimo Cecchi <cosimoc@gnome.org>
Date: Mon, 14 May 2012 18:33:06 -0400
Subject: [PATCH] autorun: add a notification when unmounting drives

Initial patch by Adel Gadllah <adel.gadllah@gmail.com>

https://bugzilla.redhat.com/show_bug.cgi?id=819492

https://bugzilla.gnome.org/show_bug.cgi?id=676125
---
 js/ui/autorunManager.js |  119 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 111 insertions(+), 8 deletions(-)

diff --git a/js/ui/autorunManager.js b/js/ui/autorunManager.js
index 27d89e8..f62dbcb 100644
--- a/js/ui/autorunManager.js
+++ b/js/ui/autorunManager.js
@@ -193,6 +193,8 @@ const AutorunManager = new Lang.Class({
 
     ejectMount: function(mount) {
         let mountOp = new ShellMountOperation.ShellMountOperation(mount);
+        let unmountNotifier = new UnmountNotifier(mountOp.mountOp, mount);
+        let didEject = true;
 
         // first, see if we have a drive
         let drive = mount.get_drive();
@@ -202,27 +204,34 @@ const AutorunManager = new Lang.Class({
             drive.get_start_stop_type() == Gio.DriveStartStopType.SHUTDOWN &&
             drive.can_stop()) {
             drive.stop(0, mountOp.mountOp, null,
-                       Lang.bind(this, this._onStop));
+                       Lang.bind(this, this._onStop, unmountNotifier));
         } else {
             if (mount.can_eject()) {
                 mount.eject_with_operation(0, mountOp.mountOp, null,
-                                           Lang.bind(this, this._onEject));
+                                           Lang.bind(this, this._onEject, unmountNotifier));
             } else if (volume && volume.can_eject()) {
                 volume.eject_with_operation(0, mountOp.mountOp, null,
-                                            Lang.bind(this, this._onEject));
+                                            Lang.bind(this, this._onEject, unmountNotifier));
             } else if (drive && drive.can_eject()) {
                 drive.eject_with_operation(0, mountOp.mountOp, null,
-                                           Lang.bind(this, this._onEject));
+                                           Lang.bind(this, this._onEject, unmountNotifier));
             } else if (mount.can_unmount()) {
                 mount.unmount_with_operation(0, mountOp.mountOp, null,
-                                             Lang.bind(this, this._onUnmount));
+                                             Lang.bind(this, this._onUnmount, unmountNotifier));
+            } else {
+                didEject = false;
             }
         }
+
+        if (didEject)
+            unmountNotifier.show();
     },
 
-    _onUnmount: function(mount, res) {
+    _onUnmount: function(mount, res, notifier) {
+        let success = false;
         try {
             mount.unmount_with_operation_finish(res);
+            success = true;
         } catch (e) {
             // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
             // but we can't access the error code from JS.
@@ -230,11 +239,15 @@ const AutorunManager = new Lang.Class({
             log('Unable to eject the mount ' + mount.get_name() 
                 + ': ' + e.toString());
         }
+
+        notifier.done(success);
     },
 
-    _onEject: function(source, res) {
+    _onEject: function(source, res, notifier) {
+        let success = false;
         try {
             source.eject_with_operation_finish(res);
+            success = true;
         } catch (e) {
             // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
             // but we can't access the error code from JS.
@@ -242,11 +255,15 @@ const AutorunManager = new Lang.Class({
             log('Unable to eject the drive ' + source.get_name() 
                 + ': ' + e.toString());
         }
+
+        notifier.done(success);
     },
 
-    _onStop: function(drive, res) {
+    _onStop: function(drive, res, notifier) {
+        let success = false;
         try {
             drive.stop_finish(res);
+            success = true;
         } catch (e) {
             // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
             // but we can't access the error code from JS.
@@ -254,7 +271,93 @@ const AutorunManager = new Lang.Class({
             log('Unable to stop the drive ' + drive.get_name() 
                 + ': ' + e.toString());
         }
+
+        notifier.done(success);
+    }
+});
+
+const UnmountNotifier = new Lang.Class({
+    Name: 'UnmountNotifier',
+    Extends: MessageTray.Source,
+
+    _init: function(mountOperation, mount) {
+        this.parent('');
+
+        this._notification = null;
+        this._shouldNotify = this._shouldNotifyForMount(mount);
+        if (!this._shouldNotify)
+            return;
+
+        this._mountName = mount.get_name();
+        mountOperation.connect('show-processes-2', Lang.bind(this,
+            function() {
+                this.done(false);
+            }));
+        mountOperation.connect('reply', Lang.bind(this,
+            function(mountOp, res) {
+                // "Unmount Anyway" choice
+                if (mountOp.choice == 0)
+                    this.show();
+            }));
+        mountOperation.connect('aborted', Lang.bind(this,
+            function() {
+                this.done(false);
+            }));
+
+        Main.messageTray.add(this);
+    },
+
+    _shouldNotifyForMount: function(mount) {
+        let deviceId = null;
+        let volume = mount.get_volume();
+        if (volume)
+            deviceId = volume.get_identifier(Gio.VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+
+        // if the unix device id matches /dev/sr* (optical media), don't notify
+        if (deviceId != null)
+            return (deviceId.match(new RegExp('/dev/sr[0-9]+', 'g')) == null);
+        else
+            return true;
     },
+
+    show: function() {
+        if (!this._shouldNotify)
+            return;
+
+        let header = _("Writing data to %s").format(this._mountName);
+        let text = _("Don't unplug until finished");
+
+        if (!this._notification) {
+            this._notification = new MessageTray.Notification(this, header, text);
+        } else {
+            this._notification.update(header, text);
+        }
+
+        this._notification.setTransient(true);
+        this._notification.setUrgency(MessageTray.Urgency.CRITICAL);
+        this.notify(this._notification);
+    },
+
+    done: function(success) {
+        if (this._notification) {
+            this._notification.destroy();
+            this._notification = null;
+        }
+
+        if (success && this._shouldNotify) {
+            let header = _("You can now unplug %s").format(this._mountName);
+            let notification = new MessageTray.Notification(this, header, null);
+            notification.setTransient(true);
+
+            this.notify(notification);
+        }
+    },
+
+    createNotificationIcon: function() {
+        return new St.Icon ({ icon_name: 'media-removable',
+                              icon_type: St.IconType.FULLCOLOR,
+                              icon_size: this.ICON_SIZE });
+    }
 });
 
 const AutorunResidentSource = new Lang.Class({
-- 
1.7.10.2