9d941f8202
xgettext gained some support for template strings, and no longer fails when encountering '/' somewhere between backticks. Unfortunately its support is still buggy as hell, and it is now silently dropping translatable strings, yay. I hate making the code worse, but until xgettext really gets its shit together, the only viable way forward seems to be to not use template strings in any files listed in POTFILES. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1014
764 lines
22 KiB
JavaScript
764 lines
22 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 } = imports.gi;
|
||
const Format = imports.format;
|
||
|
||
const _ = Gettext.gettext;
|
||
|
||
const Config = imports.misc.config;
|
||
const ExtensionUtils = imports.misc.extensionUtils;
|
||
const { loadInterfaceXML } = imports.misc.fileUtils;
|
||
|
||
const { ExtensionState, ExtensionType } = 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(
|
||
class Application extends Gtk.Application {
|
||
_init() {
|
||
GLib.set_prgname('gnome-shell-extension-prefs');
|
||
super._init({
|
||
application_id: 'org.gnome.Extensions',
|
||
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
|
||
});
|
||
}
|
||
|
||
get shellProxy() {
|
||
return this._shellProxy;
|
||
}
|
||
|
||
vfunc_activate() {
|
||
this._shellProxy.CheckForUpdatesRemote();
|
||
this._window.present();
|
||
}
|
||
|
||
vfunc_startup() {
|
||
super.vfunc_startup();
|
||
|
||
let provider = new Gtk.CssProvider();
|
||
let uri = 'resource:///org/gnome/shell/css/application.css';
|
||
try {
|
||
provider.load_from_file(Gio.File.new_for_uri(uri));
|
||
} catch (e) {
|
||
logError(e, 'Failed to add application style');
|
||
}
|
||
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
|
||
provider,
|
||
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||
|
||
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
|
||
this._window = new ExtensionsWindow({ application: this });
|
||
}
|
||
|
||
vfunc_command_line(commandLine) {
|
||
let args = commandLine.get_arguments();
|
||
|
||
if (args.length) {
|
||
let uuid = args[0];
|
||
|
||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||
uuid = stripPrefix(uuid, 'extension:///');
|
||
|
||
this._window.openPrefs(uuid);
|
||
} else {
|
||
this.activate();
|
||
}
|
||
return 0;
|
||
}
|
||
});
|
||
|
||
var ExtensionsWindow = GObject.registerClass({
|
||
GTypeName: 'ExtensionsWindow',
|
||
Template: 'resource:///org/gnome/shell/ui/extensions-window.ui',
|
||
InternalChildren: [
|
||
'userList',
|
||
'systemList',
|
||
'killSwitch',
|
||
'mainBox',
|
||
'mainStack',
|
||
'scrolledWindow',
|
||
'updatesBar',
|
||
'updatesLabel',
|
||
],
|
||
}, class ExtensionsWindow extends Gtk.ApplicationWindow {
|
||
_init(params) {
|
||
super._init(params);
|
||
|
||
this._startupUuid = null;
|
||
this._loaded = false;
|
||
this._prefsDialog = null;
|
||
this._updatesCheckId = 0;
|
||
|
||
this._mainBox.set_focus_vadjustment(this._scrolledWindow.vadjustment);
|
||
|
||
let action;
|
||
action = new Gio.SimpleAction({ name: 'show-about' });
|
||
action.connect('activate', this._showAbout.bind(this));
|
||
this.add_action(action);
|
||
|
||
action = new Gio.SimpleAction({ name: 'logout' });
|
||
action.connect('activate', this._logout.bind(this));
|
||
this.add_action(action);
|
||
|
||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||
this._settings.bind('disable-user-extensions',
|
||
this._killSwitch, 'active',
|
||
Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN);
|
||
|
||
this._userList.set_sort_func(this._sortList.bind(this));
|
||
this._userList.set_header_func(this._updateHeader.bind(this));
|
||
|
||
this._systemList.set_sort_func(this._sortList.bind(this));
|
||
this._systemList.set_header_func(this._updateHeader.bind(this));
|
||
|
||
this._shellProxy.connectSignal('ExtensionStateChanged',
|
||
this._onExtensionStateChanged.bind(this));
|
||
|
||
this._scanExtensions();
|
||
}
|
||
|
||
get _shellProxy() {
|
||
return this.application.shellProxy;
|
||
}
|
||
|
||
uninstall(uuid) {
|
||
let row = this._findExtensionRow(uuid);
|
||
|
||
let dialog = new Gtk.MessageDialog({
|
||
transient_for: this,
|
||
modal: true,
|
||
text: _('Remove “%s”?').format(row.name),
|
||
secondary_text: _('If you remove the extension, you need to return to download it if you want to enable it again'),
|
||
});
|
||
|
||
dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
|
||
dialog.add_button(_('Remove'), Gtk.ResponseType.ACCEPT)
|
||
.get_style_context().add_class('destructive-action');
|
||
|
||
dialog.connect('response', (dlg, response) => {
|
||
if (response === Gtk.ResponseType.ACCEPT)
|
||
this._shellProxy.UninstallExtensionRemote(uuid);
|
||
dialog.destroy();
|
||
});
|
||
dialog.present();
|
||
}
|
||
|
||
openPrefs(uuid) {
|
||
if (!this._loaded)
|
||
this._startupUuid = uuid;
|
||
else if (!this._showPrefs(uuid))
|
||
this.present();
|
||
}
|
||
|
||
_showPrefs(uuid) {
|
||
if (this._prefsDialog)
|
||
return false;
|
||
|
||
let row = this._findExtensionRow(uuid);
|
||
if (!row || !row.hasPrefs)
|
||
return false;
|
||
|
||
let widget;
|
||
|
||
try {
|
||
widget = row.prefsModule.buildPrefsWidget();
|
||
} catch (e) {
|
||
widget = this._buildErrorUI(row, e);
|
||
}
|
||
|
||
this._prefsDialog = new Gtk.Window({
|
||
application: this.application,
|
||
default_width: 600,
|
||
default_height: 400,
|
||
modal: this.visible,
|
||
type_hint: Gdk.WindowTypeHint.DIALOG,
|
||
window_position: Gtk.WindowPosition.CENTER,
|
||
});
|
||
|
||
this._prefsDialog.set_titlebar(new Gtk.HeaderBar({
|
||
show_close_button: true,
|
||
title: row.name,
|
||
visible: true,
|
||
}));
|
||
|
||
if (this.visible)
|
||
this._prefsDialog.transient_for = this;
|
||
|
||
this._prefsDialog.connect('destroy', () => {
|
||
this._prefsDialog = null;
|
||
|
||
if (!this.visible)
|
||
this.destroy();
|
||
});
|
||
|
||
this._prefsDialog.add(widget);
|
||
this._prefsDialog.show();
|
||
|
||
return true;
|
||
}
|
||
|
||
_showAbout() {
|
||
let aboutDialog = new Gtk.AboutDialog({
|
||
authors: [
|
||
'Florian Müllner <fmuellner@gnome.org>',
|
||
'Jasper St. Pierre <jstpierre@mecheye.net>',
|
||
'Didier Roche <didrocks@ubuntu.com>',
|
||
],
|
||
translator_credits: _('translator-credits'),
|
||
program_name: _('Extensions'),
|
||
comments: _('Manage your GNOME Extensions'),
|
||
license_type: Gtk.License.GPL_2_0,
|
||
logo_icon_name: 'org.gnome.Extensions',
|
||
version: Config.PACKAGE_VERSION,
|
||
|
||
transient_for: this,
|
||
modal: true,
|
||
});
|
||
aboutDialog.present();
|
||
}
|
||
|
||
_logout() {
|
||
this.application.get_dbus_connection().call(
|
||
'org.gnome.SessionManager',
|
||
'/org/gnome/SessionManager',
|
||
'org.gnome.SessionManager',
|
||
'Logout',
|
||
new GLib.Variant('(u)', [0]),
|
||
null,
|
||
Gio.DBusCallFlags.NONE,
|
||
-1,
|
||
null,
|
||
(o, res) => {
|
||
o.call_finish(res);
|
||
});
|
||
}
|
||
|
||
_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 = '%s\n\nStack trace:\n'.format(exc);
|
||
// Indent stack trace.
|
||
errortext +=
|
||
exc.stack.split('\n').map(line => ' %s'.format(line)).join('\n');
|
||
|
||
let buffer = new Gtk.TextBuffer({ text: errortext });
|
||
let textview = new Gtk.TextView({
|
||
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 %s had an error:'.format(row.uuid),
|
||
'```', // '`' (xgettext throws up on odd number of backticks)
|
||
exc.toString(),
|
||
'```', // '`'
|
||
'',
|
||
'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 !== '',
|
||
});
|
||
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;
|
||
}
|
||
|
||
_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._userList.get_children(),
|
||
...this._systemList.get_children(),
|
||
].find(c => c.uuid === uuid);
|
||
}
|
||
|
||
_onExtensionStateChanged(proxy, senderName, [uuid, newState]) {
|
||
let extension = ExtensionUtils.deserializeExtension(newState);
|
||
let row = this._findExtensionRow(uuid);
|
||
|
||
this._queueUpdatesCheck();
|
||
|
||
// the extension's type changed; remove the corresponding row
|
||
// and reset the variable to null so that we create a new row
|
||
// below and add it to the appropriate list
|
||
if (row && row.type !== extension.type) {
|
||
row.destroy();
|
||
row = null;
|
||
}
|
||
|
||
if (row) {
|
||
if (extension.state === ExtensionState.UNINSTALLED)
|
||
row.destroy();
|
||
return; // we only deal with new and deleted extensions here
|
||
}
|
||
this._addExtensionRow(extension);
|
||
}
|
||
|
||
_scanExtensions() {
|
||
this._shellProxy.ListExtensionsRemote(([extensionsMap], e) => {
|
||
if (e) {
|
||
if (e instanceof Gio.DBusError) {
|
||
log('Failed to connect to shell proxy: %s'.format(e.toString()));
|
||
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.show_all();
|
||
|
||
if (row.type === ExtensionType.PER_USER)
|
||
this._userList.add(row);
|
||
else
|
||
this._systemList.add(row);
|
||
}
|
||
|
||
_queueUpdatesCheck() {
|
||
if (this._updatesCheckId)
|
||
return;
|
||
|
||
this._updatesCheckId = GLib.timeout_add_seconds(
|
||
GLib.PRIORITY_DEFAULT, 1, () => {
|
||
this._checkUpdates();
|
||
|
||
this._updatesCheckId = 0;
|
||
return GLib.SOURCE_REMOVE;
|
||
});
|
||
}
|
||
|
||
_checkUpdates() {
|
||
let nUpdates = this._userList.get_children().filter(c => c.hasUpdate).length;
|
||
|
||
this._updatesLabel.label = Gettext.ngettext(
|
||
'%d extension will be updated on next login.',
|
||
'%d extensions will be updated on next login.',
|
||
nUpdates).format(nUpdates);
|
||
this._updatesBar.visible = nUpdates > 0;
|
||
}
|
||
|
||
_extensionsLoaded() {
|
||
this._userList.visible = this._userList.get_children().length > 0;
|
||
this._systemList.visible = this._systemList.get_children().length > 0;
|
||
|
||
if (this._userList.visible || this._systemList.visible)
|
||
this._mainStack.visible_child_name = 'main';
|
||
else
|
||
this._mainStack.visible_child_name = 'placeholder';
|
||
|
||
this._checkUpdates();
|
||
|
||
if (this._startupUuid)
|
||
this._showPrefs(this._startupUuid);
|
||
this._startupUuid = null;
|
||
this._loaded = true;
|
||
}
|
||
});
|
||
|
||
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 ExtensionRow = GObject.registerClass({
|
||
GTypeName: 'ExtensionRow',
|
||
Template: 'resource:///org/gnome/shell/ui/extension-row.ui',
|
||
InternalChildren: [
|
||
'nameLabel',
|
||
'descriptionLabel',
|
||
'versionLabel',
|
||
'authorLabel',
|
||
'updatesIcon',
|
||
'revealButton',
|
||
'revealer',
|
||
],
|
||
}, class ExtensionRow extends Gtk.ListBoxRow {
|
||
_init(extension) {
|
||
super._init();
|
||
|
||
this._app = Gio.Application.get_default();
|
||
this._extension = extension;
|
||
this._prefsModule = null;
|
||
|
||
this._actionGroup = new Gio.SimpleActionGroup();
|
||
this.insert_action_group('row', this._actionGroup);
|
||
|
||
let action;
|
||
action = new Gio.SimpleAction({
|
||
name: 'show-prefs',
|
||
enabled: this.hasPrefs,
|
||
});
|
||
action.connect('activate', () => this.get_toplevel().openPrefs(this.uuid));
|
||
this._actionGroup.add_action(action);
|
||
|
||
action = new Gio.SimpleAction({
|
||
name: 'show-url',
|
||
enabled: this.url !== '',
|
||
});
|
||
action.connect('activate', () => {
|
||
Gio.AppInfo.launch_default_for_uri(
|
||
this.url, this.get_display().get_app_launch_context());
|
||
});
|
||
this._actionGroup.add_action(action);
|
||
|
||
action = new Gio.SimpleAction({
|
||
name: 'uninstall',
|
||
enabled: this.type === ExtensionType.PER_USER,
|
||
});
|
||
action.connect('activate', () => this.get_toplevel().uninstall(this.uuid));
|
||
this._actionGroup.add_action(action);
|
||
|
||
action = new Gio.SimpleAction({
|
||
name: 'enabled',
|
||
state: new GLib.Variant('b', false),
|
||
});
|
||
action.connect('activate', () => {
|
||
let state = action.get_state();
|
||
action.change_state(new GLib.Variant('b', !state.get_boolean()));
|
||
});
|
||
action.connect('change-state', (a, state) => {
|
||
if (state.get_boolean())
|
||
this._app.shellProxy.EnableExtensionRemote(this.uuid);
|
||
else
|
||
this._app.shellProxy.DisableExtensionRemote(this.uuid);
|
||
});
|
||
this._actionGroup.add_action(action);
|
||
|
||
let name = GLib.markup_escape_text(this.name, -1);
|
||
this._nameLabel.label = name;
|
||
|
||
let desc = this._extension.metadata.description.split('\n')[0];
|
||
this._descriptionLabel.label = desc;
|
||
|
||
this._revealButton.connect('clicked', () => {
|
||
this._revealer.reveal_child = !this._revealer.reveal_child;
|
||
});
|
||
this._revealer.connect('notify::reveal-child', () => {
|
||
if (this._revealer.reveal_child)
|
||
this._revealButton.get_style_context().add_class('expanded');
|
||
else
|
||
this._revealButton.get_style_context().remove_class('expanded');
|
||
});
|
||
|
||
this.connect('destroy', this._onDestroy.bind(this));
|
||
|
||
this._extensionStateChangedId = this._app.shellProxy.connectSignal(
|
||
'ExtensionStateChanged', (p, sender, [uuid, newState]) => {
|
||
if (this.uuid !== uuid)
|
||
return;
|
||
|
||
this._extension = ExtensionUtils.deserializeExtension(newState);
|
||
this._updateState();
|
||
});
|
||
this._updateState();
|
||
}
|
||
|
||
get uuid() {
|
||
return this._extension.uuid;
|
||
}
|
||
|
||
get name() {
|
||
return this._extension.metadata.name;
|
||
}
|
||
|
||
get hasPrefs() {
|
||
return this._extension.hasPrefs;
|
||
}
|
||
|
||
get hasUpdate() {
|
||
return this._extension.hasUpdate || false;
|
||
}
|
||
|
||
get type() {
|
||
return this._extension.type;
|
||
}
|
||
|
||
get creator() {
|
||
return this._extension.metadata.creator || '';
|
||
}
|
||
|
||
get url() {
|
||
return this._extension.metadata.url || '';
|
||
}
|
||
|
||
get version() {
|
||
return this._extension.metadata.version || '';
|
||
}
|
||
|
||
_updateState() {
|
||
let state = this._extension.state === ExtensionState.ENABLED;
|
||
|
||
let action = this._actionGroup.lookup('enabled');
|
||
action.set_state(new GLib.Variant('b', state));
|
||
action.enabled = this._canToggle();
|
||
|
||
this._updatesIcon.visible = this.hasUpdate;
|
||
|
||
this._versionLabel.label = this.version;
|
||
this._versionLabel.visible = this.version !== '';
|
||
|
||
this._authorLabel.label = this.creator;
|
||
this._authorLabel.visible = this.creator !== '';
|
||
}
|
||
|
||
_onDestroy() {
|
||
if (!this._app.shellProxy)
|
||
return;
|
||
|
||
if (this._extensionStateChangedId)
|
||
this._app.shellProxy.disconnectSignal(this._extensionStateChangedId);
|
||
this._extensionStateChangedId = 0;
|
||
}
|
||
|
||
_canToggle() {
|
||
return this._extension.canChange;
|
||
}
|
||
|
||
get prefsModule() {
|
||
// give extension prefs access to their own extension object
|
||
ExtensionUtils.getCurrentExtension = () => this._extension;
|
||
|
||
if (!this._prefsModule) {
|
||
ExtensionUtils.installImporter(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'.format(s));
|
||
},
|
||
|
||
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell']),
|
||
};
|
||
|
||
String.prototype.format = Format.format;
|
||
}
|
||
|
||
function main(argv) {
|
||
initEnvironment();
|
||
|
||
new Application().run(argv);
|
||
}
|