That module was removed from GNOME Shell 3.6, so reimplement for mounts only, borrowing code and behaviour from the resident message tray source.
187 lines
4.9 KiB
JavaScript
187 lines
4.9 KiB
JavaScript
// Drive menu extension
|
|
const Gio = imports.gi.Gio;
|
|
const Lang = imports.lang;
|
|
const St = imports.gi.St;
|
|
const Shell = imports.gi.Shell;
|
|
|
|
const Gettext = imports.gettext.domain('gnome-shell-extensions');
|
|
const _ = Gettext.gettext;
|
|
|
|
const Main = imports.ui.main;
|
|
const Panel = imports.ui.panel;
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
|
|
|
const ExtensionUtils = imports.misc.extensionUtils;
|
|
const Me = ExtensionUtils.getCurrentExtension();
|
|
const Convenience = Me.imports.convenience;
|
|
|
|
const MountMenuItem = new Lang.Class({
|
|
Name: 'DriveMenu.MountMenuItem',
|
|
Extends: PopupMenu.PopupBaseMenuItem,
|
|
|
|
_init: function(mount) {
|
|
this.parent();
|
|
|
|
this.label = new St.Label({ text: mount.get_name() });
|
|
this.addActor(this.label);
|
|
this.actor.label_actor = this.label;
|
|
|
|
this.mount = mount;
|
|
|
|
let ejectIcon = new St.Icon({ icon_name: 'media-eject-symbolic',
|
|
style_class: 'popup-menu-icon ' });
|
|
let ejectButton = new St.Button({ child: ejectIcon });
|
|
ejectButton.connect('clicked', Lang.bind(this, this._eject));
|
|
this.addActor(ejectButton);
|
|
},
|
|
|
|
_eject: function() {
|
|
let mountOp = new ShellMountOperation.ShellMountOperation(this.mount);
|
|
|
|
if (this.mount.can_eject())
|
|
this.mount.eject_with_operation(Gio.MountUnmountFlags.NONE,
|
|
mountOp.mountOp,
|
|
null, // Gio.Cancellable
|
|
Lang.bind(this, this._ejectFinish));
|
|
else
|
|
this.mount.unmount_with_operation(Gio.MountUnmountFlags.NONE,
|
|
mountOp.mountOp,
|
|
null, // Gio.Cancellable
|
|
Lang.bind(this, this._unmountFinish));
|
|
},
|
|
|
|
_unmountFinish: function(mount, result) {
|
|
try {
|
|
mount.unmount_with_operation_finish(result);
|
|
} catch(e) {
|
|
this._reportFailure(e);
|
|
}
|
|
},
|
|
|
|
_ejectFinish: function(mount, result) {
|
|
try {
|
|
mount.eject_with_operation_finish(result);
|
|
} catch(e) {
|
|
this._reportFailure(e);
|
|
}
|
|
},
|
|
|
|
_reportFailure: function(exception) {
|
|
let msg = _("Ejecting drive '%s' failed:").format(this.mount.get_name());
|
|
Main.notifyError(msg, exception.message);
|
|
},
|
|
|
|
activate: function(event) {
|
|
Gio.AppInfo.launch_default_for_uri(this.mount.get_root().get_uri(),
|
|
global.create_app_launch_context());
|
|
|
|
this.parent(event);
|
|
}
|
|
});
|
|
|
|
const DriveMenu = new Lang.Class({
|
|
Name: 'DriveMenu.DriveMenu',
|
|
Extends: PanelMenu.SystemStatusButton,
|
|
|
|
_init: function() {
|
|
this.parent('media-eject-symbolic', _("Removable devices"));
|
|
|
|
this._monitor = Gio.VolumeMonitor.get();
|
|
this._addedId = this._monitor.connect('mount-added', Lang.bind(this, function(monitor, mount) {
|
|
this._addMount(mount);
|
|
this._updateMenuVisibility();
|
|
}));
|
|
this._removedId = this._monitor.connect('mount-removed', Lang.bind(this, function(monitor, mount) {
|
|
this._removeMount(mount);
|
|
this._updateMenuVisibility();
|
|
}));
|
|
|
|
this._mounts = [ ];
|
|
|
|
this._monitor.get_mounts().forEach(Lang.bind(this, this._addMount));
|
|
|
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
this.menu.addAction(_("Open File"), function(event) {
|
|
let appSystem = Shell.AppSystem.get_default();
|
|
let app = appSystem.lookup_app('nautilus.desktop');
|
|
app.activate_full(-1, event.get_time());
|
|
});
|
|
|
|
this._updateMenuVisibility();
|
|
},
|
|
|
|
_updateMenuVisibility: function() {
|
|
if (this._mounts.length > 0)
|
|
this.actor.show();
|
|
else
|
|
this.actor.hide();
|
|
},
|
|
|
|
_isMountInteresting: function(mount) {
|
|
if (!mount.can_eject() && !mount.can_unmount())
|
|
return false;
|
|
|
|
let volume = mount.get_volume();
|
|
|
|
if (volume == null) {
|
|
// probably a GDaemonMount, could be network or
|
|
// local, but we can't tell; assume it's local for now
|
|
return true;
|
|
}
|
|
|
|
return volume.get_identifier('class') != 'network';
|
|
},
|
|
|
|
_addMount: function(mount) {
|
|
if (!this._isMountInteresting(mount))
|
|
return;
|
|
|
|
let item = new MountMenuItem(mount);
|
|
this._mounts.unshift(item);
|
|
this.menu.addMenuItem(item, 0);
|
|
},
|
|
|
|
_removeMount: function(mount) {
|
|
if (!this._isMountInteresting(mount))
|
|
return;
|
|
|
|
for (let i = 0; i < this._mounts.length; i++) {
|
|
let item = this._mounts[i];
|
|
if (item.mount == mount) {
|
|
item.destroy();
|
|
this._mounts.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
log ('Removing a mount that was never added to the menu');
|
|
},
|
|
|
|
destroy: function() {
|
|
if (this._connectedId) {
|
|
this._monitor.disconnect(this._connectedId);
|
|
this._monitor.disconnect(this._disconnectedId);
|
|
this._connectedId = 0;
|
|
this._disconnectedId = 0;
|
|
}
|
|
|
|
this.parent();
|
|
},
|
|
});
|
|
|
|
function init() {
|
|
Convenience.initTranslations();
|
|
}
|
|
|
|
let _indicator;
|
|
|
|
function enable() {
|
|
_indicator = new DriveMenu;
|
|
Main.panel.addToStatusArea('drive-menu', _indicator);
|
|
}
|
|
|
|
function disable() {
|
|
_indicator.destroy();
|
|
}
|