|
Packit |
d345d1 |
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
const { Gio, St } = imports.gi;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
const GnomeSession = imports.misc.gnomeSession;
|
|
Packit |
d345d1 |
const Main = imports.ui.main;
|
|
Packit |
d345d1 |
const MessageTray = imports.ui.messageTray;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// GSettings keys
|
|
Packit |
d345d1 |
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
|
|
Packit |
d345d1 |
const SETTING_DISABLE_AUTORUN = 'autorun-never';
|
|
Packit |
d345d1 |
const SETTING_START_APP = 'autorun-x-content-start-app';
|
|
Packit |
d345d1 |
const SETTING_IGNORE = 'autorun-x-content-ignore';
|
|
Packit |
d345d1 |
const SETTING_OPEN_FOLDER = 'autorun-x-content-open-folder';
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var AutorunSetting = {
|
|
Packit |
d345d1 |
RUN: 0,
|
|
Packit |
d345d1 |
IGNORE: 1,
|
|
Packit |
d345d1 |
FILES: 2,
|
|
Packit |
d345d1 |
ASK: 3
|
|
Packit |
d345d1 |
};
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// misc utils
|
|
Packit |
d345d1 |
function shouldAutorunMount(mount) {
|
|
Packit |
d345d1 |
let root = mount.get_root();
|
|
Packit |
d345d1 |
let volume = mount.get_volume();
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (!volume || !volume.allowAutorun)
|
|
Packit |
d345d1 |
return false;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (root.is_native() && isMountRootHidden(root))
|
|
Packit |
d345d1 |
return false;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return true;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
function isMountRootHidden(root) {
|
|
Packit |
d345d1 |
let path = root.get_path();
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// skip any mounts in hidden directory hierarchies
|
|
Packit |
d345d1 |
return (path.indexOf('/.') != -1);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
function isMountNonLocal(mount) {
|
|
Packit |
d345d1 |
// If the mount doesn't have an associated volume, that means it's
|
|
Packit |
d345d1 |
// an uninteresting filesystem. Most devices that we care about will
|
|
Packit |
d345d1 |
// have a mount, like media players and USB sticks.
|
|
Packit |
d345d1 |
let volume = mount.get_volume();
|
|
Packit |
d345d1 |
if (volume == null)
|
|
Packit |
d345d1 |
return true;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return (volume.get_identifier("class") == "network");
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
function startAppForMount(app, mount) {
|
|
Packit |
d345d1 |
let files = [];
|
|
Packit |
d345d1 |
let root = mount.get_root();
|
|
Packit |
d345d1 |
let retval = false;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
files.push(root);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
try {
|
|
Packit |
d345d1 |
retval = app.launch(files,
|
|
Packit |
d345d1 |
global.create_app_launch_context(0, -1));
|
|
Packit |
d345d1 |
} catch (e) {
|
|
Packit |
d345d1 |
log('Unable to launch the application ' + app.get_name()
|
|
Packit |
d345d1 |
+ ': ' + e.toString());
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return retval;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
/******************************************/
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
const HotplugSnifferIface = loadInterfaceXML('org.gnome.Shell.HotplugSniffer');
|
|
Packit |
d345d1 |
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
|
|
Packit |
d345d1 |
function HotplugSniffer() {
|
|
Packit |
d345d1 |
return new HotplugSnifferProxy(Gio.DBus.session,
|
|
Packit |
d345d1 |
'org.gnome.Shell.HotplugSniffer',
|
|
Packit |
d345d1 |
'/org/gnome/Shell/HotplugSniffer');
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var ContentTypeDiscoverer = class {
|
|
Packit |
d345d1 |
constructor(callback) {
|
|
Packit |
d345d1 |
this._callback = callback;
|
|
Packit |
d345d1 |
this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
guessContentTypes(mount) {
|
|
Packit |
d345d1 |
let autorunEnabled = !this._settings.get_boolean(SETTING_DISABLE_AUTORUN);
|
|
Packit |
d345d1 |
let shouldScan = autorunEnabled && !isMountNonLocal(mount);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (shouldScan) {
|
|
Packit |
d345d1 |
// guess mount's content types using GIO
|
|
Packit |
d345d1 |
mount.guess_content_type(false, null,
|
|
Packit |
d345d1 |
this._onContentTypeGuessed.bind(this));
|
|
Packit |
d345d1 |
} else {
|
|
Packit |
d345d1 |
this._emitCallback(mount, []);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_onContentTypeGuessed(mount, res) {
|
|
Packit |
d345d1 |
let contentTypes = [];
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
try {
|
|
Packit |
d345d1 |
contentTypes = mount.guess_content_type_finish(res);
|
|
Packit |
d345d1 |
} catch (e) {
|
|
Packit |
d345d1 |
log('Unable to guess content types on added mount ' + mount.get_name()
|
|
Packit |
d345d1 |
+ ': ' + e.toString());
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (contentTypes.length) {
|
|
Packit |
d345d1 |
this._emitCallback(mount, contentTypes);
|
|
Packit |
d345d1 |
} else {
|
|
Packit |
d345d1 |
let root = mount.get_root();
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let hotplugSniffer = new HotplugSniffer();
|
|
Packit |
d345d1 |
hotplugSniffer.SniffURIRemote(root.get_uri(),
|
|
Packit |
d345d1 |
([contentTypes]) => {
|
|
Packit |
d345d1 |
this._emitCallback(mount, contentTypes);
|
|
Packit |
d345d1 |
});
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_emitCallback(mount, contentTypes) {
|
|
Packit |
d345d1 |
if (!contentTypes)
|
|
Packit |
d345d1 |
contentTypes = [];
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// we're not interested in win32 software content types here
|
|
Packit |
d345d1 |
contentTypes = contentTypes.filter(
|
|
Packit |
d345d1 |
type => (type != 'x-content/win32-software')
|
|
Packit |
d345d1 |
);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let apps = [];
|
|
Packit |
d345d1 |
contentTypes.forEach(type => {
|
|
Packit |
d345d1 |
let app = Gio.app_info_get_default_for_type(type, false);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (app)
|
|
Packit |
d345d1 |
apps.push(app);
|
|
Packit |
d345d1 |
});
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (apps.length == 0)
|
|
Packit |
d345d1 |
apps.push(Gio.app_info_get_default_for_type('inode/directory', false));
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
this._callback(mount, apps, contentTypes);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
};
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var AutorunManager = class {
|
|
Packit |
d345d1 |
constructor() {
|
|
Packit |
d345d1 |
this._session = new GnomeSession.SessionManager();
|
|
Packit |
d345d1 |
this._volumeMonitor = Gio.VolumeMonitor.get();
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
this._dispatcher = new AutorunDispatcher(this);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
enable() {
|
|
Packit |
d345d1 |
this._mountAddedId = this._volumeMonitor.connect('mount-added', this._onMountAdded.bind(this));
|
|
Packit |
d345d1 |
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', this._onMountRemoved.bind(this));
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
disable() {
|
|
Packit |
d345d1 |
this._volumeMonitor.disconnect(this._mountAddedId);
|
|
Packit |
d345d1 |
this._volumeMonitor.disconnect(this._mountRemovedId);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_onMountAdded(monitor, mount) {
|
|
Packit |
d345d1 |
// don't do anything if our session is not the currently
|
|
Packit |
d345d1 |
// active one
|
|
Packit |
d345d1 |
if (!this._session.SessionIsActive)
|
|
Packit |
d345d1 |
return;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let discoverer = new ContentTypeDiscoverer((mount, apps, contentTypes) => {
|
|
Packit |
d345d1 |
this._dispatcher.addMount(mount, apps, contentTypes);
|
|
Packit |
d345d1 |
});
|
|
Packit |
d345d1 |
discoverer.guessContentTypes(mount);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_onMountRemoved(monitor, mount) {
|
|
Packit |
d345d1 |
this._dispatcher.removeMount(mount);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
};
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var AutorunDispatcher = class {
|
|
Packit |
d345d1 |
constructor(manager) {
|
|
Packit |
d345d1 |
this._manager = manager;
|
|
Packit |
d345d1 |
this._sources = [];
|
|
Packit |
d345d1 |
this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_getAutorunSettingForType(contentType) {
|
|
Packit |
d345d1 |
let runApp = this._settings.get_strv(SETTING_START_APP);
|
|
Packit |
d345d1 |
if (runApp.indexOf(contentType) != -1)
|
|
Packit |
d345d1 |
return AutorunSetting.RUN;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let ignore = this._settings.get_strv(SETTING_IGNORE);
|
|
Packit |
d345d1 |
if (ignore.indexOf(contentType) != -1)
|
|
Packit |
d345d1 |
return AutorunSetting.IGNORE;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let openFiles = this._settings.get_strv(SETTING_OPEN_FOLDER);
|
|
Packit |
d345d1 |
if (openFiles.indexOf(contentType) != -1)
|
|
Packit |
d345d1 |
return AutorunSetting.FILES;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return AutorunSetting.ASK;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_getSourceForMount(mount) {
|
|
Packit |
d345d1 |
let filtered = this._sources.filter(source => (source.mount == mount));
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// we always make sure not to add two sources for the same
|
|
Packit |
d345d1 |
// mount in addMount(), so it's safe to assume filtered.length
|
|
Packit |
d345d1 |
// is always either 1 or 0.
|
|
Packit |
d345d1 |
if (filtered.length == 1)
|
|
Packit |
d345d1 |
return filtered[0];
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return null;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_addSource(mount, apps) {
|
|
Packit |
d345d1 |
// if we already have a source showing for this
|
|
Packit |
d345d1 |
// mount, return
|
|
Packit |
d345d1 |
if (this._getSourceForMount(mount))
|
|
Packit |
d345d1 |
return;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// add a new source
|
|
Packit |
d345d1 |
this._sources.push(new AutorunSource(this._manager, mount, apps));
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
addMount(mount, apps, contentTypes) {
|
|
Packit |
d345d1 |
// if autorun is disabled globally, return
|
|
Packit |
d345d1 |
if (this._settings.get_boolean(SETTING_DISABLE_AUTORUN))
|
|
Packit |
d345d1 |
return;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// if the mount doesn't want to be autorun, return
|
|
Packit |
d345d1 |
if (!shouldAutorunMount(mount))
|
|
Packit |
d345d1 |
return;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let setting;
|
|
Packit |
d345d1 |
if (contentTypes.length > 0)
|
|
Packit |
d345d1 |
setting = this._getAutorunSettingForType(contentTypes[0]);
|
|
Packit |
d345d1 |
else
|
|
Packit |
d345d1 |
setting = AutorunSetting.ASK;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// check at the settings for the first content type
|
|
Packit |
d345d1 |
// to see whether we should ask
|
|
Packit |
d345d1 |
if (setting == AutorunSetting.IGNORE)
|
|
Packit |
d345d1 |
return; // return right away
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let success = false;
|
|
Packit |
d345d1 |
let app = null;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (setting == AutorunSetting.RUN) {
|
|
Packit |
d345d1 |
app = Gio.app_info_get_default_for_type(contentTypes[0], false);
|
|
Packit |
d345d1 |
} else if (setting == AutorunSetting.FILES) {
|
|
Packit |
d345d1 |
app = Gio.app_info_get_default_for_type('inode/directory', false);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (app)
|
|
Packit |
d345d1 |
success = startAppForMount(app, mount);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// we fallback here also in case the settings did not specify 'ask',
|
|
Packit |
d345d1 |
// but we failed launching the default app or the default file manager
|
|
Packit |
d345d1 |
if (!success)
|
|
Packit |
d345d1 |
this._addSource(mount, apps);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
removeMount(mount) {
|
|
Packit |
d345d1 |
let source = this._getSourceForMount(mount);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// if we aren't tracking this mount, don't do anything
|
|
Packit |
d345d1 |
if (!source)
|
|
Packit |
d345d1 |
return;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// destroy the notification source
|
|
Packit |
d345d1 |
source.destroy();
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
};
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var AutorunSource = class extends MessageTray.Source {
|
|
Packit |
d345d1 |
constructor(manager, mount, apps) {
|
|
Packit |
d345d1 |
super(mount.get_name());
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
this._manager = manager;
|
|
Packit |
d345d1 |
this.mount = mount;
|
|
Packit |
d345d1 |
this.apps = apps;
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
this._notification = new AutorunNotification(this._manager, this);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
// add ourselves as a source, and popup the notification
|
|
Packit |
d345d1 |
Main.messageTray.add(this);
|
|
Packit |
d345d1 |
this.notify(this._notification);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
getIcon() {
|
|
Packit |
d345d1 |
return this.mount.get_icon();
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_createPolicy() {
|
|
Packit |
d345d1 |
return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus');
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
};
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var AutorunNotification = class extends MessageTray.Notification {
|
|
Packit |
d345d1 |
constructor(manager, source) {
|
|
Packit |
d345d1 |
super(source, source.title);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
this._manager = manager;
|
|
Packit |
d345d1 |
this._mount = source.mount;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
createBanner() {
|
|
Packit |
d345d1 |
let banner = new MessageTray.NotificationBanner(this);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
this.source.apps.forEach(app => {
|
|
Packit |
d345d1 |
let actor = this._buttonForApp(app);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
if (actor)
|
|
Packit |
d345d1 |
banner.addButton(actor);
|
|
Packit |
d345d1 |
});
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return banner;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
_buttonForApp(app) {
|
|
Packit |
d345d1 |
let box = new St.BoxLayout();
|
|
Packit |
d345d1 |
let icon = new St.Icon({ gicon: app.get_icon(),
|
|
Packit |
d345d1 |
style_class: 'hotplug-notification-item-icon' });
|
|
Packit |
d345d1 |
box.add(icon);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let label = new St.Bin({ y_align: St.Align.MIDDLE,
|
|
Packit |
d345d1 |
child: new St.Label
|
|
Packit |
d345d1 |
({ text: _("Open with %s").format(app.get_name()) })
|
|
Packit |
d345d1 |
});
|
|
Packit |
d345d1 |
box.add(label);
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let button = new St.Button({ child: box,
|
|
Packit |
d345d1 |
x_fill: true,
|
|
Packit |
d345d1 |
x_align: St.Align.START,
|
|
Packit |
d345d1 |
x_expand: true,
|
|
Packit |
d345d1 |
button_mask: St.ButtonMask.ONE,
|
|
Packit |
d345d1 |
style_class: 'hotplug-notification-item button' });
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
button.connect('clicked', () => {
|
|
Packit |
d345d1 |
startAppForMount(app, this._mount);
|
|
Packit |
d345d1 |
this.destroy();
|
|
Packit |
d345d1 |
});
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
return button;
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
activate() {
|
|
Packit |
d345d1 |
super.activate();
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
let app = Gio.app_info_get_default_for_type('inode/directory', false);
|
|
Packit |
d345d1 |
startAppForMount(app, this._mount);
|
|
Packit |
d345d1 |
}
|
|
Packit |
d345d1 |
};
|
|
Packit |
d345d1 |
|
|
Packit |
d345d1 |
var Component = AutorunManager;
|