#
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
// Use __ () and N__() for the extension gettext domain, and reuse
|
||||
// the shell domain with the default _() and N_()
|
||||
const Gettext = imports.gettext.domain('dashtodock');
|
||||
const __ = Gettext.gettext;
|
||||
const N__ = function(e) { return e };
|
||||
|
||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||
const Utils = Me.imports.utils;
|
||||
|
||||
/**
|
||||
* This class maintains a Shell.App representing the Trash and keeps it
|
||||
* up-to-date as the trash fills and is emptied over time.
|
||||
*/
|
||||
var Trash = class DashToDock_Trash {
|
||||
|
||||
constructor() {
|
||||
this._file = Gio.file_new_for_uri('trash://');
|
||||
try {
|
||||
this._monitor = this._file.monitor_directory(0, null);
|
||||
this._signalId = this._monitor.connect(
|
||||
'changed',
|
||||
this._onTrashChange.bind(this)
|
||||
);
|
||||
} catch (e) {
|
||||
log(`Impossible to monitor trash: ${e}`)
|
||||
}
|
||||
this._lastEmpty = true;
|
||||
this._empty = true;
|
||||
this._onTrashChange();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._monitor) {
|
||||
this._monitor.disconnect(this._signalId);
|
||||
this._monitor.run_dispose();
|
||||
}
|
||||
this._file.run_dispose();
|
||||
}
|
||||
|
||||
_onTrashChange() {
|
||||
try {
|
||||
let children = this._file.enumerate_children('*', 0, null);
|
||||
this._empty = children.next_file(null) == null;
|
||||
children.close(null);
|
||||
} catch (e) {
|
||||
log(`Impossible to enumerate trash children: ${e}`)
|
||||
return;
|
||||
}
|
||||
|
||||
this._ensureApp();
|
||||
}
|
||||
|
||||
_ensureApp() {
|
||||
if (this._trashApp == null ||
|
||||
this._lastEmpty != this._empty) {
|
||||
let trashKeys = new GLib.KeyFile();
|
||||
trashKeys.set_string('Desktop Entry', 'Name', __('Trash'));
|
||||
trashKeys.set_string('Desktop Entry', 'Icon',
|
||||
this._empty ? 'user-trash' : 'user-trash-full');
|
||||
trashKeys.set_string('Desktop Entry', 'Type', 'Application');
|
||||
trashKeys.set_string('Desktop Entry', 'Exec', 'gio open trash:///');
|
||||
trashKeys.set_string('Desktop Entry', 'StartupNotify', 'false');
|
||||
trashKeys.set_string('Desktop Entry', 'XdtdUri', 'trash:///');
|
||||
if (!this._empty) {
|
||||
trashKeys.set_string('Desktop Entry', 'Actions', 'empty-trash;');
|
||||
trashKeys.set_string('Desktop Action empty-trash', 'Name', __('Empty Trash'));
|
||||
trashKeys.set_string('Desktop Action empty-trash', 'Exec',
|
||||
'dbus-send --print-reply --dest=org.gnome.Nautilus /org/gnome/Nautilus org.gnome.Nautilus.FileOperations.EmptyTrash');
|
||||
}
|
||||
|
||||
let trashAppInfo = Gio.DesktopAppInfo.new_from_keyfile(trashKeys);
|
||||
this._trashApp = new Shell.App({appInfo: trashAppInfo});
|
||||
this._lastEmpty = this._empty;
|
||||
|
||||
this.emit('changed');
|
||||
}
|
||||
}
|
||||
|
||||
getApp() {
|
||||
this._ensureApp();
|
||||
return this._trashApp;
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(Trash.prototype);
|
||||
|
||||
/**
|
||||
* This class maintains Shell.App representations for removable devices
|
||||
* plugged into the system, and keeps the list of Apps up-to-date as
|
||||
* devices come and go and are mounted and unmounted.
|
||||
*/
|
||||
var Removables = class DashToDock_Removables {
|
||||
|
||||
constructor() {
|
||||
this._signalsHandler = new Utils.GlobalSignalsHandler();
|
||||
|
||||
this._monitor = Gio.VolumeMonitor.get();
|
||||
this._volumeApps = []
|
||||
this._mountApps = []
|
||||
|
||||
this._monitor.get_volumes().forEach(
|
||||
(volume) => {
|
||||
this._onVolumeAdded(this._monitor, volume);
|
||||
}
|
||||
);
|
||||
|
||||
this._monitor.get_mounts().forEach(
|
||||
(mount) => {
|
||||
this._onMountAdded(this._monitor, mount);
|
||||
}
|
||||
);
|
||||
|
||||
this._signalsHandler.add([
|
||||
this._monitor,
|
||||
'mount-added',
|
||||
this._onMountAdded.bind(this)
|
||||
], [
|
||||
this._monitor,
|
||||
'mount-removed',
|
||||
this._onMountRemoved.bind(this)
|
||||
], [
|
||||
this._monitor,
|
||||
'volume-added',
|
||||
this._onVolumeAdded.bind(this)
|
||||
], [
|
||||
this._monitor,
|
||||
'volume-removed',
|
||||
this._onVolumeRemoved.bind(this)
|
||||
]);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._signalsHandler.destroy();
|
||||
this._monitor.run_dispose();
|
||||
}
|
||||
|
||||
_getWorkingIconName(icon) {
|
||||
if (icon instanceof Gio.ThemedIcon) {
|
||||
let iconTheme = Gtk.IconTheme.get_default();
|
||||
let names = icon.get_names();
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
let iconName = names[i];
|
||||
if (iconTheme.has_icon(iconName)) {
|
||||
return iconName;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
} else {
|
||||
return icon.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
_onVolumeAdded(monitor, volume) {
|
||||
if (!volume.can_mount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let activationRoot = volume.get_activation_root();
|
||||
if (!activationRoot) {
|
||||
// Can't offer to mount a device if we don't know
|
||||
// where to mount it.
|
||||
// These devices are usually ejectable so you
|
||||
// don't normally unmount them anyway.
|
||||
return;
|
||||
}
|
||||
let uri = GLib.uri_unescape_string(activationRoot.get_uri(), null);
|
||||
|
||||
let volumeKeys = new GLib.KeyFile();
|
||||
volumeKeys.set_string('Desktop Entry', 'Name', volume.get_name());
|
||||
volumeKeys.set_string('Desktop Entry', 'Icon', this._getWorkingIconName(volume.get_icon()));
|
||||
volumeKeys.set_string('Desktop Entry', 'Type', 'Application');
|
||||
volumeKeys.set_string('Desktop Entry', 'Exec', 'nautilus "' + uri + '"');
|
||||
volumeKeys.set_string('Desktop Entry', 'StartupNotify', 'false');
|
||||
volumeKeys.set_string('Desktop Entry', 'Actions', 'mount;');
|
||||
volumeKeys.set_string('Desktop Action mount', 'Name', __('Mount'));
|
||||
volumeKeys.set_string('Desktop Action mount', 'Exec', 'gio mount "' + uri + '"');
|
||||
let volumeAppInfo = Gio.DesktopAppInfo.new_from_keyfile(volumeKeys);
|
||||
let volumeApp = new Shell.App({appInfo: volumeAppInfo});
|
||||
this._volumeApps.push(volumeApp);
|
||||
this.emit('changed');
|
||||
}
|
||||
|
||||
_onVolumeRemoved(monitor, volume) {
|
||||
for (let i = 0; i < this._volumeApps.length; i++) {
|
||||
let app = this._volumeApps[i];
|
||||
if (app.get_name() == volume.get_name()) {
|
||||
this._volumeApps.splice(i, 1);
|
||||
}
|
||||
}
|
||||
this.emit('changed');
|
||||
}
|
||||
|
||||
_onMountAdded(monitor, mount) {
|
||||
// Filter out uninteresting mounts
|
||||
if (!mount.can_eject() && !mount.can_unmount())
|
||||
return;
|
||||
if (mount.is_shadowed())
|
||||
return;
|
||||
|
||||
let volume = mount.get_volume();
|
||||
if (!volume || volume.get_identifier('class') == 'network') {
|
||||
return;
|
||||
}
|
||||
|
||||
let escapedUri = mount.get_root().get_uri()
|
||||
let uri = GLib.uri_unescape_string(escapedUri, null);
|
||||
|
||||
let mountKeys = new GLib.KeyFile();
|
||||
mountKeys.set_string('Desktop Entry', 'Name', mount.get_name());
|
||||
mountKeys.set_string('Desktop Entry', 'Icon',
|
||||
this._getWorkingIconName(volume.get_icon()));
|
||||
mountKeys.set_string('Desktop Entry', 'Type', 'Application');
|
||||
mountKeys.set_string('Desktop Entry', 'Exec', 'gio open "' + uri + '"');
|
||||
mountKeys.set_string('Desktop Entry', 'StartupNotify', 'false');
|
||||
mountKeys.set_string('Desktop Entry', 'XdtdUri', escapedUri);
|
||||
mountKeys.set_string('Desktop Entry', 'Actions', 'unmount;');
|
||||
if (mount.can_eject()) {
|
||||
mountKeys.set_string('Desktop Action unmount', 'Name', __('Eject'));
|
||||
mountKeys.set_string('Desktop Action unmount', 'Exec',
|
||||
'gio mount -e "' + uri + '"');
|
||||
} else {
|
||||
mountKeys.set_string('Desktop Entry', 'Actions', 'unmount;');
|
||||
mountKeys.set_string('Desktop Action unmount', 'Name', __('Unmount'));
|
||||
mountKeys.set_string('Desktop Action unmount', 'Exec',
|
||||
'gio mount -u "' + uri + '"');
|
||||
}
|
||||
let mountAppInfo = Gio.DesktopAppInfo.new_from_keyfile(mountKeys);
|
||||
let mountApp = new Shell.App({appInfo: mountAppInfo});
|
||||
this._mountApps.push(mountApp);
|
||||
this.emit('changed');
|
||||
}
|
||||
|
||||
_onMountRemoved(monitor, mount) {
|
||||
for (let i = 0; i < this._mountApps.length; i++) {
|
||||
let app = this._mountApps[i];
|
||||
if (app.get_name() == mount.get_name()) {
|
||||
this._mountApps.splice(i, 1);
|
||||
}
|
||||
}
|
||||
this.emit('changed');
|
||||
}
|
||||
|
||||
getApps() {
|
||||
// When we have both a volume app and a mount app, we prefer
|
||||
// the mount app.
|
||||
let apps = new Map();
|
||||
this._volumeApps.map(function(app) {
|
||||
apps.set(app.get_name(), app);
|
||||
});
|
||||
this._mountApps.map(function(app) {
|
||||
apps.set(app.get_name(), app);
|
||||
});
|
||||
|
||||
let ret = [];
|
||||
for (let app of apps.values()) {
|
||||
ret.push(app);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Signals.addSignalMethods(Removables.prototype);
|
Reference in New Issue
Block a user