summaryrefslogtreecommitdiffstats
path: root/js/dbusServices/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'js/dbusServices/extensions')
-rw-r--r--js/dbusServices/extensions/css/application.css2
-rw-r--r--js/dbusServices/extensions/extensionsService.js274
-rw-r--r--js/dbusServices/extensions/main.js20
-rw-r--r--js/dbusServices/extensions/ui/extension-prefs-dialog.ui197
4 files changed, 493 insertions, 0 deletions
diff --git a/js/dbusServices/extensions/css/application.css b/js/dbusServices/extensions/css/application.css
new file mode 100644
index 0000000..06914ea
--- /dev/null
+++ b/js/dbusServices/extensions/css/application.css
@@ -0,0 +1,2 @@
+.expander-frame > * { border-top-width: 0; }
+.expander-toolbar { border: 0 solid @borders; border-top-width: 1px; }
diff --git a/js/dbusServices/extensions/extensionsService.js b/js/dbusServices/extensions/extensionsService.js
new file mode 100644
index 0000000..9d444c5
--- /dev/null
+++ b/js/dbusServices/extensions/extensionsService.js
@@ -0,0 +1,274 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported ExtensionsService */
+
+const { Gdk, Gio, GLib, GObject, Gtk, Shew } = imports.gi;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+const { ServiceImplementation } = imports.dbusService;
+
+const ExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions');
+const ExtensionsProxy = Gio.DBusProxy.makeProxyWrapper(ExtensionsIface);
+
+var ExtensionsService = class extends ServiceImplementation {
+ constructor() {
+ super(ExtensionsIface, '/org/gnome/Shell/Extensions');
+
+ this._proxy = new ExtensionsProxy(Gio.DBus.session,
+ 'org.gnome.Shell', '/org/gnome/Shell');
+
+ this._proxy.connectSignal('ExtensionStateChanged',
+ (proxy, sender, params) => {
+ this._dbusImpl.emit_signal('ExtensionStateChanged',
+ new GLib.Variant('(sa{sv})', params));
+ });
+
+ this._proxy.connect('g-properties-changed', () => {
+ this._dbusImpl.emit_property_changed('UserExtensionsEnabled',
+ new GLib.Variant('b', this._proxy.UserExtensionsEnabled));
+ });
+ }
+
+ get ShellVersion() {
+ return this._proxy.ShellVersion;
+ }
+
+ get UserExtensionsEnabled() {
+ return this._proxy.UserExtensionsEnabled;
+ }
+
+ set UserExtensionsEnabled(enable) {
+ this._proxy.UserExtensionsEnabled = enable;
+ }
+
+ ListExtensionsAsync(params, invocation) {
+ this._proxy.ListExtensionsRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(a{sa{sv}})', res));
+ });
+ }
+
+ GetExtensionInfoAsync(params, invocation) {
+ this._proxy.GetExtensionInfoRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(a{sv})', res));
+ });
+ }
+
+ GetExtensionErrorsAsync(params, invocation) {
+ this._proxy.GetExtensionErrorsRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(as)', res));
+ });
+ }
+
+ InstallRemoteExtensionAsync(params, invocation) {
+ this._proxy.InstallRemoteExtensionRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(s)', res));
+ });
+ }
+
+ UninstallExtensionAsync(params, invocation) {
+ this._proxy.UninstallExtensionRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(b)', res));
+ });
+ }
+
+ EnableExtensionAsync(params, invocation) {
+ this._proxy.EnableExtensionRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(b)', res));
+ });
+ }
+
+ DisableExtensionAsync(params, invocation) {
+ this._proxy.DisableExtensionRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(new GLib.Variant('(b)', res));
+ });
+ }
+
+ LaunchExtensionPrefsAsync([uuid], invocation) {
+ this.OpenExtensionPrefsAsync([uuid, '', {}], invocation);
+ }
+
+ OpenExtensionPrefsAsync(params, invocation) {
+ const [uuid, parentWindow, options] = params;
+
+ this._proxy.GetExtensionInfoRemote(uuid, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ const [serialized] = res;
+ const extension = ExtensionUtils.deserializeExtension(serialized);
+
+ const window = new ExtensionPrefsDialog(extension);
+ window.realize();
+
+ let externalWindow = null;
+
+ if (parentWindow)
+ externalWindow = Shew.ExternalWindow.new_from_handle(parentWindow);
+
+ if (externalWindow)
+ externalWindow.set_parent_of(window.window);
+
+ if (options.modal)
+ window.modal = options.modal.get_boolean();
+
+ window.connect('destroy', () => this.release());
+ this.hold();
+
+ window.show();
+
+ invocation.return_value(null);
+ });
+ }
+
+ CheckForUpdatesAsync(params, invocation) {
+ this._proxy.CheckForUpdatesRemote(...params, (res, error) => {
+ if (this._handleError(invocation, error))
+ return;
+
+ invocation.return_value(null);
+ });
+ }
+};
+
+var ExtensionPrefsDialog = GObject.registerClass({
+ GTypeName: 'ExtensionPrefsDialog',
+ Template: 'resource:///org/gnome/Shell/Extensions/ui/extension-prefs-dialog.ui',
+ InternalChildren: [
+ 'headerBar',
+ 'stack',
+ 'expander',
+ 'expanderArrow',
+ 'revealer',
+ 'errorView',
+ ],
+}, class ExtensionPrefsDialog extends Gtk.Window {
+ _init(extension) {
+ super._init();
+
+ this._uuid = extension.uuid;
+ this._url = extension.metadata.url || '';
+
+ this._headerBar.title = extension.metadata.name;
+
+ this._actionGroup = new Gio.SimpleActionGroup();
+ this.insert_action_group('win', this._actionGroup);
+
+ this._initActions();
+ this._addCustomStylesheet();
+
+ this._gesture = new Gtk.GestureMultiPress({
+ widget: this._expander,
+ 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', () => {
+ this._expanderArrow.icon_name = this._revealer.reveal_child
+ ? 'pan-down-symbolic'
+ : 'pan-end-symbolic';
+ });
+
+ try {
+ ExtensionUtils.installImporter(extension);
+
+ // give extension prefs access to their own extension object
+ ExtensionUtils.getCurrentExtension = () => extension;
+
+ const prefsModule = extension.imports.prefs;
+ prefsModule.init(extension.metadata);
+
+ const widget = prefsModule.buildPrefsWidget();
+ this._stack.add(widget);
+ this._stack.visible_child = widget;
+ } catch (e) {
+ this._setError(e);
+ }
+ }
+
+ _setError(exc) {
+ this._errorView.buffer.text = `${exc}\n\nStack trace:\n`;
+ // Indent stack trace.
+ this._errorView.buffer.text +=
+ exc.stack.split('\n').map(line => ` ${line}`).join('\n');
+
+ // markdown for pasting in gitlab issues
+ let lines = [
+ `The settings of extension ${this._uuid} had an error:`,
+ '```',
+ `${exc}`,
+ '```',
+ '',
+ 'Stack trace:',
+ '```',
+ exc.stack.replace(/\n$/, ''), // stack without trailing newline
+ '```',
+ '',
+ ];
+ this._errorMarkdown = lines.join('\n');
+ this._actionGroup.lookup('copy-error').enabled = true;
+ }
+
+ _initActions() {
+ let action;
+
+ action = new Gio.SimpleAction({
+ name: 'copy-error',
+ enabled: false,
+ });
+ action.connect('activate', () => {
+ const clipboard = Gtk.Clipboard.get_default(this.get_display());
+ clipboard.set_text(this._errorMarkdown, -1);
+ });
+ 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);
+ }
+
+ _addCustomStylesheet() {
+ let provider = new Gtk.CssProvider();
+ let uri = 'resource:///org/gnome/Shell/Extensions/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);
+ }
+});
diff --git a/js/dbusServices/extensions/main.js b/js/dbusServices/extensions/main.js
new file mode 100644
index 0000000..9cc4bc5
--- /dev/null
+++ b/js/dbusServices/extensions/main.js
@@ -0,0 +1,20 @@
+/* exported main */
+
+imports.gi.versions.Gdk = '3.0';
+imports.gi.versions.Gtk = '3.0';
+
+const { Gtk } = imports.gi;
+const pkg = imports.package;
+
+const { DBusService } = imports.dbusService;
+const { ExtensionsService } = imports.extensionsService;
+
+function main() {
+ Gtk.init(null);
+ pkg.initFormat();
+
+ const service = new DBusService(
+ 'org.gnome.Shell.Extensions',
+ new ExtensionsService());
+ service.run();
+}
diff --git a/js/dbusServices/extensions/ui/extension-prefs-dialog.ui b/js/dbusServices/extensions/ui/extension-prefs-dialog.ui
new file mode 100644
index 0000000..fc08eae
--- /dev/null
+++ b/js/dbusServices/extensions/ui/extension-prefs-dialog.ui
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="ExtensionPrefsDialog" parent="GtkWindow">
+ <property name="default_width">600</property>
+ <property name="default_height">400</property>
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="headerBar">
+ <property name="visible">True</property>
+ <property name="show_close_button">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="propagate_natural_height">True</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="margin">100</property>
+ <property name="margin_bottom">60</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Something’s gone wrong</property>
+ <attributes>
+ <attribute name="scale" value="1.44"/> <!-- x-large -->
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">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.</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="margin_top">12</property>
+ <child>
+ <object class="GtkFrame" id="expander">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkEventBox">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="margin">12</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="expanderArrow">
+ <property name="visible">True</property>
+ <property name="icon_name">pan-end-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Technical Details</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="shadow_type">in</property>
+ <style>
+ <class name="expander-frame"/>
+ </style>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkTextView" id="errorView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="monospace">True</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="left_margin">12</property>
+ <property name="right_margin">12</property>
+ <property name="top_margin">12</property>
+ <property name="bottom_margin">12</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToolbar">
+ <property name="visible">True</property>
+ <style>
+ <class name="expander-toolbar"/>
+ </style>
+ <child>
+ <object class="GtkToolItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.copy-error</property>
+ <style>
+ <class name="flat"/>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon_name">edit-copy-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem">
+ <property name="visible">True</property>
+ <property name="draw">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="homeButton">
+ <property name="visible"
+ bind-source="homeButton"
+ bind-property="sensitive"
+ bind-flags="sync-create"/>
+ <property name="label" translatable="yes">Homepage</property>
+ <property name="tooltip_text" translatable="yes">Visit extension homepage</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="no_show_all">True</property>
+ <property name="action_name">win.show-url</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>