diff options
Diffstat (limited to 'js/dbusServices/extensions')
-rw-r--r-- | js/dbusServices/extensions/css/application.css | 2 | ||||
-rw-r--r-- | js/dbusServices/extensions/extensionsService.js | 274 | ||||
-rw-r--r-- | js/dbusServices/extensions/main.js | 20 | ||||
-rw-r--r-- | js/dbusServices/extensions/ui/extension-prefs-dialog.ui | 197 |
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> |