From 7fde1d8c46badad36c25d2444bd0aba760169b38 Mon Sep 17 00:00:00 2001 From: Cosimo Cecchi Date: Mon, 14 May 2012 18:33:06 -0400 Subject: [PATCH] autorun: add a notification when unmounting drives Initial patch by Adel Gadllah 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