Commits

mathematicalcoffee committed bb6a7d0 Merge

merge of default to polyglot-dev

Comments (0)

Files changed (4)

 9e202b75e17c3061b26ff386dfe0b9bc22f5d292 v2.0.1_gnome3.4
 fee52a2db1653b7807232f9126a8cb4fa0afceb8 v7_e.g.o
 e0cb983245d4c060d341cbb2b847c5787b0f2cf7 v9_e.g.o
+ecfc95017a3c43d0ef09f21ea0ec245178902571 v2.1
 In this case, I recommend either remembering your system's keyboard shortcut for un/maximising a window (e.g. Alt+F10 on Fedora), or use the [Window Options GNOME shell extension](https://bitbucket.org/mathematicalcoffee/window-options-gnome-shell-extension) which adds a drop-down menu to the title bar in the top panel with these options (shameless plug, I wrote that one). You might also like the [Window Buttons extension](https://github.com/biox/Gnome-Shell-Window-Buttons-Extension) which adds the close, minimize, maximize buttons to the top panel.
 
 ### Changelog (see `Changelog` file for further details):
-v4 (e.g.o), v1.3 (tagged):
 
-* added blacklists/whitelists
-* fixed bug where Maximus wouldn't work on windows with non EN-utf8 characters in the title (bug #4)
-* made it harder to get stuck in fullscreen mode (particularly thunderbird)
-* better behaviour when maximizing from fullscreen to halfscreen.
+* v10 (e.g.o), v2.2 (tagged):
+ + changed the default method to hide the titlebar to hopefully be more stable, if the user is not using the Ambiance or Radiance themes (the old `set_hide_titlebar` bookmark)
+ + much code cleaning
+ + various misc. fixes.
+* v4 (e.g.o), v1.3 (tagged):
+ + added blacklists/whitelists
+ + fixed bug where Maximus wouldn't work on windows with non EN-utf8 characters in the title (bug #4)
+ + made it harder to get stuck in fullscreen mode (particularly thunderbird)
+ + better behaviour when maximizing from fullscreen to halfscreen.
 
 Written 2012 by mathematical.coffee [mathematical.coffee@gmail.com](mailto:mathematical.coffee@gmail.com?subject=maximus%20question).   
 Project webpage: [at  bitbucket](https://bitbucket.org/mathematicalcoffee/maximus-gnome-shell-extension).
 
 # Branch Info (for developers)
 
-* 'Stable' branch works with GNOME 3.2 and GNOME 3.4. No fancy gsettings or UI for setting options (this extension doesn't have options for the moment).
+* 'stable' branch works with GNOME 3.2+. No fancy prefs widget.
 * 'gnome3.4' branch: GNOME3.4+ with prefs widget.
-* 'default' branch: I plan to make this the gnome3.4+ development branch.
-* 'polyglot-dev' branch/'set_hide_titlebar' bookmark: does the decoration/undecoration by setting the `_GDK_HIDE_TITLEBAR_WHEN_MAXIMIZED` window hint. Compatible with 3.2 to 3.6 (although no prefs widget yet). It seems less bug-prone than the normal branch, but **will not work** with the Unity themes (Ambiance, Radiance) which do not respect the hint. Also, the `undecorateHalfMaximised` option is permanently `true`, no way to fix it. (There is an option to turn the set_hide_titlebar method on and off).
+* 'default' branch: the gnome3.4+ development branch (has prefs.js). i.e. dev branch for 'gnome3.4'.
+* 'polyglot-dev' branch: the gnome3.2+ development branch (same as above but no prefs.js). i.e. dev branch for 'stable'.

maximus@mathematical.coffee.gmail.com/extension.js

-/*global global */
+/*global global, log */ // <-- jshint
 /*jshint maxlen: 150 */
-/*
+/* @overview
  * Maximus v2.1
  * Amy Chan <mathematical.coffee@gmail.com>
  * Other contributors:
  * refresh your memory on your system's keyboard shortcut for unmaximising a window
  * (for me it's Ctrl + Super + Down to unmaximise, Ctrl + Super + Up to maximise).
  *
- * Small idiosyncracies:
+ * ## Small idiosyncracies:
  * Note - these are simple enough for me to implement so if enough people let
  * me know that they want this behaviour, I'll do it.
  *
  * * the original Maximus also maximised all windows on startup.
  *   This doesn't (it was annoying).
  *
- * Help! It didn't work/I found a bug!
+ * ## Help! It didn't work/I found a bug!
+ *
  * 1. Make sure you can *reproduce* the bug reliably.
  * 2. Do 'Ctrl + F2' and 'lg' and see if there are any errors produced by Maximus,
  *    both in the 'Errors' window *and* the 'Extensions' > 'Maximus' > 'Show Errors'
  * (Brownie points: open a terminal, do `gnome-shell --replace` and reproduce the
  *  bug. Include any errors that pop up in this terminal.)
  *
- *
- * Note:
+ * ## Note:
  * It's actually possible to get the undecorate-on-maximise behaviour without
  * needing this extension. See the link [5] and in particular, the bit on editing
  * your metacity theme metacity-theme-3.xml. ("Method 2: editing the theme").
  *
- * References:
+ * ## References:
  * [1]:https://launchpad.net/maximus
  * [2]:https://extensions.gnome.org/extension/59/status-title-bar/
  * [3]:https://bitbucket.org/mathematicalcoffee/window-options-gnome-shell-extension
     USE_SET_HIDE_TITLEBAR = false;
 }
 
+// convenience
+Meta.MaximizeFlags.BOTH = (Meta.MaximizeFlags.VERTICAL | Meta.MaximizeFlags.HORIZONTAL);
 
 let maxID = null;
 let minID = null;
     //log(message);
 }
 
-/* NOTE: we prefer to use the window's XID but this is not stored
- * anywhere but in the window's description being [XID (%10s window title)].
- * And I'm not sure I want to rely on that being the case always.
- * (mutter/src/core/window-props.c)
+/** Guesses the X ID of a window.
  *
- * If we use the windows' title, `xprop` grabs the "least-focussed" window
- * (bottom of stack I suppose).
+ * It is often in the window's title, being `"0x%x %10s".format(XID, window.title)`.
+ * (See `mutter/src/core/window-props.c`).
  *
- * Can match winow.get_startup_id() to WM_WINDOW_ROLE(STRING)
- * If they're not equal, then try the XID ?
+ * If we couldn't find it there, we use `win`'s actor, `win.get_compositor_private()`.
+ * The actor's `x-window` property is the X ID of the window *actor*'s frame
+ * (as opposed to the window itself).
+ *
+ * However, the child window of the window actor is the window itself, so by
+ * using `xwininfo -children -id [actor's XID]` we can attempt to deduce the
+ * window's X ID.
+ *
+ * It is not always foolproof, but works good enough for now.
+ *
+ * @param {Meta.Window} win - the window to guess the XID of. You wil get better
+ * success if the window's actor (`win.get_compositor_private()`) exists.
  */
 function guessWindowXID(win) {
     let id = null;
     return null;
 }
 
-/* undecorates a window.
+/** Undecorates a window.
+ *
  * If I use set_decorations(0) from within the GNOME shell extension (i.e.
  *  from within the compositor process), the window dies.
  * If I use the same code but use `gjs` to run it, the window undecorates
  *
  * Hence I have to make some sort of external call to do the undecoration.
  * I could use 'gjs' on a javascript file (and I'm pretty sure this is installed
- *  with GNOME-shell too), but I decided to use a system call to xprop.
+ *  with GNOME-shell too), but I decided to use a system call to xprop and set
+ *  the window's `_MOTIF_WM_HINTS` property to ask for undecoration.
  *
  * We can use xprop using the window's title to identify the window, but
  *  prefer to use the window's X ID (in case the title changes, or there are
  *
  * See here for xprop usage for undecoration:
  * http://xrunhprof.wordpress.com/2009/04/13/removing-decorations-in-metacity/
+ *
+ * @param {Meta.Window} win - window to undecorate.
  */
 function undecorate(win) {
     /* Undecorate with xprop */
     Util.spawn(cmd);
 }
 
+/** Decorates a window by setting its `_MOTIF_WM_HINTS` property to ask for
+ * decoration.
+ *
+ * @param {Meta.Window} win - window to undecorate.
+ */
 function decorate(win) {
     /* Decorate with xprop: 1 == DECOR_ALL */
     let id = guessWindowXID(win),
     LOG(cmd.join(' '));
     Util.spawn(cmd);
 }
-/* setHideTitleBar: tells the window manager to hide the titlebar on
- * maximised windows (GNOME 3.4+).
+
+/** Tells the window manager to hide the titlebar on maximised windows.
+ * TODO: GNOME 3.2?
+ *
+ * Note - no checking of blacklists etc is done in the function. You should do
+ * it prior to calling the function (same with {@link decorate} and {@link undecorate}).
  *
  * Does this by setting the _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED hint - means
  * I can do it once and forget about it, rather than tracking maximize/unmaximize
  * events.
+ *
+ * **Caveat**: doesn't work with Ubuntu's Ambiance and Radiance window themes -
+ * my guess is they don't respect or implement this property.
+ *
+ * @param {Meta.Window} win - window to set the HIDE_TITLEBAR_WHEN_MAXIMIZED property of.
+ * @param {boolean} hide - whether to hide the titlebar or not.
+ * @param {boolean} [stopAdding] - if `win` does not have an actor and we couldn't
+ * find the window's XID, we try one more time to detect the XID, unless this
+ * is `true`. Internal use.
  */
-function setHideTitlebar(win, hide, stopAdding, force) {
-    let app = Shell.WindowTracker.get_default().get_window_app(win),
-        appid = (app ? app.get_id() : -1),
-        inList = APP_LIST.length > 0 && APP_LIST.indexOf(appid) >= 0;
-
-    /* Don't hide titlebar if it is in the blacklist or not in the whitelist or
-     * had no decorations to begin with */
-    if (!force && ((IS_BLACKLIST && inList) || (!IS_BLACKLIST && !inList) ||
-            !win._maximusDecoratedOriginal)) {
-        return;
-    }
+function setHideTitlebar(win, hide, stopAdding) {
     LOG('setHideTitlebar: ' + win.get_title() + ': ' + hide + (stopAdding ? ' (2)' : ''));
 
     let id = guessWindowXID(win);
-
     /* Newly-created windows are added to the workspace before
      * the compositor knows about them: get_compositor_private() is null.
      * Additionally things like .get_maximized() aren't properly done yet.
      */
     if (!id && !win.get_compositor_private() && !stopAdding) {
         Mainloop.idle_add(function () {
-            setHideTitlebar(null, win, true, force); // only try once more.
+            setHideTitlebar(null, win, true); // only try once more.
             return false; // define as one-time event
         });
         return;
         cmd[1] = '-name';
         cmd[2] = win.get_title();
     }
-
     LOG(cmd.join(' '));
     Util.spawn(cmd);
 }
 
+/** Returns whether we should affect `win`'s decorationa t all.
+ *
+ * If the window was originally undecorated we do not do anything with it
+ *  (decorate or undecorate),
+ *
+ * Also if it's in the blacklist, or if it's NOT in the whitelist, we don't
+ * do anything with it.
+ *
+ * @returns {boolean} whether the window is originally decorated and not in
+ * the blacklist (or in the whitelist).
+ */
+function shouldAffect(win) {
+    if (!win._maximusDecoratedOriginal) {
+        return false;
+    }
+    let app = Shell.WindowTracker.get_default().get_window_app(win),
+        appid = (app ? app.get_id() : -1),
+        inList = APP_LIST.length > 0 && APP_LIST.indexOf(appid) >= 0;
+    return !((IS_BLACKLIST && inList) || (!IS_BLACKLIST && !inList));
+}
+
+
 /**** Callbacks ****/
-/* onMaximise: called when a window is maximised and removes decorations. */
+/** Called when a window is maximized, including half-maximization.
+ *
+ * If the window is not in the blacklist (or is in the whitelist), we undecorate
+ * it.
+ *
+ * @param {Meta.WindowActor} actor - the window actor for the maximized window.
+ * It is expected to be maximized (in at least one direction) already - we will
+ * not check before undecorating.
+ */
 function onMaximise(shellwm, actor) {
     if (!actor) {
         return;
     }
-    let win = actor.get_meta_window(),
-        max = win.get_maximized(),
-        app = Shell.WindowTracker.get_default().get_window_app(win),
-        appid = (app ? app.get_id() : -1),
-        inList = APP_LIST.length > 0 && APP_LIST.indexOf(appid) >= 0;
-
-    LOG('onMaximise: ' + win.get_title() + ' [' + win.get_wm_class() + ']');
-    /* Don't undecorate if it is in the blacklist or not in the whitelist */
-    if ((IS_BLACKLIST && inList) || (!IS_BLACKLIST && !inList)) {
+    let win = actor.get_meta_window();
+    if (!shouldAffect(win)) {
         return;
     }
-
-    // do nothing if maximus isn't managing decorations for this window
-    // or we are not maximized
-    // (can force if it's in the whitelist)
-    if (!max || (!win._maximusDecoratedOriginal && (IS_BLACKLIST || !inList))) {
+    // note: window is maximized by this point.
+    let max = win.get_maximized();
+    LOG('onMaximise: ' + win.get_title() + ' [' + win.get_wm_class() + ']');
+    // if this is a partial maximization, and we do not wish to undecorate
+    // half-maximized windows, make sure the window is decorated.
+    if (max !== Meta.MaximizeFlags.BOTH &&
+            !undecorateHalfMaximised) {
+        decorate(win);
         return;
     }
-
-    // if this is a partial maximization
-    if (max !== (Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL)) {
-        // if we want decorations for partial maximization
-        if (!undecorateHalfMaximised) {
-            decorate(win);
-            return;
-        }
-    }
-
     undecorate(win);
 }
 
+/** Called when a window is unmaximized.
+ *
+ * If the window is not in the blacklist (or is in the whitelist), we decorate
+ * it.
+ *
+ * @param {Meta.WindowActor} actor - the window actor for the unmaximized window.
+ * It is expected to be unmaximized - we will not check before decorating.
+ */
 function onUnmaximise(shellwm, actor) {
     if (!actor) {
         return;
     }
-
-    let win = actor.meta_window,
-        app = Shell.WindowTracker.get_default().get_window_app(win),
-        appid = (app ? app.get_id() : -1),
-        inList = APP_LIST.length > 0 && APP_LIST.indexOf(appid) >= 0;
-    LOG('onUnmaximise: ' + win.get_title());
-    /* don't decorate if it's not meant to be decorated
-     * (like chrome with no 'use system title bars')
-     * (but if we forced it via the blacklist/whitelist then do it)
-     */
-    if (!win._maximusDecoratedOriginal && (IS_BLACKLIST || !inList)) {
+    let win = actor.meta_window;
+    if (!shouldAffect(win)) {
         return;
     }
+    LOG('onUnmaximise: ' + win.get_title());
     decorate(win);
-    return;
 }
 
+/** Callback for a window's 'notify::maximized-horizontally' and
+ * 'notify::maximized-vertically' signals.
+ *
+ * If the window is half-maximised we force it to show its titlebar.
+ * Otherwise we set it to hide if it is maximized.
+ *
+ * Only used if using the SET_HIDE_TITLEBAR method AND we wish half-maximized
+ * windows to be *decorated* (the GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED atom will
+ * hide the titlebar of half-maximized windows too).
+ *
+ * @param {Meta.Window} win - the window whose maximized-horizontally or
+ * maximized-vertically properties has changed.
+ *
+ * @see onWindowAdded
+ */
 function onWindowChangesMaximiseState(win) {
     if ((win.maximized_horizontally && !win.maximized_vertically) ||
         (!win.maximized_horizontally && win.maximized_vertically)) {
     }
 }
 
+/** Callback when a window is added in any of the workspaces.
+ * This includes a window switching to another workspace.
+ *
+ * If it is a window we already know about, we do nothing.
+ *
+ * Otherwise, we:
+ *
+ * * record the window as on we know about.
+ * * store whether the window was initially decorated (e.g. Chrome windows aren't usually).
+ * * if using the SET_HIDE_TITLEBAR method, we:
+ *  + set the GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED atom on the window.
+ *  + if we wish to keep half-maximised windows decorated, we connect up some signals
+ *    to ensure that half-maximised windows remain decorated (the GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED
+ *    atom will automatically undecorated half-maximised windows).
+ *    See {@link onWindowChangesMaximiseState}.
+ * * otherwise (not using SET_HIDE_TITLEBAR):
+ *  + if the window is maximized, we undecorate it (see {@link undecorate});
+ *  + if the window is half-maximized and we wish to undecorate half-maximised
+ *    windows, we also undecorate it.
+ *
+ * @param {Meta.Window} win - the window that was added.
+ *
+ * @see undecorate
+ */
 function onWindowAdded(ws, win) {
     // if the window is simply switching workspaces, it will trigger a
     // window-added signal. We don't want to reprocess it then because we already
     win._maximusDecoratedOriginal = win.decorated !== false || false;
     LOG('onWindowAdded: ' + win.get_title() + ' initially decorated? ' + win._maximusDecoratedOriginal);
 
+    if (!shouldAffect(win)) {
+        return;
+    }
+
     // with set_hide_titlebar, set the window hint when the window is added and
     // there is no further need to listen to maximize/unmaximize on the window.
     if (USE_SET_HIDE_TITLEBAR) {
         if (!undecorateHalfMaximised) {
             win._maxHStateId = win.connect('notify::maximized-horizontally', onWindowChangesMaximiseState);
             win._maxVStateId = win.connect('notify::maximized-vertically', onWindowChangesMaximiseState);
+            if (win.get_maximized()) {
+                onWindowChangesMaximiseState(win);
+            }
         }
-        return;
-    }
-
-    if (!win.get_compositor_private()) {
-        Mainloop.idle_add(function () {
-            onMaximise(null, win.get_compositor_private());
-            return false; // define as one-time event
-        });
     } else {
-        onMaximise(null, win.get_compositor_private());
+        // if it is added initially maximized, we undecorate it.
+        let max = win.get_maximized();
+        if ((max === Meta.MaximizeFlags.BOTH) ||
+            (undecorateHalfMaximised && max)) {
+            if (!win.get_compositor_private()) {
+                Mainloop.idle_add(function () {
+                    undecorate(win);
+                    return false; // define as one-time event
+                });
+            } else {
+                undecorate(win);
+            }
+        }
     }
 }
 
-/* Whenever the number of workspaces is changed,
- * listen to an 'add window' event in case it starts
- * maximised.
+/** Callback whenever the number of workspaces changes.
+ *
+ * We ensure that we are listening to the 'window-added' signal on each f
+ * the workspaces.
+ *
+ * @see onWindowAdded
  */
 function onChangeNWorkspaces() {
     let ws,
     }
 }
 
-/* start listening to events and affect already-existing windows. */
+/** Start listening to events and undecorate already-existing windows. */
 function startUndecorating() {
     /* Connect events */
     changeWorkspaceID = global.screen.connect('notify::n-workspaces', onChangeNWorkspaces);
     // if we are not using the set_hide_titlebar hint, we must listen to maximize and unmaximize events.
     if (!USE_SET_HIDE_TITLEBAR) {
+        /* this is needed to prevent Metacity from interpreting an attempted drag
+         * of an undecorated window as a fullscreen request. Otherwise thunderbird
+         * (in particular) has no way to get out of fullscreen, resulting in the user
+         * being stuck there.
+         * See issue #6
+         * https://bitbucket.org/mathematicalcoffee/maximus-gnome-shell-extension/issue/6
+         *
+         * Once we can properly set the window's hide_titlebar_when_maximized property
+         * this will no loner be necessary.
+         */
         maxID = global.window_manager.connect('maximize', onMaximise);
         minID = global.window_manager.connect('unmaximize', onUnmaximise);
+        oldFullscreenPref = Meta.prefs_get_force_fullscreen();
+        Meta.prefs_set_force_fullscreen(false);
     }
 
     /* Go through already-maximised windows & undecorate.
         let winList = global.get_window_actors().map(function (w) { return w.meta_window; }),
             i       = winList.length;
         while (i--) {
-            if (USE_SET_HIDE_TITLEBAR) {
-                onWindowAdded(null, winList[i]);
-            } else {
-                /* store original decorated state to restore after. If undefined, then decorated. */
-                winList[i]._maximusDecoratedOriginal = winList[i].decorated !== false || false;
-                onMaximise(null, winList[i].get_compositor_private());
+            let win = winList[i];
+            if (win.window_type === Meta.WindowType.DESKTOP) {
+                continue;
             }
+            onWindowAdded(null, win);
         }
         onChangeNWorkspaces();
         return false; // define as one-time event
     });
 }
 
-/* stop listening to events, restore all windows back to their original
+/** Stop listening to events, restore all windows back to their original
  * decoration state. */
 function stopUndecorating() {
     if (maxID) global.window_manager.disconnect(maxID);
     let winList = global.get_window_actors().map(function (w) { return w.meta_window; }),
         i       = winList.length;
     while (i--) {
-        if (winList[i].hasOwnProperty('_maximusDecoratedOriginal')) {
+        let win = winList[i];
+        if (win.window_type === Meta.WindowType.DESKTOP) {
+            continue;
+        }
+        // if it wasn't decorated originally, we haven't done anything to it so
+        // don't need to undo anything.
+        if (win._maximusDecoratedOriginal) {
             if (USE_SET_HIDE_TITLEBAR) {
-                let win = winList[i];
-                setHideTitlebar(win, false, false, true);
-                if (!undecorateHalfMaximised) {
-                    if (win._maxHStateId) {
-                        win.disconnect(win._maxHStateId);
-                        delete win._maxHStateId;
-                    }
-                    if (win._maxVStateId) {
-                        win.disconnect(win._maxVStateId);
-                        delete win._maxVStateId;
-                    }
-                    if (win._maximusDecoratedOriginal) {
-                        decorate(win);
-                    }
+                setHideTitlebar(win, false);
+                if (win._maxHStateId) {
+                    win.disconnect(win._maxHStateId);
+                    delete win._maxHStateId;
                 }
-            } else {
-                if (!winList[i].decorated && winList[i]._maximusDecoratedOriginal) {
-                    onUnmaximise(null, winList[i].get_compositor_private());
+                if (win._maxVStateId) {
+                    win.disconnect(win._maxVStateId);
+                    delete win._maxVStateId;
                 }
             }
-            delete winList[i]._maximusDecoratedOriginal;
+            decorate(win);
         }
+        delete win._maximusDecoratedOriginal;
+    }
+
+    if (oldFullscreenPref !== null) {
+        /* restore old meta force fullscreen pref */
+        Meta.prefs_set_force_fullscreen(oldFullscreenPref);
+        oldFullscreenPref = null;
     }
 }
 
 
 function enable() {
     startUndecorating();
-
-    /* this is needed to prevent Metacity from interpreting an attempted drag
-     * of an undecorated window as a fullscreen request. Otherwise thunderbird
-     * (in particular) has no way to get out of fullscreen, resulting in the user
-     * being stuck there.
-     * See issue #6
-     * https://bitbucket.org/mathematicalcoffee/maximus-gnome-shell-extension/issue/6
-     *
-     * Once we can properly set the window's hide_titlebar_when_maximized property
-     * this will no loner be necessary.
-     */
-    oldFullscreenPref = Meta.prefs_get_force_fullscreen();
-    Meta.prefs_set_force_fullscreen(false);
 }
 
 function disable() {
     stopUndecorating();
-
-    /* restore old meta force fullscreen pref */
-    Meta.prefs_set_force_fullscreen(oldFullscreenPref);
 }

maximus@mathematical.coffee.gmail.com/schemas/org.gnome.shell.extensions.maximus.gschema.xml

-<?xml version="1.0" encoding="UTF-8"?>
-<schemalist gettext-domain="gnome-shell-extensions">
-    <schema path="/org/gnome/shell/extensions/maximus/" id="org.gnome.shell.extensions.maximus">
-    <key type="b" name="undecorate-half-maximized">
-      <default>false</default>
-      <summary>Whether to undecorate half-maximized windows.</summary>
-      <description>Whether to undecorate half-maximized windows.</description>
-    </key>
-    <key type="b" name="is-blacklist">
-      <default>true</default>
-      <summary>Whether the 'window-list' key describes a blacklist or a whitelist.</summary>
-      <description>Whether the 'window-list' key describes a blacklist or a whitelist.</description>
-    </key>
-    <key type="as" name="app-list">
-      <default>[]</default>
-      <summary>Blacklist/whitelist</summary>
-      <description>A list of apps (xxxx.desktop) to blacklist or whitelist (depending on the 'is-blacklist' key).</description>
-    </key>
-  </schema>
-</schemalist>