Commit 2a800e4c authored by Jasper St. Pierre's avatar Jasper St. Pierre

Rearchitect the Shell to have a components system

Components are pieces of the shell code that can be added/removed
at runtime, like extension, but are tied more directly to a session
mode. The session polkit agent, the network agent, autorun/automount,
are all components, keyring, recorder and telepathy client are all
now copmonents.

https://bugzilla.gnome.org/show_bug.cgi?id=683156
parent ca2e09fe
NULL =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
......@@ -37,8 +38,6 @@ nobase_dist_js_DATA = \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/automountManager.js \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
......@@ -47,15 +46,14 @@ nobase_dist_js_DATA = \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/keyringPrompt.js \
ui/layout.js \
ui/lightbox.js \
ui/lookingGlass.js \
......@@ -64,7 +62,6 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
......@@ -74,7 +71,6 @@ nobase_dist_js_DATA = \
ui/panelMenu.js \
ui/placeDisplay.js \
ui/pointerWatcher.js \
ui/polkitAuthenticationAgent.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
......@@ -90,7 +86,6 @@ nobase_dist_js_DATA = \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
......@@ -102,4 +97,13 @@ nobase_dist_js_DATA = \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js
ui/xdndHandler.js \
ui/components/__init__.js \
ui/components/autorunManager.js \
ui/components/automountManager.js \
ui/components/networkAgent.js \
ui/components/polkitAgent.js \
ui/components/recorder.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
$(NULL)
const Lang = imports.lang;
const Main = imports.ui.main;
const ComponentManager = new Lang.Class({
Name: 'ComponentManager',
_init: function() {
this._allComponents = {};
this._enabledComponents = [];
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_sessionUpdated: function() {
let newEnabledComponents = Main.sessionMode.components;
newEnabledComponents.filter(Lang.bind(this, function(name) {
return this._enabledComponents.indexOf(name) == -1;
})).forEach(Lang.bind(this, function(name) {
this._enableComponent(name);
}));
this._enabledComponents.filter(Lang.bind(this, function(name) {
return newEnabledComponents.indexOf(name) == -1;
})).forEach(Lang.bind(this, function(name) {
this._disableComponent(name);
}));
this._enabledComponents = newEnabledComponents;
},
_importComponent: function(name) {
let module = imports.ui.components[name];
return module.Component;
},
_ensureComponent: function(name) {
let component = this._allComponents[name];
if (component)
return component;
let constructor = this._importComponent(name);
component = new constructor();
this._allComponents[name] = component;
return component;
},
_enableComponent: function(name) {
let component = this._ensureComponent(name);
component.enable();
},
_disableComponent: function(name) {
let component = this._allComponents[name];
if (component == null)
return;
component.disable();
}
});
......@@ -34,28 +34,30 @@ const AutomountManager = new Lang.Class({
this._inhibited = false;
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get();
},
Main.screenShield.connect('lock-status-changed', Lang.bind(this, this._lockStatusChanged));
enable: function() {
this._volumeAddedId = this._volumeMonitor.connect('volume-added', Lang.bind(this, this._onVolumeAdded));
this._volumeRemovedId = this._volumeMonitor.connect('volume-removed', Lang.bind(this, this._onVolumeRemoved));
this._driveConnectedId = this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._onDriveConnected));
this._driveDisconnectedId = this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._onDriveDisconnected));
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
this._volumeMonitor = Gio.VolumeMonitor.get();
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
disable: function() {
this._volumeMonitor.disconnect(this._volumeAddedId);
this._volumeMonitor.disconnect(this._volumeRemovedId);
this._volumeMonitor.disconnect(this._driveConnectedId);
this._volumeMonitor.disconnect(this._driveDisconnectedId);
this._volumeMonitor.disconnect(this._driveEjectButtonId);
this._volumeMonitor.connect('volume-added',
Lang.bind(this,
this._onVolumeAdded));
this._volumeMonitor.connect('volume-removed',
Lang.bind(this,
this._onVolumeRemoved));
this._volumeMonitor.connect('drive-connected',
Lang.bind(this,
this._onDriveConnected));
this._volumeMonitor.connect('drive-disconnected',
Lang.bind(this,
this._onDriveDisconnected));
this._volumeMonitor.connect('drive-eject-button',
Lang.bind(this,
this._onDriveEjectButton));
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
if (this._mountAllId > 0) {
Mainloop.source_remove(this._mountAllId);
this._mountAllId = 0;
}
},
_InhibitorsChanged: function(object, senderName, [inhibtor]) {
......@@ -68,17 +70,6 @@ const AutomountManager = new Lang.Class({
}));
},
_lockStatusChanged: function(shield, locked) {
if (!locked) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
}
// clear the queue anyway
this._volumeQueue = [];
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
......@@ -87,6 +78,7 @@ const AutomountManager = new Lang.Class({
allowAutorun: false });
}));
this._mountAllId = 0;
return false;
},
......@@ -96,9 +88,6 @@ const AutomountManager = new Lang.Class({
if (!this._loginManager.sessionActive)
return;
if (Main.screenShield.locked)
return;
global.play_theme_sound(0, 'device-added-media');
},
......@@ -108,9 +97,6 @@ const AutomountManager = new Lang.Class({
if (!this._loginManager.sessionActive)
return;
if (Main.screenShield.locked)
return;
global.play_theme_sound(0, 'device-removed-media');
},
......@@ -159,13 +145,6 @@ const AutomountManager = new Lang.Class({
// don't attempt automount
if (!this._loginManager.sessionActive)
return;
if (Main.screenShield.locked) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
return;
}
}
if (this._inhibited)
......@@ -259,3 +238,4 @@ const AutomountManager = new Lang.Class({
});
}
});
const Component = AutomountManager;
......@@ -147,18 +147,25 @@ const AutorunManager = new Lang.Class({
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('mount-added',
Lang.bind(this,
this._onMountAdded));
this._volumeMonitor.connect('mount-removed',
Lang.bind(this,
this._onMountRemoved));
this._transDispatcher = new AutorunTransientDispatcher(this);
},
this._transDispatcher = new AutorunTransientDispatcher();
this._createResidentSource();
enable: function() {
this._residentSource = new AutorunResidentSource(this);
this._scanMounts();
let mounts = this._volumeMonitor.get_mounts();
this._mountAddedId = this._volumeMonitor.connect('mount-added', Lang.bind(this, this._onMountAdded));
this._mountRemovedId = this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._onMountRemoved));
},
disable: function() {
this._residentSource.destroy();
this._volumeMonitor.disconnect(this._mountAddedId);
this._volumeMonitor.disconnect(this._mountRemovedId);
},
_scanMounts: function() {
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function (mount) {
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps) {
......@@ -169,13 +176,6 @@ const AutorunManager = new Lang.Class({
}));
},
_createResidentSource: function() {
this._residentSource = new AutorunResidentSource();
this._residentSource.connect('destroy',
Lang.bind(this,
this._createResidentSource));
},
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
......@@ -260,13 +260,14 @@ const AutorunResidentSource = new Lang.Class({
Name: 'AutorunResidentSource',
Extends: MessageTray.Source,
_init: function() {
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._notification = new AutorunResidentNotification(this._manager, this);
this._manager = manager;
},
addMount: function(mount, apps) {
......@@ -316,7 +317,7 @@ const AutorunResidentNotification = new Lang.Class({
Name: 'AutorunResidentNotification',
Extends: MessageTray.Notification,
_init: function(source) {
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
// set the notification as resident
......@@ -324,6 +325,7 @@ const AutorunResidentNotification = new Lang.Class({
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this._manager = manager;
this.addActor(this._layout,
{ x_expand: true,
......@@ -382,11 +384,11 @@ const AutorunResidentNotification = new Lang.Class({
// now connect signals
mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
startAppForMount(apps[0], mount);
this._manager.startAppForMount(apps[0], mount);
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(mount);
this._manager.ejectMount(mount);
}));
return item;
......@@ -396,7 +398,8 @@ const AutorunResidentNotification = new Lang.Class({
const AutorunTransientDispatcher = new Lang.Class({
Name: 'AutorunTransientDispatcher',
_init: function() {
_init: function(manager) {
this._manager = manager;
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
......@@ -439,7 +442,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// add a new source
this._sources.push(new AutorunTransientSource(mount, apps));
this._sources.push(new AutorunTransientSource(this._manager, mount, apps));
},
addMount: function(mount, apps, contentTypes) {
......@@ -492,13 +495,14 @@ const AutorunTransientSource = new Lang.Class({
Name: 'AutorunTransientSource',
Extends: MessageTray.Source,
_init: function(mount, apps) {
_init: function(manager, mount, apps) {
this._manager = manager;
this.mount = mount;
this.apps = apps;
this.parent(mount.get_name());
this._notification = new AutorunTransientNotification(this);
this._notification = new AutorunTransientNotification(this._manager, this);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
......@@ -515,9 +519,10 @@ const AutorunTransientNotification = new Lang.Class({
Name: 'AutorunTransientNotification',
Extends: MessageTray.Notification,
_init: function(source) {
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
this._manager = manager;
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
......@@ -586,10 +591,11 @@ const AutorunTransientNotification = new Lang.Class({
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(this._mount);
this._manager.ejectMount(this._mount);
}));
return button;
}
});
const Component = AutorunManager;
......@@ -192,23 +192,35 @@ const KeyringDialog = new Lang.Class({
},
_onContinueButton: function() {
this.prompt.complete()
this.prompt.complete();
},
_onCancelButton: function() {
this.prompt.cancel()
this.prompt.cancel();
},
});
function init() {
prompter = new Gcr.SystemPrompter();
prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
let connection = Gio.DBus.session;
prompter.register(connection);
Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
}
const KeyringPrompter = new Lang.Class({
Name: 'KeyringPrompter',
_init: function() {
this._prompter = new Gcr.SystemPrompter();
this._prompter.connect('new-prompt', function(prompter) {
let dialog = new KeyringDialog();
return dialog.prompt;
});
this._dbusId = null;
},
enable: function() {
this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.REPLACE, null, null);
},
disable: function() {
Gio.DBus.session.unown_name(this._dbusId);
}
});
const Component = KeyringPrompter;
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
......@@ -603,7 +584,7 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: true,
this._native = new Shell.NetworkAgent({ auto_register: false,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
......@@ -613,6 +594,14 @@ const NetworkAgent = new Lang.Class({
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
enable: function() {
this._native.register();
},
disable: function() {
this._native.unregister();
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags);
......@@ -702,3 +691,4 @@ const NetworkAgent = new Lang.Class({
}
}
});
const Component = NetworkAgent;
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
const Lang = imports.lang;
const Signals = imports.signals;
......@@ -33,6 +13,7 @@ const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
const PolkitAgent = imports.gi.PolkitAgent;
const Components = imports.ui.components;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserMenu = imports.ui.userMenu;
......@@ -336,11 +317,20 @@ const AuthenticationAgent = new Lang.Class({
Name: 'AuthenticationAgent',
_init: function() {
this._currentDialog = null;
this._isCompleting = false;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
this._native.connect('cancel', Lang.bind(this, this._onCancel));
this._currentDialog = null;
this._isCompleting = false;
},
enable: function() {
this._native.register();
},
disable: function() {
this._native.unregister();
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
......@@ -398,6 +388,4 @@ const AuthenticationAgent = new Lang.Class({
}
});
function init() {
let agent = new AuthenticationAgent();
}
const Component = AuthenticationAgent;
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Recorder = new Lang.Class({
Name: 'Recorder',
_init: function() {
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
this._recorder = null;
},
enable: function() {
global.display.add_keybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder));
},
disable: function() {
global.display.remove_keybinding('toggle-recording');
},
_ensureRecorder: function() {
if (this._recorder == null)
this._recorder = new Shell.Recorder({ stage: global.stage });
return this._recorder;
},
_toggleRecorder: function() {
let recorder = this._ensureRecorder();
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else if (!desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_filename(_("Screencast from %d %t") + '.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();