9a49b20fac
It isn't possible to easily delete the marker from an XDG autostart file, so make it on startup to simplify that. This is in preparation for the next commit which adds the appropriate autostart file. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/695
748 lines
23 KiB
JavaScript
748 lines
23 KiB
JavaScript
/* exported main */
|
||
imports.gi.versions.Gdk = '3.0';
|
||
imports.gi.versions.Gtk = '3.0';
|
||
|
||
const Gettext = imports.gettext;
|
||
const { Gdk, GLib, Gio, GObject, Gtk, Pango } = imports.gi;
|
||
const Format = imports.format;
|
||
|
||
const _ = Gettext.gettext;
|
||
|
||
const Config = imports.misc.config;
|
||
const ExtensionUtils = imports.misc.extensionUtils;
|
||
const { loadInterfaceXML, deleteGFile } = imports.misc.fileUtils;
|
||
|
||
const { ExtensionState } = ExtensionUtils;
|
||
|
||
const GnomeShellIface = loadInterfaceXML('org.gnome.Shell.Extensions');
|
||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||
|
||
function stripPrefix(string, prefix) {
|
||
if (string.slice(0, prefix.length) == prefix)
|
||
return string.slice(prefix.length);
|
||
return string;
|
||
}
|
||
|
||
var Application = GObject.registerClass({
|
||
GTypeName: 'ExtensionPrefs_Application'
|
||
}, class Application extends Gtk.Application {
|
||
_init() {
|
||
GLib.set_prgname('gnome-shell-extension-prefs');
|
||
super._init({
|
||
application_id: 'org.gnome.shell.ExtensionPrefs',
|
||
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
|
||
});
|
||
|
||
this._startupUuid = null;
|
||
this._loaded = false;
|
||
this._skipMainWindow = false;
|
||
this._shellProxy = null;
|
||
}
|
||
|
||
get shellProxy() {
|
||
return this._shellProxy;
|
||
}
|
||
|
||
_showPrefs(uuid) {
|
||
let row = this._extensionSelector.get_children().find(c => {
|
||
return c.uuid === uuid && c.hasPrefs;
|
||
});
|
||
|
||
if (!row)
|
||
return false;
|
||
|
||
let widget;
|
||
|
||
try {
|
||
widget = row.prefsModule.buildPrefsWidget();
|
||
} catch (e) {
|
||
widget = this._buildErrorUI(row, e);
|
||
}
|
||
|
||
let dialog = new Gtk.Window({
|
||
modal: !this._skipMainWindow,
|
||
type_hint: Gdk.WindowTypeHint.DIALOG
|
||
});
|
||
dialog.set_titlebar(new Gtk.HeaderBar({
|
||
show_close_button: true,
|
||
title: row.name,
|
||
visible: true
|
||
}));
|
||
|
||
if (this._skipMainWindow) {
|
||
this.add_window(dialog);
|
||
if (this._window)
|
||
this._window.destroy();
|
||
this._window = dialog;
|
||
this._window.window_position = Gtk.WindowPosition.CENTER;
|
||
} else {
|
||
dialog.transient_for = this._window;
|
||
}
|
||
|
||
dialog.set_default_size(600, 400);
|
||
dialog.add(widget);
|
||
dialog.show();
|
||
|
||
return true;
|
||
}
|
||
|
||
_buildErrorUI(row, exc) {
|
||
let scroll = new Gtk.ScrolledWindow({
|
||
hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||
propagate_natural_height: true
|
||
});
|
||
|
||
let box = new Gtk.Box({
|
||
orientation: Gtk.Orientation.VERTICAL,
|
||
spacing: 12,
|
||
margin: 100,
|
||
margin_bottom: 60
|
||
});
|
||
scroll.add(box);
|
||
|
||
let label = new Gtk.Label({
|
||
label: '<span size="x-large">%s</span>'.format(_("Something’s gone wrong")),
|
||
use_markup: true
|
||
});
|
||
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
|
||
box.add(label);
|
||
|
||
label = new Gtk.Label({
|
||
label: _("We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. We recommend that you report the issue to the extension authors."),
|
||
justify: Gtk.Justification.CENTER,
|
||
wrap: true
|
||
});
|
||
box.add(label);
|
||
|
||
let expander = new Expander({
|
||
label: _("Technical Details"),
|
||
margin_top: 12
|
||
});
|
||
box.add(expander);
|
||
|
||
let errortext = `${exc}\n\nStack trace:\n${
|
||
// Indent stack trace.
|
||
exc.stack.split('\n').map(line => ` ${line}`).join('\n')
|
||
}`;
|
||
|
||
let buffer = new Gtk.TextBuffer({ text: errortext });
|
||
let textview = new Gtk.TextView({
|
||
buffer: buffer,
|
||
wrap_mode: Gtk.WrapMode.WORD,
|
||
monospace: true,
|
||
editable: false,
|
||
top_margin: 12,
|
||
bottom_margin: 12,
|
||
left_margin: 12,
|
||
right_margin: 12
|
||
});
|
||
|
||
let toolbar = new Gtk.Toolbar();
|
||
let provider = new Gtk.CssProvider();
|
||
provider.load_from_data(`* {
|
||
border: 0 solid @borders;
|
||
border-top-width: 1px;
|
||
}`);
|
||
toolbar.get_style_context().add_provider(
|
||
provider,
|
||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||
);
|
||
|
||
let copyButton = new Gtk.ToolButton({
|
||
icon_name: 'edit-copy-symbolic',
|
||
tooltip_text: _("Copy Error")
|
||
});
|
||
toolbar.add(copyButton);
|
||
|
||
copyButton.connect('clicked', w => {
|
||
let clipboard = Gtk.Clipboard.get_default(w.get_display());
|
||
// markdown for pasting in gitlab issues
|
||
let lines = [
|
||
`The settings of extension ${row.uuid} had an error:`,
|
||
'```',
|
||
`${exc}`,
|
||
'```',
|
||
'',
|
||
'Stack trace:',
|
||
'```',
|
||
exc.stack.replace(/\n$/, ''), // stack without trailing newline
|
||
'```',
|
||
''
|
||
];
|
||
clipboard.set_text(lines.join('\n'), -1);
|
||
});
|
||
|
||
let spacing = new Gtk.SeparatorToolItem({ draw: false });
|
||
toolbar.add(spacing);
|
||
toolbar.child_set_property(spacing, "expand", true);
|
||
|
||
let urlButton = new Gtk.ToolButton({
|
||
label: _("Homepage"),
|
||
tooltip_text: _("Visit extension homepage"),
|
||
no_show_all: true,
|
||
visible: row.url != null
|
||
});
|
||
toolbar.add(urlButton);
|
||
|
||
urlButton.connect('clicked', w => {
|
||
let context = w.get_display().get_app_launch_context();
|
||
Gio.AppInfo.launch_default_for_uri(row.url, context);
|
||
});
|
||
|
||
let expandedBox = new Gtk.Box({
|
||
orientation: Gtk.Orientation.VERTICAL
|
||
});
|
||
expandedBox.add(textview);
|
||
expandedBox.add(toolbar);
|
||
|
||
expander.add(expandedBox);
|
||
|
||
scroll.show_all();
|
||
return scroll;
|
||
}
|
||
|
||
_buildUI() {
|
||
this._window = new Gtk.ApplicationWindow({ application: this,
|
||
window_position: Gtk.WindowPosition.CENTER });
|
||
|
||
this._window.set_default_size(800, 500);
|
||
|
||
this._titlebar = new Gtk.HeaderBar({ show_close_button: true,
|
||
title: _("Shell Extensions") });
|
||
this._window.set_titlebar(this._titlebar);
|
||
|
||
let killSwitch = new Gtk.Switch({ valign: Gtk.Align.CENTER });
|
||
this._titlebar.pack_end(killSwitch);
|
||
|
||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||
this._settings.bind('disable-user-extensions', killSwitch, 'active',
|
||
Gio.SettingsBindFlags.DEFAULT |
|
||
Gio.SettingsBindFlags.INVERT_BOOLEAN);
|
||
|
||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||
this._window.add(vbox);
|
||
|
||
this.disabledInfobar = new Gtk.InfoBar({
|
||
message_type: Gtk.MessageType.ERROR,
|
||
revealed: false,
|
||
show_close_button: true
|
||
});
|
||
this.disabledInfobar.connect('response', () => {
|
||
this.disabledInfobar.revealed = false;
|
||
});
|
||
let contentArea = this.disabledInfobar.get_content_area();
|
||
let label = new Gtk.Label({
|
||
label: _('A problem was detected and extensions were automatically disabled. It is recommended to disable or reconfigure any extensions that may have caused the issue before re-enabling them at the top.'),
|
||
ellipsize: Pango.EllipsizeMode.END,
|
||
wrap: true,
|
||
lines: 2,
|
||
xalign: 0,
|
||
margin: 6
|
||
});
|
||
contentArea.add(label);
|
||
vbox.add(this.disabledInfobar);
|
||
|
||
this._mainStack = new Gtk.Stack({
|
||
transition_type: Gtk.StackTransitionType.CROSSFADE
|
||
});
|
||
vbox.add(this._mainStack);
|
||
|
||
let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||
|
||
this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE });
|
||
this._extensionSelector.set_sort_func(this._sortList.bind(this));
|
||
this._extensionSelector.set_header_func(this._updateHeader.bind(this));
|
||
|
||
scroll.add(this._extensionSelector);
|
||
|
||
this._mainStack.add_named(scroll, 'listing');
|
||
this._mainStack.add_named(new EmptyPlaceholder(), 'placeholder');
|
||
|
||
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
|
||
this._shellProxy.connectSignal('ExtensionStateChanged',
|
||
this._onExtensionStateChanged.bind(this));
|
||
|
||
this._window.show_all();
|
||
}
|
||
|
||
_sortList(row1, row2) {
|
||
return row1.name.localeCompare(row2.name);
|
||
}
|
||
|
||
_updateHeader(row, before) {
|
||
if (!before || row.get_header())
|
||
return;
|
||
|
||
let sep = new Gtk.Separator({ orientation: Gtk.Orientation.HORIZONTAL });
|
||
row.set_header(sep);
|
||
}
|
||
|
||
_findExtensionRow(uuid) {
|
||
return this._extensionSelector.get_children().find(c => c.uuid === uuid);
|
||
}
|
||
|
||
_onExtensionStateChanged(proxy, senderName, [uuid, newState]) {
|
||
let row = this._findExtensionRow(uuid);
|
||
if (row) {
|
||
let { state } = ExtensionUtils.deserializeExtension(newState);
|
||
if (state == ExtensionState.UNINSTALLED)
|
||
row.destroy();
|
||
return; // we only deal with new and deleted extensions here
|
||
}
|
||
|
||
this._shellProxy.GetExtensionInfoRemote(uuid, ([serialized]) => {
|
||
let extension = ExtensionUtils.deserializeExtension(serialized);
|
||
if (!extension)
|
||
return;
|
||
// check the extension wasn't added in between
|
||
if (this._findExtensionRow(uuid) != null)
|
||
return;
|
||
this._addExtensionRow(extension);
|
||
});
|
||
}
|
||
|
||
_scanExtensions() {
|
||
this._shellProxy.ListExtensionsRemote(([extensionsMap], e) => {
|
||
if (e) {
|
||
if (e instanceof Gio.DBusError) {
|
||
log(`Failed to connect to shell proxy: ${e}`);
|
||
this._mainStack.add_named(new NoShellPlaceholder(), 'noshell');
|
||
this._mainStack.visible_child_name = 'noshell';
|
||
} else
|
||
throw e;
|
||
return;
|
||
}
|
||
|
||
for (let uuid in extensionsMap) {
|
||
let extension = ExtensionUtils.deserializeExtension(extensionsMap[uuid]);
|
||
this._addExtensionRow(extension);
|
||
}
|
||
this._extensionsLoaded();
|
||
});
|
||
}
|
||
|
||
_addExtensionRow(extension) {
|
||
let row = new ExtensionRow(extension);
|
||
|
||
row.prefsButton.connect('clicked', () => {
|
||
this._showPrefs(row.uuid);
|
||
});
|
||
|
||
row.show_all();
|
||
this._extensionSelector.add(row);
|
||
}
|
||
|
||
_extensionsLoaded() {
|
||
if (this._extensionSelector.get_children().length > 0)
|
||
this._mainStack.visible_child_name = 'listing';
|
||
else
|
||
this._mainStack.visible_child_name = 'placeholder';
|
||
|
||
if (this._startupUuid)
|
||
this._showPrefs(this._startupUuid);
|
||
this._startupUuid = null;
|
||
this._skipMainWindow = false;
|
||
this._loaded = true;
|
||
}
|
||
|
||
vfunc_activate() {
|
||
this._window.present();
|
||
}
|
||
|
||
vfunc_startup() {
|
||
super.vfunc_startup();
|
||
|
||
this._buildUI();
|
||
this._scanExtensions();
|
||
}
|
||
|
||
vfunc_command_line(commandLine) {
|
||
this.activate();
|
||
let args = commandLine.get_arguments();
|
||
|
||
if (args.length) {
|
||
if (args[0] == '--disabled-warning') {
|
||
if (!this._settings.is_writable('disable-user-extensions'))
|
||
this.quit();
|
||
|
||
this.disabledInfobar.set_revealed(true);
|
||
|
||
let file = GLib.build_filenamev ([GLib.get_user_config_dir(), 'gnome-shell-extensions-disabled-warning']);
|
||
let gfile = Gio.File.new_for_path(file);
|
||
if (gfile.query_exists(null))
|
||
deleteGFile(gfile);
|
||
|
||
return 0;
|
||
}
|
||
|
||
let uuid = args[0];
|
||
|
||
this._skipMainWindow = true;
|
||
|
||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||
uuid = stripPrefix(uuid, "extension:///");
|
||
|
||
if (!this._loaded)
|
||
this._startupUuid = uuid;
|
||
else if (!this._showPrefs(uuid))
|
||
this._skipMainWindow = false;
|
||
}
|
||
return 0;
|
||
}
|
||
});
|
||
|
||
var Expander = GObject.registerClass({
|
||
Properties: {
|
||
'label': GObject.ParamSpec.string(
|
||
'label', 'label', 'label',
|
||
GObject.ParamFlags.READWRITE,
|
||
null
|
||
)
|
||
}
|
||
}, class Expander extends Gtk.Box {
|
||
_init(params = {}) {
|
||
this._labelText = null;
|
||
|
||
super._init(Object.assign(params, {
|
||
orientation: Gtk.Orientation.VERTICAL,
|
||
spacing: 0
|
||
}));
|
||
|
||
this._frame = new Gtk.Frame({
|
||
shadow_type: Gtk.ShadowType.IN,
|
||
hexpand: true
|
||
});
|
||
|
||
let eventBox = new Gtk.EventBox();
|
||
this._frame.add(eventBox);
|
||
|
||
let hbox = new Gtk.Box({
|
||
spacing: 6,
|
||
margin: 12
|
||
});
|
||
eventBox.add(hbox);
|
||
|
||
this._arrow = new Gtk.Image({
|
||
icon_name: 'pan-end-symbolic'
|
||
});
|
||
hbox.add(this._arrow);
|
||
|
||
this._label = new Gtk.Label({ label: this._labelText });
|
||
hbox.add(this._label);
|
||
|
||
this._revealer = new Gtk.Revealer();
|
||
|
||
this._childBin = new Gtk.Frame({
|
||
shadow_type: Gtk.ShadowType.IN
|
||
});
|
||
this._revealer.add(this._childBin);
|
||
|
||
// Directly chain up to parent for internal children
|
||
super.add(this._frame);
|
||
super.add(this._revealer);
|
||
|
||
let provider = new Gtk.CssProvider();
|
||
provider.load_from_data('* { border-top-width: 0; }');
|
||
this._childBin.get_style_context().add_provider(
|
||
provider,
|
||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
|
||
);
|
||
|
||
this._gesture = new Gtk.GestureMultiPress({
|
||
widget: this._frame,
|
||
button: 0,
|
||
exclusive: true
|
||
});
|
||
this._gesture.connect('released', (gesture, nPress) => {
|
||
if (nPress == 1)
|
||
this._revealer.reveal_child = !this._revealer.reveal_child;
|
||
});
|
||
this._revealer.connect('notify::reveal-child', () => {
|
||
if (this._revealer.reveal_child)
|
||
this._arrow.icon_name = 'pan-down-symbolic';
|
||
else
|
||
this._arrow.icon_name = 'pan-end-symbolic';
|
||
});
|
||
}
|
||
|
||
get label() {
|
||
return this._labelText;
|
||
}
|
||
|
||
set label(text) {
|
||
if (this._labelText == text)
|
||
return;
|
||
|
||
if (this._label)
|
||
this._label.label = text;
|
||
this._labelText = text;
|
||
this.notify('label');
|
||
}
|
||
|
||
add(child) {
|
||
// set expanded child
|
||
this._childBin.get_children().forEach(c => {
|
||
this._childBin.remove(c);
|
||
});
|
||
|
||
if (child)
|
||
this._childBin.add(child);
|
||
}
|
||
});
|
||
|
||
var EmptyPlaceholder = GObject.registerClass(
|
||
class EmptyPlaceholder extends Gtk.Box {
|
||
_init() {
|
||
super._init({
|
||
orientation: Gtk.Orientation.VERTICAL,
|
||
spacing: 6,
|
||
margin: 32
|
||
});
|
||
|
||
let image = new Gtk.Image({
|
||
icon_name: 'application-x-addon-symbolic',
|
||
pixel_size: 96,
|
||
visible: true,
|
||
vexpand: true,
|
||
valign: Gtk.Align.END
|
||
});
|
||
image.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
|
||
this.add(image);
|
||
|
||
let label = new Gtk.Label({
|
||
label: `<b><span size="x-large">${_("No Extensions Installed" )}</span></b>`,
|
||
use_markup: true,
|
||
visible: true
|
||
});
|
||
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
|
||
this.add(label);
|
||
|
||
let appInfo = Gio.DesktopAppInfo.new('org.gnome.Software.desktop');
|
||
|
||
let desc = new Gtk.Label({
|
||
label: _("Extensions can be installed through Software or <a href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."),
|
||
use_markup: true,
|
||
wrap: true,
|
||
justify: Gtk.Justification.CENTER,
|
||
visible: true,
|
||
max_width_chars: 50,
|
||
hexpand: true,
|
||
vexpand: (appInfo == null),
|
||
halign: Gtk.Align.CENTER,
|
||
valign: Gtk.Align.START
|
||
});
|
||
this.add(desc);
|
||
|
||
if (appInfo) {
|
||
let button = new Gtk.Button({
|
||
label: _("Browse in Software"),
|
||
image: new Gtk.Image({
|
||
icon_name: "org.gnome.Software-symbolic"
|
||
}),
|
||
always_show_image: true,
|
||
margin_top: 12,
|
||
visible: true,
|
||
halign: Gtk.Align.CENTER,
|
||
valign: Gtk.Align.START,
|
||
vexpand: true
|
||
});
|
||
this.add(button);
|
||
|
||
button.connect('clicked', w => {
|
||
let context = w.get_display().get_app_launch_context();
|
||
appInfo.launch([], context);
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
var NoShellPlaceholder = GObject.registerClass(
|
||
class NoShellPlaceholder extends Gtk.Box {
|
||
_init() {
|
||
super._init({
|
||
orientation: Gtk.Orientation.VERTICAL,
|
||
spacing: 12,
|
||
margin: 100,
|
||
margin_bottom: 60
|
||
});
|
||
|
||
let label = new Gtk.Label({
|
||
label: '<span size="x-large">%s</span>'.format(
|
||
_("Something’s gone wrong")),
|
||
use_markup: true
|
||
});
|
||
label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
|
||
this.add(label);
|
||
|
||
label = new Gtk.Label({
|
||
label: _("We’re very sorry, but it was not possible to get the list of installed extensions. Make sure you are logged into GNOME and try again."),
|
||
justify: Gtk.Justification.CENTER,
|
||
wrap: true
|
||
});
|
||
this.add(label);
|
||
|
||
this.show_all();
|
||
}
|
||
});
|
||
|
||
var DescriptionLabel = GObject.registerClass(
|
||
class DescriptionLabel extends Gtk.Label {
|
||
vfunc_get_preferred_height_for_width(width) {
|
||
// Hack: Request the maximum height allowed by the line limit
|
||
if (this.lines > 0)
|
||
return super.vfunc_get_preferred_height_for_width(0);
|
||
return super.vfunc_get_preferred_height_for_width(width);
|
||
}
|
||
});
|
||
|
||
var ExtensionRow = GObject.registerClass(
|
||
class ExtensionRow extends Gtk.ListBoxRow {
|
||
_init(extension) {
|
||
super._init();
|
||
|
||
this._app = Gio.Application.get_default();
|
||
this._extension = extension;
|
||
this._prefsModule = null;
|
||
|
||
this._extensionStateChangedId = this._app.shellProxy.connectSignal(
|
||
'ExtensionStateChanged', (p, sender, [uuid, newState]) => {
|
||
if (this.uuid !== uuid)
|
||
return;
|
||
|
||
this._extension = ExtensionUtils.deserializeExtension(newState);
|
||
let state = (this._extension.state == ExtensionState.ENABLED);
|
||
this._switch.freeze_notify();
|
||
this._switch.state = state;
|
||
this._switch.active = this._extension.isRequested;
|
||
this._switch.sensitive = this._canToggle();
|
||
this._switch.thaw_notify();
|
||
});
|
||
|
||
this.connect('destroy', this._onDestroy.bind(this));
|
||
|
||
this._buildUI();
|
||
}
|
||
|
||
get uuid() {
|
||
return this._extension.uuid;
|
||
}
|
||
|
||
get name() {
|
||
return this._extension.metadata.name;
|
||
}
|
||
|
||
get hasPrefs() {
|
||
return this._extension.hasPrefs;
|
||
}
|
||
|
||
get url() {
|
||
return this._extension.metadata.url;
|
||
}
|
||
|
||
_onDestroy() {
|
||
if (!this._app.shellProxy)
|
||
return;
|
||
|
||
if (this._extensionStateChangedId)
|
||
this._app.shellProxy.disconnectSignal(this._extensionStateChangedId);
|
||
this._extensionStateChangedId = 0;
|
||
}
|
||
|
||
_buildUI() {
|
||
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
|
||
hexpand: true, margin_end: 24, spacing: 24,
|
||
margin: 12 });
|
||
this.add(hbox);
|
||
|
||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
|
||
spacing: 6, hexpand: true });
|
||
hbox.add(vbox);
|
||
|
||
let name = GLib.markup_escape_text(this.name, -1);
|
||
let label = new Gtk.Label({ label: '<b>' + name + '</b>',
|
||
use_markup: true,
|
||
halign: Gtk.Align.START });
|
||
vbox.add(label);
|
||
|
||
let desc = this._extension.metadata.description.split('\n')[0];
|
||
label = new DescriptionLabel({ label: desc, wrap: true, lines: 2,
|
||
ellipsize: Pango.EllipsizeMode.END,
|
||
xalign: 0, yalign: 0 });
|
||
vbox.add(label);
|
||
|
||
let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
|
||
visible: this.hasPrefs,
|
||
no_show_all: true });
|
||
button.set_image(new Gtk.Image({ icon_name: 'emblem-system-symbolic',
|
||
icon_size: Gtk.IconSize.BUTTON,
|
||
visible: true }));
|
||
button.get_style_context().add_class('circular');
|
||
hbox.add(button);
|
||
|
||
this.prefsButton = button;
|
||
|
||
this._switch = new Gtk.Switch({
|
||
valign: Gtk.Align.CENTER,
|
||
sensitive: this._canToggle(),
|
||
});
|
||
this._switch.connect('notify::active', () => {
|
||
if (this._switch.active)
|
||
this._app.shellProxy.EnableExtensionRemote(this.uuid);
|
||
else
|
||
this._app.shellProxy.DisableExtensionRemote(this.uuid);
|
||
});
|
||
this._switch.connect('state-set', () => true);
|
||
this._switch.freeze_notify();
|
||
this._switch.state = this._extension.state === ExtensionState.ENABLED;
|
||
this._switch.active = this._extension.isRequested;
|
||
this._switch.thaw_notify();
|
||
|
||
hbox.add(this._switch);
|
||
}
|
||
|
||
_canToggle() {
|
||
return this._extension.canChange;
|
||
}
|
||
|
||
get prefsModule() {
|
||
if (!this._prefsModule) {
|
||
ExtensionUtils.installImporter(this._extension);
|
||
|
||
// give extension prefs access to their own extension object
|
||
ExtensionUtils.getCurrentExtension = () => this._extension;
|
||
|
||
this._prefsModule = this._extension.imports.prefs;
|
||
this._prefsModule.init(this._extension.metadata);
|
||
}
|
||
|
||
return this._prefsModule;
|
||
}
|
||
});
|
||
|
||
function initEnvironment() {
|
||
// Monkey-patch in a "global" object that fakes some Shell utilities
|
||
// that ExtensionUtils depends on.
|
||
window.global = {
|
||
log(...args) {
|
||
print(args.join(', '));
|
||
},
|
||
|
||
logError(s) {
|
||
log(`ERROR: ${s}`);
|
||
},
|
||
|
||
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
|
||
};
|
||
|
||
String.prototype.format = Format.format;
|
||
}
|
||
|
||
function main(argv) {
|
||
initEnvironment();
|
||
|
||
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
||
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
||
|
||
new Application().run(argv);
|
||
}
|