[gnome-shell] Add a new tool, 'gnome-shell-extension-prefs', which can configure extensions



commit b8a54faf941976cfd59ae5d9f922d9828f3cd06c
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Wed Jan 18 21:21:56 2012 -0500

    Add a new tool, 'gnome-shell-extension-prefs', which can configure extensions
    
    A new tool, 'gnome-shell-extension-prefs' can load a new entry point from
    extensions, 'prefs.js', which has an entry point to return a GTK+ widget.
    This allows extensions to have their own preferences dialog, without each
    extension needing to ship its own Python script and .desktop file.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=668429

 .gitignore                                     |    3 +
 data/Makefile.am                               |    4 +-
 data/gnome-shell-extension-prefs.desktop.in.in |   12 +
 js/Makefile.am                                 |    1 +
 js/extensionPrefs/main.js                      |  280 ++++++++++++++++++++++++
 js/misc/config.js.in                           |    4 +
 src/Makefile.am                                |   11 +-
 src/gnome-shell-extension-prefs.in             |   15 ++
 8 files changed, 326 insertions(+), 4 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 4ec7bad..f6dc70b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,8 @@ config
 configure
 data/gnome-shell.desktop
 data/gnome-shell.desktop.in
+data/gnome-shell-extension-prefs.desktop
+data/gnome-shell-extension-prefs.desktop.in
 data/gschemas.compiled
 data/org.gnome.shell.gschema.xml
 data/org.gnome.shell.gschema.valid
@@ -62,6 +64,7 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
 src/gnome-shell
 src/gnome-shell-calendar-server
 src/gnome-shell-extension-tool
+src/gnome-shell-extension-prefs
 src/gnome-shell-hotplug-sniffer
 src/gnome-shell-jhbuild
 src/gnome-shell-perf-helper
diff --git a/data/Makefile.am b/data/Makefile.am
index 04c8f4f..c077aa2 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,5 +1,5 @@
 desktopdir=$(datadir)/applications
-desktop_DATA = gnome-shell.desktop
+desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 
 # We substitute in bindir so it works as an autostart
 # file when built in a non-system prefix
@@ -69,6 +69,7 @@ shaders_DATA = \
 
 EXTRA_DIST =						\
 	gnome-shell.desktop.in.in			\
+	gnome-shell-extension-prefs.in.in		\
 	$(menu_DATA)					\
 	$(shaders_DATA)					\
 	$(convert_DATA)					\
@@ -76,6 +77,7 @@ EXTRA_DIST =						\
 
 CLEANFILES =						\
 	gnome-shell.desktop.in				\
+	gnome-shell-extension-prefs.in			\
 	$(desktop_DATA)					\
 	$(gsettings_SCHEMAS)				\
 	gschemas.compiled
diff --git a/data/gnome-shell-extension-prefs.desktop.in.in b/data/gnome-shell-extension-prefs.desktop.in.in
new file mode 100644
index 0000000..9f8bbf4
--- /dev/null
+++ b/data/gnome-shell-extension-prefs.desktop.in.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Type=Application
+_Name=GNOME Shell Extension Preferences
+_Comment=Configure GNOME Shell Extensions
+Exec= bindir@/gnome-shell-extension-prefs %u
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-shell
+X-GNOME-Bugzilla-Component=extensions
+X-GNOME-Bugzilla-Version= VERSION@
+Categories=GNOME;GTK;
+OnlyShowIn=GNOME;
+NoDisplay=true
diff --git a/js/Makefile.am b/js/Makefile.am
index f7ce7cd..4c41bff 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -7,6 +7,7 @@ nobase_dist_js_DATA = 	\
 	gdm/fingerprint.js	\
 	gdm/loginDialog.js	\
 	gdm/powerMenu.js	\
+	extensionPrefs/main.js	\
 	misc/config.js		\
 	misc/docInfo.js		\
 	misc/extensionUtils.js	\
diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js
new file mode 100644
index 0000000..82b67b4
--- /dev/null
+++ b/js/extensionPrefs/main.js
@@ -0,0 +1,280 @@
+
+const Lang = imports.lang;
+const Gettext = imports.gettext;
+const GLib = imports.gi.GLib;
+const GObject = imports.gi.GObject;
+const Gio = imports.gi.Gio;
+const Gtk = imports.gi.Gtk;
+const Pango = imports.gi.Pango;
+
+const _ = Gettext.gettext;
+
+const Config = imports.misc.config;
+const Format = imports.misc.format;
+const ExtensionUtils = imports.misc.extensionUtils;
+
+
+const GnomeShellIface = <interface name="org.gnome.Shell">
+<signal name="ExtensionStatusChanged">
+    <arg type="s" name="uuid"/>
+    <arg type="i" name="state"/>
+    <arg type="s" name="error"/>
+</signal>
+</interface>;
+
+const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
+
+function stripPrefix(string, prefix) {
+    if (string.slice(0, prefix.length) == prefix)
+        return string.slice(prefix.length);
+    return string;
+}
+
+const Application = new Lang.Class({
+    Name: 'Application',
+    _init: function() {
+        GLib.set_prgname('gnome-shell-extension-prefs');
+        this.application = new Gtk.Application({
+            application_id: 'org.gnome.shell.ExtensionPrefs',
+            flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
+        });
+
+        this.application.connect('activate', Lang.bind(this, this._onActivate));
+        this.application.connect('command-line', Lang.bind(this, this._onCommandLine));
+        this.application.connect('startup', Lang.bind(this, this._onStartup));
+
+        this._extensionMetas = {};
+        this._extensionPrefsModules = {};
+
+        this._extensionIters = {};
+    },
+
+    _buildModel: function() {
+        this._model = new Gtk.ListStore();
+        this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
+    },
+
+    _extensionAvailable: function(uuid) {
+        let meta = this._extensionMetas[uuid];
+
+        if (!meta)
+            return false;
+
+        if (ExtensionUtils.isOutOfDate(meta))
+            return false;
+
+        if (!meta.dir.get_child('prefs.js').query_exists(null))
+            return false;
+
+        return true;
+    },
+
+    _setExtensionInsensitive: function(layout, cell, model, iter, data) {
+        let uuid = model.get_value(iter, 0);
+        if (!this._extensionAvailable(uuid))
+            cell.set_sensitive(false);
+    },
+
+    _getExtensionPrefsModule: function(meta) {
+        if (this._extensionPrefsModules.hasOwnProperty(meta.uuid))
+            return this._extensionPrefsModules[meta.uuid];
+
+        ExtensionUtils.installImporter(meta);
+
+        let prefsModule = meta.importer.prefs;
+        prefsModule.init(meta);
+
+        this._extensionPrefsModules[meta.uuid] = prefsModule;
+        return prefsModule;
+    },
+
+    _selectExtension: function(uuid) {
+        if (!this._extensionAvailable(uuid))
+            return;
+
+        let meta = this._extensionMetas[uuid];
+        let widget;
+
+        try {
+            let prefsModule = this._getExtensionPrefsModule(meta);
+            widget = prefsModule.buildPrefsWidget(meta);
+        } catch (e) {
+            widget = this._buildErrorUI(meta, e);
+        }
+
+        // Destroy the current prefs widget, if it exists
+        if (this._extensionPrefsBin.get_child())
+            this._extensionPrefsBin.get_child().destroy();
+
+        this._extensionPrefsBin.add(widget);
+        this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
+    },
+
+    _extensionSelected: function() {
+        let [success, iter] = this._extensionSelector.get_active_iter();
+        if (!success)
+            return;
+
+        let uuid = this._model.get_value(iter, 0);
+        this._selectExtension(uuid);
+    },
+
+    _buildErrorUI: function(meta, exc) {
+        let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+        let label = new Gtk.Label({
+            label: _("There was an error loading the preferences dialog for %s:").format(meta.name)
+        });
+        box.add(label);
+
+        let errortext = '';
+        errortext += exc;
+        errortext += '\n\n';
+        errortext += 'Stack trace:\n';
+
+        // Indent stack trace.
+        errortext += exc.stack.split('\n').map(function(line) {
+            return '  ' + line;
+        }).join('\n');
+
+        let scroll = new Gtk.ScrolledWindow({ vexpand: true });
+        let buffer = new Gtk.TextBuffer({ text: errortext });
+        let textview = new Gtk.TextView({ buffer: buffer });
+        textview.override_font(Pango.font_description_from_string('monospace'));
+        scroll.add(textview);
+        box.add(scroll);
+
+        box.show_all();
+        return box;
+    },
+
+    _buildUI: function(app) {
+        this._window = new Gtk.ApplicationWindow({ application: app,
+                                                   window_position: Gtk.WindowPosition.CENTER,
+                                                   title: _("GNOME Shell Extension Preferences") });
+
+        this._window.set_size_request(600, 400);
+
+        let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+        this._window.add(vbox);
+
+        let toolbar = new Gtk.Toolbar();
+        toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
+        vbox.add(toolbar);
+        let toolitem;
+
+        let label = new Gtk.Label({ label: _("<b>Extension</b>"),
+                                    use_markup: true });
+        toolitem = new Gtk.ToolItem({ child: label });
+        toolbar.add(toolitem);
+
+        this._extensionSelector = new Gtk.ComboBox({ model: this._model,
+                                                     margin_left: 8,
+                                                     hexpand: true });
+        this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
+
+        let renderer = new Gtk.CellRendererText();
+        this._extensionSelector.pack_start(renderer, true);
+        this._extensionSelector.add_attribute(renderer, 'text', 1);
+        this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
+        this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
+
+        toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
+        toolitem.set_expand(true);
+        toolbar.add(toolitem);
+
+        this._extensionPrefsBin = new Gtk.Frame();
+        vbox.add(this._extensionPrefsBin);
+
+        let label = new Gtk.Label({
+            label: _("Select an extension to configure using the combobox above."),
+            vexpand: true
+        });
+
+        this._extensionPrefsBin.add(label);
+
+        this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
+        this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
+            if (!this._extensionMetas.hasOwnProperty(uuid))
+                this._scanExtensions();
+        }));
+
+        this._window.show_all();
+    },
+
+    _scanExtensions: function() {
+        ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
+            if (this._extensionMetas.hasOwnProperty(uuid))
+                return;
+
+            let meta;
+            try {
+                meta = ExtensionUtils.loadMetadata(uuid, dir, type);
+            } catch(e) {
+                global.logError('' + e);
+                return;
+            }
+
+            this._extensionMetas[uuid] = meta;
+
+            let iter = this._model.append();
+            this._model.set(iter, [0, 1], [uuid, meta.name]);
+            this._extensionIters[uuid] = iter;
+        }));
+    },
+
+    _onActivate: function() {
+        this._window.present();
+    },
+
+    _onStartup: function(app) {
+        this._buildModel();
+        this._buildUI(app);
+        this._scanExtensions();
+    },
+
+    _onCommandLine: function(app, commandLine) {
+        app.activate();
+        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:///");
+
+            if (!this._extensionAvailable(uuid))
+                return 1;
+
+            this._selectExtension(uuid);
+        }
+        return 0;
+    }
+});
+
+function initEnvironment() {
+    // Monkey-patch in a "global" object that fakes some Shell utilities
+    // that ExtensionUtils depends on.
+    window.global = {
+        log: function() {
+            print([].join.call(arguments, ', '));
+        },
+
+        logError: function(s) {
+            global.log('ERROR: ' + s);
+        },
+
+        userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
+    };
+
+    String.prototype.format = Format.format;
+}
+
+function main(argv) {
+    initEnvironment();
+    ExtensionUtils.init();
+
+    Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
+    Gettext.textdomain(Config.GETTEXT_PACKAGE);
+
+    let app = new Application();
+    app.application.run(argv);
+}
diff --git a/js/misc/config.js.in b/js/misc/config.js.in
index a35fe28..6cbf932 100644
--- a/js/misc/config.js.in
+++ b/js/misc/config.js.in
@@ -10,3 +10,7 @@ const GJS_VERSION = '@GJS_VERSION@';
 const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
 /* The system TLS CA list */
 const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
+/* gettext package */
+const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
+/* locale dir */
+const LOCALEDIR = '@datadir@/locale';
diff --git a/src/Makefile.am b/src/Makefile.am
index a46d779..464831a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -27,8 +27,8 @@ CLEANFILES += $(service_DATA)
 
 CLEANFILES += $(gir_DATA) $(typelib_DATA)
 
-bin_SCRIPTS += gnome-shell-extension-tool
-EXTRA_DIST += gnome-shell-extension-tool.in
+bin_SCRIPTS += gnome-shell-extension-tool gnome-shell-extension-prefs
+EXTRA_DIST += gnome-shell-extension-tool.in gnome-shell-extension-prefs.in
 bin_PROGRAMS = gnome-shell-real
 
 if USE_JHBUILD_WRAPPER_SCRIPT
@@ -52,11 +52,13 @@ generated_script_substitutions = \
 	    -e "s|@datadir[ ]|$(datadir)|g" \
 	    -e "s|@libexecdir[ ]|$(libexecdir)|g" \
 	    -e "s|@libdir[ ]|$(libdir)|g" \
+	    -e "s|@pkglibdir[ ]|$(pkglibdir)|g" \
 	    -e "s|@JHBUILD_TYPELIBDIR[ ]|$(JHBUILD_TYPELIBDIR)|g" \
 	    -e "s|@pkgdatadir[ ]|$(pkgdatadir)|g" \
 	    -e "s|@PYTHON[ ]|$(PYTHON)|g" \
 	    -e "s|@VERSION[ ]|$(VERSION)|g" \
-	    -e "s|@sysconfdir[ ]|$(sysconfdir)|g"
+	    -e "s|@sysconfdir[ ]|$(sysconfdir)|g" \
+	    -e "s|@GJS_CONSOLE[ ]|$(GJS_CONSOLE)|g"
 
 gnome-shell-jhbuild: gnome-shell-jhbuild.in gnome-shell-real Makefile
 	$(AM_V_GEN) sed $(generated_script_substitutions) $< > $  tmp && mv $  tmp $@ && chmod a+x $@
@@ -66,6 +68,9 @@ gnome-shell-jhbuild: gnome-shell-jhbuild.in gnome-shell-real Makefile
 gnome-shell-extension-tool: gnome-shell-extension-tool.in Makefile
 	$(AM_V_GEN) sed $(generated_script_substitutions) $< > $  tmp && mv $  tmp $@ && chmod a+x $@
 
+gnome-shell-extension-prefs: gnome-shell-extension-prefs.in Makefile
+	$(AM_V_GEN) sed $(generated_script_substitutions) $< > $  tmp && mv $  tmp $@ && chmod a+x $@
+
 CLEANFILES += gnome-shell $(bin_SCRIPTS)
 
 include Makefile-st.am
diff --git a/src/gnome-shell-extension-prefs.in b/src/gnome-shell-extension-prefs.in
new file mode 100644
index 0000000..fa37254
--- /dev/null
+++ b/src/gnome-shell-extension-prefs.in
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+if [ -n "$GI_TYPELIB_PATH" ]; then
+    export GI_TYPELIB_PATH= pkglibdir@
+else
+    export GI_TYPELIB_PATH= pkglibdir@:$GI_TYPELIB_PATH
+fi
+
+if [ -n "$LD_LIBRARY_PATH" ] ; then
+    export LD_LIBRARY_PATH= pkglibdir@
+else
+    export LD_LIBRARY_PATH= pkglibdir@:$LD_LIBRARY_PATH
+fi
+
+ GJS_CONSOLE@ -I @pkgdatadir@/js -c "const Main = imports.extensionPrefs.main; Main.main(ARGV);" "$@"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]