[gnome-shell] Kill off ShellAppInfo, move into ShellApp



commit 10dcc100e9afb82c6e12365478c45785f516abfd
Author: Colin Walters <walters verbum org>
Date:   Thu Apr 21 13:35:01 2011 -0400

    Kill off ShellAppInfo, move into ShellApp
    
    This dramatically thins down and sanitizes the application code.
    
    The ShellAppSystem changes in a number of ways:
    * Preferences are special cased more explicitly; they aren't apps,
      they're shortcuts for an app), and we don't have many of them, so
      don't need e.g. the optimizations in ShellAppSystem for searching.
    * get_app() changes to lookup_app() and returns null if an app isn't
      found.  The semantics where it tried to find the .desktop file
      if we didn't know about it were just broken; I am pretty sure no
      caller needs this, and if they do we'll fix them.
    * ShellAppSystem maintains two indexes on apps (by desktop file id
      and by GMenuTreeEntry), but is no longer in the business of
      dealing with GMenuTree as far as hierarchy and categories go.  That
      is moved up into js/ui/appDisplay.js.  Actually, it flattens both
      apps and settings.
    
    Also, ShellWindowTracker is now the sole reference-owner for
    window-backed apps.  We still do the weird "window:0x1234beef" id
    for these apps, but a reference is not stored in ShellAppSystem.
    
    The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
    Variable names are clearer:
    
    _apps -> _appIcons
    _filterApp -> _visibleApps
    _filters -> _categoryBox
    
    Similarly for function names.  We no longer call (for every app) a
    recursive lookup in GMenuTree to see if it's in a particular section
    on every category switch; it's all cached.
    
    NOTE - this intentionally reverts the incremental loading code from
    commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656.  It's fast enough
    here without that.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=648149

 js/ui/appDisplay.js           |  308 +++++----
 js/ui/appFavorites.js         |    6 +-
 js/ui/dash.js                 |   12 +-
 js/ui/dateMenu.js             |    2 +-
 js/ui/endSessionDialog.js     |    2 +-
 js/ui/lookingGlass.js         |    2 +-
 js/ui/overview.js             |    2 +-
 js/ui/status/accessibility.js |    2 +-
 js/ui/status/bluetooth.js     |    2 +-
 js/ui/status/keyboard.js      |    2 +-
 js/ui/status/network.js       |    2 +-
 js/ui/status/power.js         |    2 +-
 js/ui/status/volume.js        |    2 +-
 js/ui/statusMenu.js           |    4 +-
 src/Makefile.am               |    2 +-
 src/shell-app-private.h       |   11 +-
 src/shell-app-system.c        | 1546 ++++++++++------------------------------
 src/shell-app-system.h        |   74 +--
 src/shell-app-usage.c         |    8 +-
 src/shell-app.c               |  450 +++++++++++--
 src/shell-app.h               |   19 +-
 src/shell-window-tracker.c    |   28 +-
 22 files changed, 1044 insertions(+), 1444 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 811695d..975667c 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -3,6 +3,7 @@
 const Clutter = imports.gi.Clutter;
 const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
+const GMenu = imports.gi.GMenu;
 const Shell = imports.gi.Shell;
 const Lang = imports.lang;
 const Signals = imports.signals;
@@ -35,8 +36,7 @@ AlphabeticalView.prototype = {
         this._appSystem = Shell.AppSystem.get_default();
 
         this._pendingAppLaterId = 0;
-        this._apps = [];
-        this._filterApp = null;
+        this._appIcons = {}; // desktop file id
 
         let box = new St.BoxLayout({ vertical: true });
         box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@@ -63,20 +63,17 @@ AlphabeticalView.prototype = {
 
     _removeAll: function() {
         this._grid.removeAll();
-        this._apps = [];
+        this._appIcons = {};
     },
 
-    _addApp: function(appInfo) {
-        let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
+    _addApp: function(app) {
+        var id = app.get_id();
+        let appIcon = new AppWellIcon(app);
 
         this._grid.addItem(appIcon.actor);
         appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
 
-        appIcon._appInfo = appInfo;
-        if (this._filterApp && !this._filterApp(appInfo))
-            appIcon.actor.hide();
-
-        this._apps.push(appIcon);
+        this._appIcons[id] = appIcon;
     },
 
     _ensureIconVisible: function(icon) {
@@ -105,52 +102,33 @@ AlphabeticalView.prototype = {
                            transition: 'easeOutQuad' });
     },
 
-    setFilter: function(filter) {
-        this._filterApp = filter;
-        for (let i = 0; i < this._apps.length; i++)
-            this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
-    },
-
-    // Create actors for the applications in an idle to avoid blocking
-    // for too long; see bug 647778
-    _addPendingApps: function() {
-        let i;
-        let startTimeMillis = new Date().getTime();
-        for (i = 0; i < this._pendingAppIds.length; i++) {
-            let id = this._pendingAppIds[i];
-            this._addApp(this._pendingApps[id]);
-
-            let currentTimeMillis = new Date().getTime();
-            if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
-                break;
-        }
-        this._pendingAppIds.splice(0, i + 1);
-        if (this._pendingAppIds.length > 0) {
-            return true;
+    setVisibleApps: function(apps) {
+        if (apps == null) { // null implies "all"
+            for (var id in this._appIcons) {
+                var icon = this._appIcons[id];
+                icon.actor.visible = true;
+            }
         } else {
-            this._pendingAppLaterId = 0;
-            this._pendingAppIds = null;
-            this._pendingApps = null;
-            return false;
+            // Set everything to not-visible, then set to visible what we should see
+            for (var id in this._appIcons) {
+                var icon = this._appIcons[id];
+                icon.actor.visible = false;
+            }
+            for (var i = 0; i < apps.length; i++) {
+                var app = apps[i];
+                var id = app.get_id();
+                var icon = this._appIcons[id];
+                icon.actor.visible = true;
+            }
         }
     },
 
-    refresh: function(apps) {
-        let ids = [];
-        for (let i in apps)
-            ids.push(i);
-        ids.sort(function(a, b) {
-            return apps[a].get_name().localeCompare(apps[b].get_name());
-        });
-
+    setAppList: function(apps) {
         this._removeAll();
-
-        this._pendingAppIds = ids;
-        this._pendingApps = apps;
-        if (this._pendingAppLaterId)
-            Meta.later_remove(this._pendingAppLaterId);
-        this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
-                                                 Lang.bind(this, this._addPendingApps));
+        for (var i = 0; i < apps.length; i++) {
+            var app = apps[i];
+            this._addApp(app);
+         }
     }
 };
 
@@ -171,23 +149,24 @@ ViewByCategories.prototype = {
         // -2 is a flag to indicate that nothing is selected
         // (used only before the actor is mapped the first time)
         this._currentCategory = -2;
-        this._filters = new St.BoxLayout({ vertical: true, reactive: true });
-        this._filtersBox = new St.ScrollView({ x_fill: false,
-                                               y_fill: false,
-                                               style_class: 'vfade' });
-        this._filtersBox.add_actor(this._filters);
+        this._categories = [];
+        this._apps = null;
+
+        this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
+        this._categoryScroll = new St.ScrollView({ x_fill: false,
+                                                   y_fill: false,
+                                                   style_class: 'vfade' });
+        this._categoryScroll.add_actor(this._categoryBox);
         this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
-        this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
+        this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
 
         // Always select the "All" filter when switching to the app view
         this.actor.connect('notify::mapped', Lang.bind(this,
             function() {
-                if (this.actor.mapped && this._allFilter)
+                if (this.actor.mapped && this._allCategoryButton)
                     this._selectCategory(-1);
             }));
 
-        this._sections = [];
-
         // We need a dummy actor to catch the keyboard focus if the
         // user Ctrl-Alt-Tabs here before the deferred work creates
         // our real contents
@@ -201,64 +180,94 @@ ViewByCategories.prototype = {
 
         this._currentCategory = num;
 
-        if (num != -1)
-            this._allFilter.remove_style_pseudo_class('selected');
-        else
-            this._allFilter.add_style_pseudo_class('selected');
-
-        this._view.setFilter(Lang.bind(this, function(app) {
-            if (num == -1)
-                return true;
-            return this._sections[num].name == app.get_section();
-        }));
+        if (num != -1) {
+            var category = this._categories[num];
+            this._allCategoryButton.remove_style_pseudo_class('selected');
+            this._view.setVisibleApps(category.apps);
+        } else {
+            this._allCategoryButton.add_style_pseudo_class('selected');
+            this._view.setVisibleApps(null);
+        }
 
-        for (let i = 0; i < this._sections.length; i++) {
+        for (var i = 0; i < this._categories.length; i++) {
             if (i == num)
-                this._sections[i].filterActor.add_style_pseudo_class('selected');
+                this._categories[i].button.add_style_pseudo_class('selected');
             else
-                this._sections[i].filterActor.remove_style_pseudo_class('selected');
+                this._categories[i].button.remove_style_pseudo_class('selected');
+        }
+    },
+
+    // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
+    _loadCategory: function(dir, appList) {
+        var iter = dir.iter();
+        var nextType;
+        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
+            if (nextType == GMenu.TreeItemType.ENTRY) {
+                var entry = iter.get_entry();
+                var app = this._appSystem.lookup_app_by_tree_entry(entry);
+                appList.push(app);
+            } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
+                _loadCategory(iter.get_directory(), appList);
+            }
         }
     },
 
-    _addFilter: function(name, num) {
+    _addCategory: function(name, index, dir, allApps) {
         let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
                                      style_class: 'app-filter',
                                      x_align: St.Align.START,
                                      can_focus: true });
-        this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
         button.connect('clicked', Lang.bind(this, function() {
-            this._selectCategory(num);
+            this._selectCategory(index);
         }));
 
-        if (num != -1)
-            this._sections[num] = { filterActor: button,
-                                    name: name };
-        else
-            this._allFilter = button;
+        var apps;
+        if (dir == null) {
+            apps = allApps;
+            this._allCategoryButton = button;
+        } else {
+            apps = [];
+            this._loadCategory(dir, apps);
+            this._categories.push({ apps: apps,
+                                    name: name,
+                                    button: button });
+        }
+
+        this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
     },
 
     _removeAll: function() {
-        this._sections = [];
-        this._filters.destroy_children();
+        this._categories = [];
+        this._categoryBox.destroy_children();
     },
 
-    refresh: function(apps) {
+    refresh: function() {
         this._removeAll();
 
-        let sections = this._appSystem.get_sections();
-        this._apps = apps;
+        var allApps = Shell.AppSystem.get_default().get_all();
+        allApps.sort(function(a, b) {
+            return a.compare_by_name(b);
+        });
 
         /* Translators: Filter to display all applications */
-        this._addFilter(_("All"), -1);
-
-        if (!sections)
-            return;
-
-        for (let i = 0; i < sections.length; i++)
-            this._addFilter(sections[i], i);
+        this._addCategory(_("All"), -1, null, allApps);
+
+        var tree = this._appSystem.get_tree();
+        var root = tree.get_root_directory();
+
+        var iter = root.iter();
+        var nextType;
+        var i = 0;
+        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
+            if (nextType == GMenu.TreeItemType.DIRECTORY) {
+                var dir = iter.get_directory();
+                this._addCategory(dir.get_name(), i, dir);
+                i++;
+            }
+        }
 
+        this._view.setAppList(allApps);
         this._selectCategory(-1);
-        this._view.refresh(apps);
 
         if (this._focusDummy) {
             let focused = this._focusDummy.has_key_focus();
@@ -291,31 +300,24 @@ AllAppDisplay.prototype = {
     },
 
     _redisplay: function() {
-        let apps = this._appSystem.get_flattened_apps().filter(function(app) {
-            return !app.get_is_nodisplay();
-        });
-
-        this._appView.refresh(apps);
+        this._appView.refresh();
     }
 };
 
-function BaseAppSearchProvider() {
+function AppSearchProvider() {
     this._init();
 }
 
-BaseAppSearchProvider.prototype = {
+AppSearchProvider.prototype = {
     __proto__: Search.SearchProvider.prototype,
 
-    _init: function(name) {
-        Search.SearchProvider.prototype._init.call(this, name);
+    _init: function() {
+        Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
         this._appSys = Shell.AppSystem.get_default();
     },
 
-    getResultMeta: function(resultId) {
-        let app = this._appSys.get_app(resultId);
-        if (!app)
-            return null;
-        return { 'id': resultId,
+    getResultMeta: function(app) {
+        return { 'id': app,
                  'name': app.get_name(),
                  'createIcon': function(size) {
                                    return app.create_icon_texture(size);
@@ -323,6 +325,14 @@ BaseAppSearchProvider.prototype = {
                };
     },
 
+    getInitialResultSet: function(terms) {
+        return this._appSys.initial_search(terms);
+    },
+
+    getSubsearchResultSet: function(previousResults, terms) {
+        return this._appSys.subsearch(previousResults, terms);
+    },
+
     activateResult: function(id, params) {
         params = Params.parse(params, { workspace: null,
                                         timestamp: null });
@@ -332,7 +342,7 @@ BaseAppSearchProvider.prototype = {
         let modifiers = event ? Shell.get_event_state(event) : 0;
         let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
 
-        let app = this._appSys.get_app(id);
+        let app = this._appSys.lookup_app(id);
         if (openNewWindow)
             app.open_new_window(workspace);
         else
@@ -343,54 +353,62 @@ BaseAppSearchProvider.prototype = {
         params = Params.parse(params, { workspace: null,
                                         timestamp: null });
 
-        let app = this._appSys.get_app(id);
+        let app = this._appSys.lookup_app(id);
         app.open_new_window(params.workspace ? params.workspace.index() : -1);
+    },
+
+    createResultActor: function (resultMeta, terms) {
+        let app = resultMeta['id'];
+        let icon = new AppWellIcon(app);
+        return icon.actor;
     }
 };
 
-function AppSearchProvider() {
+function SettingsSearchProvider() {
     this._init();
 }
 
-AppSearchProvider.prototype = {
-    __proto__: BaseAppSearchProvider.prototype,
+SettingsSearchProvider.prototype = {
+    __proto__: Search.SearchProvider.prototype,
 
     _init: function() {
-         BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
+        Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
+        this._appSys = Shell.AppSystem.get_default();
+        this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
+    },
+
+    getResultMeta: function(pref) {
+        return { 'id': pref,
+                 'name': pref.get_name(),
+                 'createIcon': function(size) {
+                                   return pref.create_icon_texture(size);
+                               }
+               };
     },
 
     getInitialResultSet: function(terms) {
-        return this._appSys.initial_search(false, terms);
+        return this._appSys.search_settings(terms);
     },
 
     getSubsearchResultSet: function(previousResults, terms) {
-        return this._appSys.subsearch(false, previousResults, terms);
+        return this._appSys.search_settings(terms);
     },
 
-    createResultActor: function (resultMeta, terms) {
-        let app = this._appSys.get_app(resultMeta['id']);
-        let icon = new AppWellIcon(app);
-        return icon.actor;
-    }
-};
-
-function PrefsSearchProvider() {
-    this._init();
-}
-
-PrefsSearchProvider.prototype = {
-    __proto__: BaseAppSearchProvider.prototype,
+    activateResult: function(pref, params) {
+        params = Params.parse(params, { workspace: null,
+                                        timestamp: null });
 
-    _init: function() {
-        BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
+        pref.activate(params.workspace);
     },
 
-    getInitialResultSet: function(terms) {
-        return this._appSys.initial_search(true, terms);
+    dragActivateResult: function(pref, params) {
+        this.activateResult(pref, params);
     },
 
-    getSubsearchResultSet: function(previousResults, terms) {
-        return this._appSys.subsearch(true, previousResults, terms);
+    createResultActor: function (resultMeta, terms) {
+        let app = resultMeta['id'];
+        let icon = new AppWellIcon(app);
+        return icon.actor;
     }
 };
 
@@ -416,12 +434,12 @@ AppIcon.prototype = {
     }
 };
 
-function AppWellIcon(app, iconParams) {
-    this._init(app, iconParams);
+function AppWellIcon(app, iconParams, onActivateOverride) {
+    this._init(app, iconParams, onActivateOverride);
 }
 
 AppWellIcon.prototype = {
-    _init : function(app, iconParams) {
+    _init : function(app, iconParams, onActivateOverride) {
         this.app = app;
         this.actor = new St.Button({ style_class: 'app-well-app',
                                      reactive: true,
@@ -436,6 +454,8 @@ AppWellIcon.prototype = {
 
         this.actor.label_actor = this.icon.label;
 
+        // A function callback to override the default "app.activate()"; used by preferences
+        this._onActivateOverride = onActivateOverride;
         this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
         this.actor.connect('clicked', Lang.bind(this, this._onClicked));
         this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@@ -569,11 +589,15 @@ AppWellIcon.prototype = {
         this.emit('launching');
         let modifiers = Shell.get_event_state(event);
 
-        if (modifiers & Clutter.ModifierType.CONTROL_MASK
-            && this.app.state == Shell.AppState.RUNNING) {
-            this.app.open_new_window(-1);
+        if (this._onActivateOverride) {
+            this._onActivateOverride(event);
         } else {
-            this.app.activate(-1);
+            if (modifiers & Clutter.ModifierType.CONTROL_MASK
+                && this.app.state == Shell.AppState.RUNNING) {
+                this.app.open_new_window(-1);
+            } else {
+                this.app.activate(-1);
+            }
         }
         Main.overview.hide();
     },
diff --git a/js/ui/appFavorites.js b/js/ui/appFavorites.js
index a5dce27..d4f7455 100644
--- a/js/ui/appFavorites.js
+++ b/js/ui/appFavorites.js
@@ -28,7 +28,7 @@ AppFavorites.prototype = {
         let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
         let appSys = Shell.AppSystem.get_default();
         let apps = ids.map(function (id) {
-                return appSys.get_app(id);
+                return appSys.lookup_app(id);
             }).filter(function (app) {
                 return app != null;
             });
@@ -65,7 +65,7 @@ AppFavorites.prototype = {
         if (appId in this._favorites)
             return false;
 
-        let app = Shell.AppSystem.get_default().get_app(appId);
+        let app = Shell.AppSystem.get_default().lookup_app(appId);
 
         if (!app)
             return false;
@@ -84,7 +84,7 @@ AppFavorites.prototype = {
         if (!this._addFavorite(appId, pos))
             return;
 
-        let app = Shell.AppSystem.get_default().get_app(appId);
+        let app = Shell.AppSystem.get_default().lookup_app(appId);
 
         Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
             this._removeFavorite(appId);
diff --git a/js/ui/dash.js b/js/ui/dash.js
index 2b3d080..f2c652c 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -207,7 +207,7 @@ RemoveFavoriteIcon.prototype = {
         let app = null;
         if (source instanceof AppDisplay.AppWellIcon) {
             let appSystem = Shell.AppSystem.get_default();
-            app = appSystem.get_app(source.getId());
+            app = appSystem.lookup_app(source.getId());
         } else if (source.metaWindow) {
             let tracker = Shell.WindowTracker.get_default();
             app = tracker.get_window_app(source.metaWindow);
@@ -330,7 +330,7 @@ Dash.prototype = {
     _onDragMotion: function(dragEvent) {
         let app = null;
         if (dragEvent.source instanceof AppDisplay.AppWellIcon)
-            app = this._appSystem.get_app(dragEvent.source.getId());
+            app = this._appSystem.lookup_app(dragEvent.source.getId());
         else if (dragEvent.source.metaWindow)
             app = this._tracker.get_window_app(dragEvent.source.metaWindow);
         else
@@ -619,12 +619,12 @@ Dash.prototype = {
     handleDragOver : function(source, actor, x, y, time) {
         let app = null;
         if (source instanceof AppDisplay.AppWellIcon)
-            app = this._appSystem.get_app(source.getId());
+            app = this._appSystem.lookup_app(source.getId());
         else if (source.metaWindow)
             app = this._tracker.get_window_app(source.metaWindow);
 
         // Don't allow favoriting of transient apps
-        if (app == null || app.is_transient())
+        if (app == null || app.is_window_backed())
             return DND.DragMotionResult.NO_DROP;
 
         let favorites = AppFavorites.getAppFavorites().getFavorites();
@@ -704,13 +704,13 @@ Dash.prototype = {
     acceptDrop : function(source, actor, x, y, time) {
         let app = null;
         if (source instanceof AppDisplay.AppWellIcon) {
-            app = this._appSystem.get_app(source.getId());
+            app = this._appSystem.lookup_app(source.getId());
         } else if (source.metaWindow) {
             app = this._tracker.get_window_app(source.metaWindow);
         }
 
         // Don't allow favoriting of transient apps
-        if (app == null || app.is_transient()) {
+        if (app == null || app.is_window_backed()) {
             return false;
         }
 
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index a6a5196..99ad76d 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -199,7 +199,7 @@ DateMenuButton.prototype = {
     _onPreferencesActivate: function() {
         this.menu.close();
         Main.overview.hide();
-        let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
+        let app = Shell.AppSystem.get_default().lookup_setting('gnome-datetime-panel.desktop');
         app.activate(-1);
     },
 
diff --git a/js/ui/endSessionDialog.js b/js/ui/endSessionDialog.js
index b4e458d..cad887d 100644
--- a/js/ui/endSessionDialog.js
+++ b/js/ui/endSessionDialog.js
@@ -113,7 +113,7 @@ function findAppFromInhibitor(inhibitor) {
     let app = null;
     for (let i = 0; i < candidateDesktopFiles.length; i++) {
         try {
-            app = appSystem.get_app(candidateDesktopFiles[i]);
+            app = appSystem.lookup_app(candidateDesktopFiles[i]);
 
             if (app)
                 break;
diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js
index bafcb37..03830c2 100644
--- a/js/ui/lookingGlass.js
+++ b/js/ui/lookingGlass.js
@@ -247,7 +247,7 @@ WindowList.prototype = {
             box.add(propsBox);
             propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
             let app = tracker.get_window_app(metaWindow);
-            if (app != null && !app.is_transient()) {
+            if (app != null && !app.is_window_backed()) {
                 let icon = app.create_icon_texture(22);
                 let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
                 propsBox.add(propBox);
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 085f292..8f33efa 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -188,7 +188,7 @@ Overview.prototype = {
 
         // Default search providers
         this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
-        this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
+        this.viewSelector.addSearchProvider(new AppDisplay.SettingsSearchProvider());
         this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
         this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
 
diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js
index 560042c..542c230 100644
--- a/js/ui/status/accessibility.js
+++ b/js/ui/status/accessibility.js
@@ -90,7 +90,7 @@ ATIndicator.prototype = {
         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
         this.menu.addAction(_("Universal Access Settings"), function() {
             Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
+            let app = Shell.AppSystem.get_default().lookup_setting('gnome-universal-access-panel.desktop');
             app.activate(-1);
         });
     },
diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js
index 4cf8d3b..a73eb7e 100644
--- a/js/ui/status/bluetooth.js
+++ b/js/ui/status/bluetooth.js
@@ -91,7 +91,7 @@ Indicator.prototype = {
 
         this.menu.addAction(_("Bluetooth Settings"), function() {
             Main.overview.hide()
-            let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
+            let app = Shell.AppSystem.get_default().lookup_setting('bluetooth-properties.desktop');
             app.activate(-1);
         });
 
diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
index 5c33233..0ae9929 100644
--- a/js/ui/status/keyboard.js
+++ b/js/ui/status/keyboard.js
@@ -74,7 +74,7 @@ XKBIndicator.prototype = {
         }));
         this.menu.addAction(_("Region and Language Settings"), function() {
             Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
+            let app = Shell.AppSystem.get_default().lookup_setting('gnome-region-panel.desktop');
             app.activate(-1);
         });
     },
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index a4081ff..38b2e25 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -1604,7 +1604,7 @@ NMApplet.prototype = {
 
         this.menu.addAction(_("Network Settings"), function() {
             Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-network-panel.desktop');
+            let app = Shell.AppSystem.get_default().lookup_setting('gnome-network-panel.desktop');
             app.activate(-1);
         });
 
diff --git a/js/ui/status/power.js b/js/ui/status/power.js
index a64a2ba..1f5b5bb 100644
--- a/js/ui/status/power.js
+++ b/js/ui/status/power.js
@@ -82,7 +82,7 @@ Indicator.prototype = {
 
         this.menu.addAction(_("Power Settings"),function() {
             Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
+            let app = Shell.AppSystem.get_default().lookup_setting('gnome-power-panel.desktop');
             app.activate(-1);
         });
 
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index bd74cd9..adc1207 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -63,7 +63,7 @@ Indicator.prototype = {
         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
         this.menu.addAction(_("Sound Settings"), function() {
             Main.overview.hide();
-            let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
+            let app = Shell.AppSystem.get_default().lookup_setting('gnome-sound-panel.desktop');
             app.activate(-1);
         });
 
diff --git a/js/ui/statusMenu.js b/js/ui/statusMenu.js
index c506be0..f2d825c 100644
--- a/js/ui/statusMenu.js
+++ b/js/ui/statusMenu.js
@@ -274,13 +274,13 @@ StatusMenuButton.prototype = {
 
     _onMyAccountActivate: function() {
         Main.overview.hide();
-        let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
+        let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
         app.activate(-1);
     },
 
     _onPreferencesActivate: function() {
         Main.overview.hide();
-        let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
+        let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
         app.activate(-1);
     },
 
diff --git a/src/Makefile.am b/src/Makefile.am
index cb84d6e..b20e03d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -269,7 +269,7 @@ libgnome_shell_la_LIBADD =		\
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 Shell-0.1.gir: libgnome-shell.la St-1.0.gir
-Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4
+Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0
 Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
 Shell_0_1_gir_LIBS = libgnome-shell.la
 Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)
diff --git a/src/shell-app-private.h b/src/shell-app-private.h
index 052dfbc..a5bf198 100644
--- a/src/shell-app-private.h
+++ b/src/shell-app-private.h
@@ -10,11 +10,9 @@
 
 G_BEGIN_DECLS
 
-ShellAppInfo *_shell_app_get_info (ShellApp *app);
-
 ShellApp* _shell_app_new_for_window (MetaWindow *window);
 
-ShellApp* _shell_app_new (ShellAppInfo *appinfo);
+ShellApp* _shell_app_new (GMenuTreeEntry *entry);
 
 void _shell_app_handle_startup_sequence (ShellApp *app, SnStartupSequence *sequence);
 
@@ -22,6 +20,13 @@ void _shell_app_add_window (ShellApp *app, MetaWindow *window);
 
 void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
 
+void _shell_app_do_match (ShellApp         *app,
+                          GSList           *terms,
+                          GSList          **multiple_prefix_results,
+                          GSList          **prefix_results,
+                          GSList          **multiple_substring_results,
+                          GSList          **substring_results);
+
 G_END_DECLS
 
 #endif /* __SHELL_APP_PRIVATE_H__ */
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
index 48b0ddf..9808a2b 100644
--- a/src/shell-app-system.c
+++ b/src/shell-app-system.c
@@ -17,9 +17,6 @@
 #include "shell-global.h"
 #include "st.h"
 
-#define GMENU_I_KNOW_THIS_IS_UNSTABLE
-#include <gmenu-tree.h>
-
 /* Vendor prefixes are something that can be preprended to a .desktop
  * file name.  Undo this.
  */
@@ -42,126 +39,21 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 struct _ShellAppSystemPrivate {
   GMenuTree *apps_tree;
-  GMenuTree *settings_tree;
 
-  GHashTable *app_id_to_info;
-  GHashTable *app_id_to_app;
+  GHashTable *entry_to_app;
 
-  GSList *cached_flattened_apps; /* ShellAppInfo */
-  GSList *cached_settings; /* ShellAppInfo */
   GSList *known_vendor_prefixes;
 
-  gboolean loaded;
-  gint app_monitor_id;
-
-  guint app_change_timeout_id;
+  GMenuTree *settings_tree;
+  GHashTable *setting_entry_to_app;
 };
 
-static char *shell_app_info_get_prefix (ShellAppInfo *info);
 static void shell_app_system_finalize (GObject *object);
-static gboolean on_tree_changed (gpointer user_data);
-static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
-static void reread_menus (ShellAppSystem *self);
+static void on_apps_tree_changed_cb (GMenuTree *tree, gpointer user_data);
+static void on_settings_tree_changed_cb (GMenuTree *tree, gpointer user_data);
 
 G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
 
-typedef enum {
-  SHELL_APP_INFO_TYPE_ENTRY,
-  SHELL_APP_INFO_TYPE_DESKTOP_FILE,
-  SHELL_APP_INFO_TYPE_WINDOW
-} ShellAppInfoType;
-
-struct _ShellAppInfo {
-  ShellAppInfoType type;
-
-  /* We need this for two reasons.  First, GKeyFile doesn't have a refcount.
-   * http://bugzilla.gnome.org/show_bug.cgi?id=590808
-   *
-   * But more generally we'll always need it so we know when to free this
-   * structure (short of weak references on each item).
-   */
-  guint refcount;
-
-  char *casefolded_name;
-  char *name_collation_key;
-  char *casefolded_description;
-  char *casefolded_exec;
-
-  GMenuTreeEntry *entry;
-
-  GKeyFile *keyfile;
-  char *keyfile_path;
-
-  MetaWindow *window;
-  char *window_id;
-};
-
-ShellAppInfo*
-shell_app_info_ref (ShellAppInfo *info)
-{
-  info->refcount++;
-  return info;
-}
-
-void
-shell_app_info_unref (ShellAppInfo *info)
-{
-  if (--info->refcount > 0)
-    return;
-
-  g_free (info->casefolded_name);
-  g_free (info->name_collation_key);
-  g_free (info->casefolded_description);
-
-  switch (info->type)
-  {
-  case SHELL_APP_INFO_TYPE_ENTRY:
-    gmenu_tree_item_unref (info->entry);
-    break;
-  case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-    g_key_file_free (info->keyfile);
-    g_free (info->keyfile_path);
-    break;
-  case SHELL_APP_INFO_TYPE_WINDOW:
-    g_object_unref (info->window);
-    g_free (info->window_id);
-    break;
-  }
-  g_slice_free (ShellAppInfo, info);
-}
-
-static ShellAppInfo *
-shell_app_info_new_from_tree_item (GMenuTreeEntry *entry)
-{
-  ShellAppInfo *info;
-
-  if (!entry)
-    return NULL;
-
-  info = g_slice_alloc0 (sizeof (ShellAppInfo));
-  info->type = SHELL_APP_INFO_TYPE_ENTRY;
-  info->refcount = 1;
-  info->entry = (GMenuTreeEntry*)gmenu_tree_item_ref (entry);
-  return info;
-}
-
-static ShellAppInfo *
-shell_app_info_new_from_window (MetaWindow *window)
-{
-  ShellAppInfo *info;
-
-  info = g_slice_alloc0 (sizeof (ShellAppInfo));
-  info->type = SHELL_APP_INFO_TYPE_WINDOW;
-  info->refcount = 1;
-  info->window = g_object_ref (window);
-  /* For windows, its id is simply its pointer address as a string.
-   * There are various other alternatives, but the address is unique
-   * and unchanging, which is pretty much the best we can do.
-   */
-  info->window_id = g_strdup_printf ("window:%p", window);
-  return info;
-}
-
 static void shell_app_system_class_init(ShellAppSystemClass *klass)
 {
   GObjectClass *gobject_class = (GObjectClass *)klass;
@@ -189,26 +81,25 @@ shell_app_system_init (ShellAppSystem *self)
                                                    SHELL_TYPE_APP_SYSTEM,
                                                    ShellAppSystemPrivate);
 
-  /* The key is owned by the value */
-  priv->app_id_to_info = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                NULL, (GDestroyNotify) shell_app_info_unref);
-
-  /* Key is owned by info */
-  priv->app_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
+  priv->entry_to_app = g_hash_table_new_full (NULL, NULL,
+                                              (GDestroyNotify)gmenu_tree_item_unref,
+                                              (GDestroyNotify)g_object_unref);
+  priv->setting_entry_to_app = g_hash_table_new_full (NULL, NULL,
+                                                      (GDestroyNotify)gmenu_tree_item_unref,
+                                                      (GDestroyNotify)g_object_unref);
 
   /* For now, we want to pick up Evince, Nautilus, etc.  We'll
    * handle NODISPLAY semantics at a higher level or investigate them
    * case by case.
    */
   priv->apps_tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
-  priv->settings_tree = gmenu_tree_new ("gnomecc.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
-
-  priv->app_change_timeout_id = 0;
+  g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_apps_tree_changed_cb), self);
 
-  g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_tree_changed_cb), self);
-  g_signal_connect (priv->settings_tree, "changed", G_CALLBACK (on_tree_changed_cb), self);
+  priv->settings_tree = gmenu_tree_new ("gnomecc.menu", 0);
+  g_signal_connect (priv->settings_tree, "changed", G_CALLBACK (on_settings_tree_changed_cb), self);
 
-  reread_menus (self);
+  on_apps_tree_changed_cb (priv->apps_tree, self);
+  on_settings_tree_changed_cb (priv->settings_tree, self);
 }
 
 static void
@@ -220,29 +111,150 @@ shell_app_system_finalize (GObject *object)
   g_object_unref (priv->apps_tree);
   g_object_unref (priv->settings_tree);
 
-  g_hash_table_destroy (priv->app_id_to_info);
-  g_hash_table_destroy (priv->app_id_to_app);
-
-  g_slist_foreach (priv->cached_flattened_apps, (GFunc)shell_app_info_unref, NULL);
-  g_slist_free (priv->cached_flattened_apps);
-  priv->cached_flattened_apps = NULL;
+  g_hash_table_destroy (priv->entry_to_app);
+  g_hash_table_destroy (priv->setting_entry_to_app);
 
   g_slist_foreach (priv->known_vendor_prefixes, (GFunc)g_free, NULL);
   g_slist_free (priv->known_vendor_prefixes);
   priv->known_vendor_prefixes = NULL;
 
-  g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
-  g_slist_free (priv->cached_settings);
-  priv->cached_settings = NULL;
+  G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
+}
+
+static char *
+get_prefix_for_entry (GMenuTreeEntry *entry)
+{
+  char *prefix = NULL, *file_prefix = NULL;
+  const char *id;
+  GFile *file;
+  char *name;
+  int i = 0;
+
+  id = gmenu_tree_entry_get_desktop_file_id (entry);
+  file = g_file_new_for_path (gmenu_tree_entry_get_desktop_file_path (entry));
+  name = g_file_get_basename (file);
+
+  if (!name)
+    {
+      g_object_unref (file);
+      return NULL;
+    }
+  for (i = 0; vendor_prefixes[i]; i++)
+    {
+      if (g_str_has_prefix (name, vendor_prefixes[i]))
+        {
+          file_prefix = g_strdup (vendor_prefixes[i]);
+          break;
+        }
+    }
+
+  while (strcmp (name, id) != 0)
+    {
+      char *t;
+      char *pname;
+      GFile *parent = g_file_get_parent (file);
+
+      if (!parent)
+        {
+          g_warn_if_reached ();
+          break;
+        }
+
+      pname = g_file_get_basename (parent);
+      if (!pname)
+        {
+          g_object_unref (parent);
+          break;
+        }
+      if (!g_strstr_len (id, -1, pname))
+        {
+          /* handle <LegacyDir prefix="..."> */
+          char *t;
+          size_t name_len = strlen (name);
+          size_t id_len = strlen (id);
+          char *t_id = g_strdup (id);
+
+          t_id[id_len - name_len] = '\0';
+          t = g_strdup(t_id);
+          g_free (prefix);
+          g_free (t_id);
+          g_free (name);
+          name = g_strdup (id);
+          prefix = t;
+
+          g_object_unref (file);
+          file = parent;
+          g_free (pname);
+          g_free (file_prefix);
+          file_prefix = NULL;
+          break;
+        }
+
+      t = g_strconcat (pname, "-", name, NULL);
+      g_free (name);
+      name = t;
+
+      t = g_strconcat (pname, "-", prefix, NULL);
+      g_free (prefix);
+      prefix = t;
+
+      g_object_unref (file);
+      file = parent;
+      g_free (pname);
+    }
+
+  if (file)
+    g_object_unref (file);
+
+  if (strcmp (name, id) == 0)
+    {
+      g_free (name);
+      if (file_prefix && !prefix)
+        return file_prefix;
+      if (file_prefix)
+        {
+          char *t = g_strconcat (prefix, "-", file_prefix, NULL);
+          g_free (prefix);
+          g_free (file_prefix);
+          prefix = t;
+        }
+      return prefix;
+    }
+
+  g_free (name);
+  g_free (prefix);
+  g_free (file_prefix);
+  g_return_val_if_reached (NULL);
+}
+
+static void
+load_app_entry (ShellAppSystem *self,
+                GMenuTreeEntry *entry)
+{
+  char *prefix;
+  ShellApp *app;
+
+  if (g_hash_table_lookup (self->priv->entry_to_app, entry))
+    return;
+
+  prefix = get_prefix_for_entry (entry);
+
+  if (prefix
+      && !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix,
+                               (GCompareFunc)g_strcmp0))
+    self->priv->known_vendor_prefixes = g_slist_append (self->priv->known_vendor_prefixes,
+                                                        prefix);
+  else
+    g_free (prefix);
 
-  G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
+  app = _shell_app_new (entry);
+
+  g_hash_table_insert (self->priv->entry_to_app, gmenu_tree_item_ref (entry), app);
 }
 
-static GSList *
-gather_entries_recurse (ShellAppSystem     *monitor,
-                        GSList             *apps,
-                        GHashTable         *unique,
-                        GMenuTreeDirectory *root)
+static void
+gather_apps_recurse (ShellAppSystem     *self,
+                     GMenuTreeDirectory *root)
 {
   GMenuTreeIter *iter = gmenu_tree_directory_iter (root);
   GMenuTreeItemType next_type;
@@ -253,203 +265,181 @@ gather_entries_recurse (ShellAppSystem     *monitor,
 
       switch (next_type)
         {
-        case GMENU_TREE_ITEM_INVALID:
-          break;
         case GMENU_TREE_ITEM_ENTRY:
           {
-            ShellAppInfo *app;
             item = gmenu_tree_iter_get_entry (iter);
-            app = shell_app_info_new_from_tree_item (item);
-            if (!g_hash_table_lookup (unique, shell_app_info_get_id (app)))
-              {
-                apps = g_slist_prepend (apps, app);
-                g_hash_table_insert (unique, (char*)shell_app_info_get_id (app), app);
-              }
+            load_app_entry (self, (GMenuTreeEntry*)item);
           }
           break;
         case GMENU_TREE_ITEM_DIRECTORY:
           {
             item = gmenu_tree_iter_get_directory (iter);
-            apps = gather_entries_recurse (monitor, apps, unique, (GMenuTreeDirectory*)item);
+            gather_apps_recurse (self, (GMenuTreeDirectory*)item);
           }
           break;
         default:
           break;
         }
-      gmenu_tree_item_unref (item);
+      if (item != NULL)
+        gmenu_tree_item_unref (item);
     }
 
   gmenu_tree_iter_unref (iter);
-
-  return apps;
 }
 
 static void
-reread_entries (ShellAppSystem     *self,
-                GSList            **cache,
-                GHashTable         *unique,
-                GMenuTree          *tree)
+gather_settings_recurse (ShellAppSystem     *self,
+                         GMenuTreeDirectory *root)
 {
-  GMenuTreeDirectory *trunk;
-
-  trunk = gmenu_tree_get_root_directory (tree);
-
-  g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
-  g_slist_free (*cache);
-  *cache = NULL;
+  GMenuTreeIter *iter = gmenu_tree_directory_iter (root);
+  GMenuTreeItemType next_type;
 
-  if (!trunk)
-    {
-      *cache = NULL;
-    }
-  else
+  while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
     {
-      *cache = gather_entries_recurse (self, *cache, unique, trunk);
-      gmenu_tree_item_unref (trunk);
-    }
-}
+      gpointer item = NULL;
 
-static void
-cache_by_id (ShellAppSystem *self, GSList *apps)
-{
-  GSList *iter;
+      switch (next_type)
+        {
+        case GMENU_TREE_ITEM_ENTRY:
+          {
+            ShellApp *app;
 
-  for (iter = apps; iter; iter = iter->next)
-    {
-      ShellAppInfo *info = iter->data;
-      const char *id = shell_app_info_get_id (info);
-      char *prefix = shell_app_info_get_prefix (info);
-
-      shell_app_info_ref (info);
-      /* the name is owned by the info itself */
-
-      if (prefix
-          && !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix,
-                                   (GCompareFunc)g_strcmp0))
-        self->priv->known_vendor_prefixes = g_slist_append (self->priv->known_vendor_prefixes,
-                                                            prefix);
-      else
-        g_free (prefix);
-      g_hash_table_replace (self->priv->app_id_to_info, (char*)id, info);
+            item = gmenu_tree_iter_get_entry (iter);
+            if (g_hash_table_lookup (self->priv->setting_entry_to_app, item))
+              return;
+            
+            app = _shell_app_new (item);
+
+            g_hash_table_insert (self->priv->setting_entry_to_app, gmenu_tree_item_ref (item), app);
+          }
+          break;
+        case GMENU_TREE_ITEM_DIRECTORY:
+          {
+            item = gmenu_tree_iter_get_directory (iter);
+            gather_settings_recurse (self, (GMenuTreeDirectory*)item);
+          }
+          break;
+        default:
+          break;
+        }
+      if (item != NULL)
+        gmenu_tree_item_unref (item);
     }
+
+  gmenu_tree_iter_unref (iter);
 }
 
 static void
-reread_menus (ShellAppSystem *self)
+on_apps_tree_changed_cb (GMenuTree *tree,
+                         gpointer   user_data)
 {
-  GHashTable *unique;
+  ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
   GError *error = NULL;
+  GMenuTreeDirectory *root;
 
-  if (!self->priv->loaded)
-    {
-      if (!gmenu_tree_load_sync (self->priv->apps_tree, &error))
-        {
-          g_warning ("Failed to load apps: %s", error->message);
-          return;
-        }
-      if (!gmenu_tree_load_sync (self->priv->settings_tree, &error))
-        {
-          g_warning ("Failed to load settings: %s", error->message);
-          return;
-        }
-      self->priv->loaded = TRUE;
-    }
-
-  unique = g_hash_table_new (g_str_hash, g_str_equal);
+  g_assert (tree == self->priv->apps_tree);
 
+  g_hash_table_remove_all (self->priv->entry_to_app);
   g_slist_foreach (self->priv->known_vendor_prefixes, (GFunc)g_free, NULL);
   g_slist_free (self->priv->known_vendor_prefixes);
   self->priv->known_vendor_prefixes = NULL;
 
-  reread_entries (self, &(self->priv->cached_flattened_apps), unique, self->priv->apps_tree);
-  g_hash_table_remove_all (unique);
-  reread_entries (self, &(self->priv->cached_settings), unique, self->priv->settings_tree);
-  g_hash_table_destroy (unique);
-
-  g_hash_table_remove_all (self->priv->app_id_to_info);
-
-  cache_by_id (self, self->priv->cached_flattened_apps);
-  cache_by_id (self, self->priv->cached_settings);
-}
+  if (!gmenu_tree_load_sync (self->priv->apps_tree, &error))
+    {
+      g_warning ("Failed to load apps: %s", error->message);
+      return;
+    }
 
-static gboolean
-on_tree_changed (gpointer user_data)
-{
-  ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
+  root = gmenu_tree_get_root_directory (self->priv->apps_tree);
 
-  self->priv->loaded = FALSE;
-  reread_menus (self);
+  if (root)
+    {
+      gather_apps_recurse (self, root);
+      gmenu_tree_item_unref (root);
+    }
 
   g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
-
-  self->priv->app_change_timeout_id = 0;
-  return FALSE;
 }
 
 static void
-on_tree_changed_cb (GMenuTree *monitor, gpointer user_data)
+on_settings_tree_changed_cb (GMenuTree *tree,
+                             gpointer   user_data)
 {
   ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
+  GError *error = NULL;
+  GMenuTreeDirectory *root;
 
-  /* GMenu currently gives us a separate notification on the entire
-   * menu tree for each node in the tree that might potentially have
-   * changed. (See http://bugzilla.gnome.org/show_bug.cgi?id=172046.)
-   * We need to compress these to avoid doing large extra amounts of
-   * work.
-   *
-   * Even when that bug is fixed, compression is still useful; for one
-   * thing we want to need to compress across notifications of changes
-   * to the settings tree. Second we want to compress if multiple
-   * changes are made to the desktop files at different times but in
-   * short succession.
-   */
+  g_assert (tree == self->priv->settings_tree);
 
-  if (self->priv->app_change_timeout_id != 0)
-    return;
-  self->priv->app_change_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 3000,
-                                                          (GSourceFunc) on_tree_changed,
-                                                          self, NULL);
-}
+  g_hash_table_remove_all (self->priv->setting_entry_to_app);
+  if (!gmenu_tree_load_sync (self->priv->settings_tree, &error))
+    {
+      g_warning ("Failed to load settings: %s", error->message);
+      return;
+    }
 
-GType
-shell_app_info_get_type (void)
-{
-  static GType gtype = G_TYPE_INVALID;
-  if (gtype == G_TYPE_INVALID)
+  root = gmenu_tree_get_root_directory (self->priv->settings_tree);
+
+  if (root)
     {
-      gtype = g_boxed_type_register_static ("ShellAppInfo",
-          (GBoxedCopyFunc)shell_app_info_ref,
-          (GBoxedFreeFunc)shell_app_info_unref);
+      gather_settings_recurse (self, root);
+      gmenu_tree_item_unref (root);
     }
-  return gtype;
 }
 
 /**
- * shell_app_system_get_flattened_apps:
+ * shell_app_system_get_tree:
  *
- * Traverses a toplevel menu, and returns all items under it.  Nested items
- * are flattened.  This value is computed on initial call and cached thereafter
- * until the set of installed applications changes.
- *
- * Return value: (transfer none) (element-type ShellAppInfo): List of applications
+ * Return Value: (transfer none): The #GMenuTree for apps
  */
-GSList *
-shell_app_system_get_flattened_apps (ShellAppSystem *self)
+GMenuTree *
+shell_app_system_get_tree (ShellAppSystem *self)
 {
-  return self->priv->cached_flattened_apps;
+  return self->priv->apps_tree;
 }
 
 /**
- * shell_app_system_get_all_settings:
+ * shell_app_system_get_settings_tree:
  *
- * Returns a list of application items under "settings.menu".
+ * Return Value: (transfer none): The #GMenuTree for apps
+ */
+GMenuTree *
+shell_app_system_get_settings_tree (ShellAppSystem *self)
+{
+  return self->priv->settings_tree;
+}
+
+/**
+ * shell_app_system_lookup_setting:
+ * @self:
+ * @id: desktop file id
  *
- * Return value: (transfer none) (element-type ShellAppInfo): List of applications
+ * Returns: (transfer none): Application in gnomecc.menu, or %NULL if none
  */
-GSList *
-shell_app_system_get_all_settings (ShellAppSystem *monitor)
+ShellApp *
+shell_app_system_lookup_setting (ShellAppSystem *self,
+                                 const char     *id)
 {
-  return monitor->priv->cached_settings;
+  GMenuTreeEntry *entry;
+  ShellApp *app;
+
+  /* Actually defer to the main app set if there's overlap */
+  app = shell_app_system_lookup_app (self, id);
+  if (app != NULL)
+    return app;
+
+  entry = gmenu_tree_get_entry_by_id (self->priv->settings_tree, id);
+  if (entry == NULL)
+    return NULL;
+  
+  app = g_hash_table_lookup (self->priv->setting_entry_to_app, entry);
+  if (app != NULL)
+    return app;
+  
+  app = _shell_app_new (entry);
+  g_hash_table_insert (self->priv->setting_entry_to_app, gmenu_tree_item_ref (entry), app); 
+
+  return app;
 }
 
 /**
@@ -468,71 +458,60 @@ shell_app_system_get_default ()
   return instance;
 }
 
-typedef struct {
-  ShellAppSystem *appsys;
-  ShellAppInfo *info;
-} ShellAppRef;
-
-static void
-shell_app_system_on_app_weakref (gpointer  data,
-                                 GObject  *location)
-{
-  ShellAppRef *ref = data;
-
-  g_hash_table_remove (ref->appsys->priv->app_id_to_app, shell_app_info_get_id (ref->info));
-  shell_app_info_unref (ref->info);
-  g_free (ref);
-}
-
 /**
- * shell_app_system_get_app:
+ * shell_app_system_lookup_app:
  *
- * Find or create a #ShellApp corresponding to an id; if already cached
- * elsewhere in memory, return that instance.  Otherwise, create a new
- * one.
+ * Find a #ShellApp corresponding to an id.
  *
- * Return value: (transfer full): The #ShellApp for id, or %NULL if none
+ * Return value: (transfer none): The #ShellApp for id, or %NULL if none
  */
 ShellApp *
-shell_app_system_get_app (ShellAppSystem   *self,
-                          const char       *id)
+shell_app_system_lookup_app (ShellAppSystem   *self,
+                             const char       *id)
 {
-  ShellAppInfo *info;
-  ShellApp *app;
-
-  app = g_hash_table_lookup (self->priv->app_id_to_app, id);
-  if (app)
-    return g_object_ref (app);
+  GMenuTreeEntry *entry;
 
-  info = g_hash_table_lookup (self->priv->app_id_to_info, id);
-  if (!info)
+  entry = gmenu_tree_get_entry_by_id (self->priv->apps_tree, id);
+  if (entry == NULL)
     return NULL;
 
-  app = _shell_app_new (info);
+  return g_hash_table_lookup (self->priv->entry_to_app, entry);
+}
 
-  return app;
+/**
+ * shell_app_system_lookup_app_by_tree_entry:
+ * @system: a #ShellAppSystem
+ * @entry: a #GMenuTreeEntry
+ *
+ * Find a #ShellApp corresponding to a #GMenuTreeEntry.
+ *
+ * Return value: (transfer none): The #ShellApp for @entry, or %NULL if none
+ */
+ShellApp *
+shell_app_system_lookup_app_by_tree_entry (ShellAppSystem  *self,
+                                           GMenuTreeEntry  *entry)
+{
+  return g_hash_table_lookup (self->priv->entry_to_app, entry);
 }
 
 /**
- * shell_app_system_get_app_for_path:
+ * shell_app_system_lookup_app_for_path:
  * @system: a #ShellAppSystem
  * @desktop_path: (type utf8): UTF-8 encoded absolute file name
  *
- * Find or create a #ShellApp corresponding to a given absolute
- * file name which must be in the standard paths (XDG_DATA_DIRS).
- * For files outside the datadirs, this function returns %NULL.
+ * Find or create a #ShellApp corresponding to a given absolute file
+ * name which must be in the standard paths (XDG_DATA_DIRS).  For
+ * files outside the datadirs, this function returns %NULL.
  *
- * If already cached elsewhere in memory, return that instance.
- * Otherwise, create a new one.
- *
- * Return value: (transfer full): The #ShellApp for id, or %NULL if none
+ * Return value: (transfer none): The #ShellApp for id, or %NULL if none
  */
 ShellApp *
-shell_app_system_get_app_for_path (ShellAppSystem   *system,
-                                   const char       *desktop_path)
+shell_app_system_lookup_app_for_path (ShellAppSystem   *system,
+                                      const char       *desktop_path)
 {
   const char *basename;
-  ShellAppInfo *info;
+  const char *app_path;
+  ShellApp *app;
 
   basename = g_strrstr (desktop_path, "/");
   if (basename)
@@ -540,82 +519,15 @@ shell_app_system_get_app_for_path (ShellAppSystem   *system,
   else
     basename = desktop_path;
 
-  info = g_hash_table_lookup (system->priv->app_id_to_info, basename);
-  if (!info)
+  app = shell_app_system_lookup_app (system, basename);
+  if (!app)
     return NULL;
 
-  if (info->type == SHELL_APP_INFO_TYPE_ENTRY)
-    {
-      const char *full_path = gmenu_tree_entry_get_desktop_file_path (info->entry);
-      if (strcmp (desktop_path, full_path) != 0)
-        return NULL;
-    }
-  else
+  app_path = gmenu_tree_entry_get_desktop_file_path (shell_app_get_tree_entry (app));
+  if (strcmp (desktop_path, app_path) != 0)
     return NULL;
 
-  return shell_app_system_get_app (system, basename);
-}
-
-/**
- * shell_app_system_get_app_for_window:
- * @self: A #ShellAppSystem
- * @window: A #MetaWindow
- *
- * Find or create a #ShellApp for window
- *
- * Return value: (transfer full): The #ShellApp for window, or %NULL if none
- */
-ShellApp *
-shell_app_system_get_app_for_window (ShellAppSystem *self,
-                                     MetaWindow *window)
-{
-  char *id = g_strdup_printf ("window:%p", window);
-  ShellApp *app = g_hash_table_lookup (self->priv->app_id_to_app, id);
-
-  if (app)
-    g_object_ref (G_OBJECT (app));
-  else
-    app = _shell_app_new_for_window (window);
-
-  g_free (id);
-
-  return app;
-}
-
-/* ShellAppSystem ensures we have a unique instance of
- * apps per id.
- */
-void
-_shell_app_system_register_app (ShellAppSystem   *self,
-                                ShellApp         *app)
-{
-  const char *id;
-  ShellAppRef *ref;
-
-  id = shell_app_get_id (app);
-
-  g_return_if_fail (g_hash_table_lookup (self->priv->app_id_to_app, id) == NULL);
-
-  ref = g_new0 (ShellAppRef, 1);
-  ref->appsys = self;
-  ref->info = shell_app_info_ref (_shell_app_get_info (app));
-  g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (ref->info), app);
-  g_object_weak_ref (G_OBJECT (app), shell_app_system_on_app_weakref, ref);
-}
-
-/**
- * shell_app_system_create_from_window:
- *
- * In the case where we can't otherwise determine an application
- * associated with a window, this function can create a "fake"
- * application just backed by information from the window itself.
- *
- * Return value: (transfer full): A new #ShellAppInfo
- */
-ShellAppInfo *
-shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window)
-{
-  return shell_app_info_new_from_window (window);
+  return app;
 }
 
 /**
@@ -627,21 +539,23 @@ shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window)
  * heuristically determined application identifier
  * string, or %NULL if none.
  *
- * Returns: (transfer full): A #ShellApp for @name
+ * Returns: (transfer none): A #ShellApp for @name
  */
 ShellApp *
 shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
-                                            const char *name)
+                                            const char     *name)
 {
   ShellApp *result;
   GSList *prefix;
-  result = shell_app_system_get_app (system, name);
+
+  result = shell_app_system_lookup_app (system, name);
   if (result != NULL)
     return result;
+
   for (prefix = system->priv->known_vendor_prefixes; prefix; prefix = g_slist_next (prefix))
     {
       char *tmpid = g_strconcat ((char*)prefix->data, name, NULL);
-      result = shell_app_system_get_app (system, tmpid);
+      result = shell_app_system_lookup_app (system, tmpid);
       g_free (tmpid);
       if (result != NULL)
         return result;
@@ -650,143 +564,37 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
   return NULL;
 }
 
-typedef enum {
-  MATCH_NONE,
-  MATCH_SUBSTRING, /* Not prefix, substring */
-  MATCH_MULTIPLE_SUBSTRING, /* Matches multiple criteria with substrings */
-  MATCH_PREFIX, /* Strict prefix */
-  MATCH_MULTIPLE_PREFIX, /* Matches multiple criteria, at least one prefix */
-} ShellAppInfoSearchMatch;
-
-static char *
-normalize_and_casefold (const char *str)
-{
-  char *normalized, *result;
-
-  if (str == NULL)
-    return NULL;
-
-  normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
-  result = g_utf8_casefold (normalized, -1);
-  g_free (normalized);
-  return result;
-}
-
-static char *
-trim_exec_line (const char *str)
-{
-  const char *start, *end, *pos;
-
-  end = strchr (str, ' ');
-  if (end == NULL)
-    end = str + strlen (str);
-
-  start = str;
-  while ((pos = strchr (start, '/')) && pos < end)
-    start = ++pos;
-
-  return g_strndup (start, end - start);
-}
-
-static void
-shell_app_info_init_search_data (ShellAppInfo *info)
-{
-  const char *name;
-  const char *exec;
-  const char *comment;
-  char *normalized_exec;
-  GDesktopAppInfo *appinfo;
-
-  g_assert (info->type == SHELL_APP_INFO_TYPE_ENTRY);
-
-  appinfo = gmenu_tree_entry_get_app_info (info->entry);
-  name = g_app_info_get_name (G_APP_INFO (appinfo));
-  info->casefolded_name = normalize_and_casefold (name);
-
-  comment = g_app_info_get_description (G_APP_INFO (appinfo));
-  info->casefolded_description = normalize_and_casefold (comment);
-
-  exec = g_app_info_get_executable (G_APP_INFO (appinfo));
-  normalized_exec = normalize_and_casefold (exec);
-  info->casefolded_exec = trim_exec_line (normalized_exec);
-  g_free (normalized_exec);
-}
-
-static ShellAppInfoSearchMatch
-shell_app_info_match_terms (ShellAppInfo  *info,
-                            GSList        *terms)
+/**
+ * shell_app_system_get_all:
+ * @self:
+ *
+ * Returns: (transfer container) (element-type ShellApp): All installed applications
+ */
+GSList *
+shell_app_system_get_all (ShellAppSystem  *self)
 {
-  GSList *iter;
-  ShellAppInfoSearchMatch match;
-
-  if (G_UNLIKELY(!info->casefolded_name))
-    shell_app_info_init_search_data (info);
+  GSList *result = NULL;
+  GHashTableIter iter;
+  gpointer key, value;
 
-  match = MATCH_NONE;
-  for (iter = terms; iter; iter = iter->next)
+  g_hash_table_iter_init (&iter, self->priv->entry_to_app);
+  while (g_hash_table_iter_next (&iter, &key, &value))
     {
-      ShellAppInfoSearchMatch current_match;
-      const char *term = iter->data;
-      const char *p;
-
-      current_match = MATCH_NONE;
-
-      p = strstr (info->casefolded_name, term);
-      if (p == info->casefolded_name)
-        current_match = MATCH_PREFIX;
-      else if (p != NULL)
-        current_match = MATCH_SUBSTRING;
-
-      p = strstr (info->casefolded_exec, term);
-      if (p != NULL)
-        {
-          if (p == info->casefolded_exec)
-            current_match = (current_match == MATCH_NONE) ? MATCH_PREFIX
-                                                          : MATCH_MULTIPLE_PREFIX;
-          else if (current_match < MATCH_PREFIX)
-            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
-                                                          : MATCH_MULTIPLE_SUBSTRING;
-        }
-
-      if (info->casefolded_description && current_match < MATCH_PREFIX)
-        {
-          /* Only do substring matches, as prefix matches are not meaningful
-           * enough for descriptions
-           */
-          p = strstr (info->casefolded_description, term);
-          if (p != NULL)
-            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
-                                                          : MATCH_MULTIPLE_SUBSTRING;
-        }
-
-      if (current_match == MATCH_NONE)
-        return current_match;
-
-      if (current_match > match)
-        match = current_match;
+      ShellApp *app = value;
+      result = g_slist_prepend (result, app);
     }
-  return match;
+  return result;
 }
 
 static gint
-shell_app_info_compare (gconstpointer a,
-                        gconstpointer b,
-                        gpointer      data)
+compare_apps_by_name (gconstpointer a,
+                      gconstpointer b,
+                      gpointer      data)
 {
-  ShellAppSystem *system = data;
-  const char *id_a = a;
-  const char *id_b = b;
-  ShellAppInfo *info_a = g_hash_table_lookup (system->priv->app_id_to_info, id_a);
-  ShellAppInfo *info_b = g_hash_table_lookup (system->priv->app_id_to_info, id_b);
-  GDesktopAppInfo *app_info_a = gmenu_tree_entry_get_app_info (info_a->entry);
-  GDesktopAppInfo *app_info_b = gmenu_tree_entry_get_app_info (info_b->entry);
-
-  if (!info_a->name_collation_key)
-    info_a->name_collation_key = g_utf8_collate_key (g_app_info_get_name ((GAppInfo*)app_info_a), -1);
-  if (!info_b->name_collation_key)
-    info_b->name_collation_key = g_utf8_collate_key (g_app_info_get_name ((GAppInfo*)app_info_b), -1);
-
-  return strcmp (info_a->name_collation_key, info_b->name_collation_key);
+  ShellApp *app_a = (ShellApp*)a;
+  ShellApp *app_b = (ShellApp*)b;
+
+  return shell_app_compare_by_name (app_a, app_b);
 }
 
 static GSList *
@@ -797,20 +605,34 @@ sort_and_concat_results (ShellAppSystem *system,
                          GSList         *substring_matches)
 {
   multiple_prefix_matches = g_slist_sort_with_data (multiple_prefix_matches,
-                                                    shell_app_info_compare,
+                                                    compare_apps_by_name,
                                                     system);
   prefix_matches = g_slist_sort_with_data (prefix_matches,
-                                           shell_app_info_compare,
+                                           compare_apps_by_name,
                                            system);
   multiple_substring_matches = g_slist_sort_with_data (multiple_substring_matches,
-                                                       shell_app_info_compare,
+                                                       compare_apps_by_name,
                                                        system);
   substring_matches = g_slist_sort_with_data (substring_matches,
-                                              shell_app_info_compare,
+                                              compare_apps_by_name,
                                               system);
   return g_slist_concat (multiple_prefix_matches, g_slist_concat (prefix_matches, g_slist_concat (multiple_substring_matches, substring_matches)));
 }
 
+static char *
+normalize_and_casefold (const char *str)
+{
+  char *normalized, *result;
+
+  if (str == NULL)
+    return NULL;
+
+  normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
+  result = g_utf8_casefold (normalized, -1);
+  g_free (normalized);
+  return result;
+}
+
 /**
  * normalize_terms:
  * @terms: (element-type utf8): Input search terms
@@ -830,94 +652,59 @@ normalize_terms (GSList *terms)
   return normalized_terms;
 }
 
-static inline void
-shell_app_system_do_match (ShellAppSystem   *system,
-                           ShellAppInfo     *info,
-                           GSList           *terms,
-                           GSList          **multiple_prefix_results,
-                           GSList          **prefix_results,
-                           GSList          **multiple_substring_results,
-                           GSList          **substring_results)
-{
-  const char *id = shell_app_info_get_id (info);
-  ShellAppInfoSearchMatch match;
-
-  if (shell_app_info_get_is_nodisplay (info))
-    return;
-
-  match = shell_app_info_match_terms (info, terms);
-  switch (match)
-    {
-      case MATCH_NONE:
-        break;
-      case MATCH_MULTIPLE_PREFIX:
-        *multiple_prefix_results = g_slist_prepend (*multiple_prefix_results,
-                                                    (char *) id);
-        break;
-      case MATCH_PREFIX:
-        *prefix_results = g_slist_prepend (*prefix_results, (char *) id);
-        break;
-      case MATCH_MULTIPLE_SUBSTRING:
-        *multiple_substring_results = g_slist_prepend (*multiple_substring_results,
-                                                    (char *) id);
-        break;
-      case MATCH_SUBSTRING:
-        *substring_results = g_slist_prepend (*substring_results, (char *) id);
-        break;
-    }
-}
-
 static GSList *
-shell_app_system_initial_search_internal (ShellAppSystem  *self,
-                                          GSList          *terms,
-                                          GSList          *source)
+search_tree (ShellAppSystem *self,
+             GSList         *terms,
+             GHashTable     *apps)
 {
   GSList *multiple_prefix_results = NULL;
   GSList *prefix_results = NULL;
   GSList *multiple_subtring_results = NULL;
   GSList *substring_results = NULL;
-  GSList *iter;
-  GSList *normalized_terms = normalize_terms (terms);
+  GSList *normalized_terms;
+  GHashTableIter iter;
+  gpointer key, value;
 
-  for (iter = source; iter; iter = iter->next)
-    {
-      ShellAppInfo *info = iter->data;
+  normalized_terms = normalize_terms (terms);
 
-      shell_app_system_do_match (self, info, normalized_terms,
-                                 &multiple_prefix_results, &prefix_results,
-                                 &multiple_subtring_results, &substring_results);
+  g_hash_table_iter_init (&iter, apps);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      const char *id = key;
+      ShellApp *app = value;
+      (void)id;
+      _shell_app_do_match (app, normalized_terms,
+                           &multiple_prefix_results, &prefix_results,
+                           &multiple_subtring_results, &substring_results);
     }
   g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
   g_slist_free (normalized_terms);
 
-  return sort_and_concat_results (self, multiple_prefix_results, prefix_results, multiple_subtring_results, substring_results);
+  return sort_and_concat_results (self, multiple_prefix_results, prefix_results,
+                                  multiple_subtring_results, substring_results);
+
 }
 
 /**
  * shell_app_system_initial_search:
  * @system: A #ShellAppSystem
- * @prefs: %TRUE if we should search preferences instead of apps
  * @terms: (element-type utf8): List of terms, logical AND
  *
- * Search through applications for the given search terms.  Note that returned
- * strings are only valid until a return to the main loop.
+ * Search through applications for the given search terms.
  *
- * Returns: (transfer container) (element-type utf8): List of application identifiers
+ * Returns: (transfer container) (element-type ShellApp): List of applications
  */
 GSList *
 shell_app_system_initial_search (ShellAppSystem  *self,
-                                 gboolean         prefs,
                                  GSList          *terms)
 {
-  return shell_app_system_initial_search_internal (self, terms,
-            prefs ? self->priv->cached_settings : self->priv->cached_flattened_apps);
+  return search_tree (self, terms, self->priv->entry_to_app);
 }
 
 /**
  * shell_app_system_subsearch:
  * @system: A #ShellAppSystem
- * @prefs: %TRUE if we should search preferences instead of apps
- * @previous_results: (element-type utf8): List of previous results
+ * @previous_results: (element-type ShellApp): List of previous results
  * @terms: (element-type utf8): List of terms, logical AND
  *
  * Search through a previous result set; for more information, see
@@ -925,11 +712,10 @@ shell_app_system_initial_search (ShellAppSystem  *self,
  * the same as passed to shell_app_system_initial_search().  Note that returned
  * strings are only valid until a return to the main loop.
  *
- * Returns: (transfer container) (element-type utf8): List of application identifiers
+ * Returns: (transfer container) (element-type ShellApp): List of application identifiers
  */
 GSList *
 shell_app_system_subsearch (ShellAppSystem   *system,
-                            gboolean          prefs,
                             GSList           *previous_results,
                             GSList           *terms)
 {
@@ -940,23 +726,13 @@ shell_app_system_subsearch (ShellAppSystem   *system,
   GSList *substring_results = NULL;
   GSList *normalized_terms = normalize_terms (terms);
 
-  /* Note prefs is deliberately ignored; both apps and prefs are in app_id_to_app,
-   * but we have the parameter for consistency and in case in the future
-   * they're not in the same data structure.
-   */
-
   for (iter = previous_results; iter; iter = iter->next)
     {
-      const char *id = iter->data;
-      ShellAppInfo *info;
-
-      info = g_hash_table_lookup (system->priv->app_id_to_info, id);
-      if (!info)
-        continue;
-
-      shell_app_system_do_match (system, info, normalized_terms,
-                                 &multiple_prefix_results, &prefix_results,
-                                 &multiple_substring_results, &substring_results);
+      ShellApp *app = iter->data;
+      
+      _shell_app_do_match (app, normalized_terms,
+                           &multiple_prefix_results, &prefix_results,
+                           &multiple_substring_results, &substring_results);
     }
   g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
   g_slist_free (normalized_terms);
@@ -967,566 +743,18 @@ shell_app_system_subsearch (ShellAppSystem   *system,
   return sort_and_concat_results (system, multiple_prefix_results, prefix_results, multiple_substring_results, substring_results);
 }
 
-const char *
-shell_app_info_get_id (ShellAppInfo *info)
-{
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      return gmenu_tree_entry_get_desktop_file_id (info->entry);
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-      return info->keyfile_path;
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      return info->window_id;
-  }
-  g_assert_not_reached ();
-  return NULL;
-}
-
-static char *
-shell_app_info_get_prefix (ShellAppInfo *info)
-{
-  char *prefix = NULL, *file_prefix = NULL;
-  const char *id;
-  GFile *file;
-  char *name;
-  int i = 0;
-
-  if (info->type != SHELL_APP_INFO_TYPE_ENTRY)
-    return NULL;
-
-  id = gmenu_tree_entry_get_desktop_file_id (info->entry);
-  file = g_file_new_for_path (gmenu_tree_entry_get_desktop_file_path (info->entry));
-  name = g_file_get_basename (file);
-
-  if (!name)
-    {
-      g_object_unref (file);
-      return NULL;
-    }
-  for (i = 0; vendor_prefixes[i]; i++)
-    {
-      if (g_str_has_prefix (name, vendor_prefixes[i]))
-        {
-          file_prefix = g_strdup (vendor_prefixes[i]);
-          break;
-        }
-    }
-
-  while (strcmp (name, id) != 0)
-    {
-      char *t;
-      char *pname;
-      GFile *parent = g_file_get_parent (file);
-
-      if (!parent)
-        {
-          g_warn_if_reached ();
-          break;
-        }
-
-      pname = g_file_get_basename (parent);
-      if (!pname)
-        {
-          g_object_unref (parent);
-          break;
-        }
-      if (!g_strstr_len (id, -1, pname))
-        {
-          /* handle <LegacyDir prefix="..."> */
-          char *t;
-          size_t name_len = strlen (name);
-          size_t id_len = strlen (id);
-          char *t_id = g_strdup (id);
-
-          t_id[id_len - name_len] = '\0';
-          t = g_strdup(t_id);
-          g_free (prefix);
-          g_free (t_id);
-          g_free (name);
-          name = g_strdup (id);
-          prefix = t;
-
-          g_object_unref (file);
-          file = parent;
-          g_free (pname);
-          g_free (file_prefix);
-          file_prefix = NULL;
-          break;
-        }
-
-      t = g_strconcat (pname, "-", name, NULL);
-      g_free (name);
-      name = t;
-
-      t = g_strconcat (pname, "-", prefix, NULL);
-      g_free (prefix);
-      prefix = t;
-
-      g_object_unref (file);
-      file = parent;
-      g_free (pname);
-    }
-
-  if (file)
-    g_object_unref (file);
-
-  if (strcmp (name, id) == 0)
-    {
-      g_free (name);
-      if (file_prefix && !prefix)
-        return file_prefix;
-      if (file_prefix)
-        {
-          char *t = g_strconcat (prefix, "-", file_prefix, NULL);
-          g_free (prefix);
-          g_free (file_prefix);
-          prefix = t;
-        }
-      return prefix;
-    }
-
-  g_free (name);
-  g_free (prefix);
-  g_free (file_prefix);
-  g_return_val_if_reached (NULL);
-}
-
-#define DESKTOP_ENTRY_GROUP "Desktop Entry"
-
-char *
-shell_app_info_get_name (ShellAppInfo *info)
-{
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      {
-        const char *name = g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (info->entry)));
-        return g_strdup (name);
-      }
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-      return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Name", NULL, NULL);
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      {
-        const char *name;
-
-        name = meta_window_get_wm_class (info->window);
-        if (!name)
-          name = _("Unknown");
-        return g_strdup (name);
-      }
-  }
-  g_assert_not_reached ();
-  return NULL;
-}
-
-char *
-shell_app_info_get_description (ShellAppInfo *info)
-{
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      {
-        const char *description = g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (info->entry)));
-        return g_strdup (description);
-      }
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-      return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      return NULL;
-  }
-  g_assert_not_reached ();
-  return NULL;
-}
-
-char *
-shell_app_info_get_executable (ShellAppInfo *info)
-{
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      {
-        const char *exec = g_app_info_get_executable (G_APP_INFO (gmenu_tree_entry_get_app_info (info->entry)));
-        return g_strdup (exec);
-      }
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-      return g_key_file_get_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Exec", NULL);
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      return NULL;
-  }
-  g_assert_not_reached ();
-  return NULL;
-}
-
-char *
-shell_app_info_get_desktop_file_path (ShellAppInfo *info)
-{
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      return g_strdup (gmenu_tree_entry_get_desktop_file_path (info->entry));
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-      return g_strdup (info->keyfile_path);
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      return NULL;
-  }
-  g_assert_not_reached ();
-  return NULL;
-}
-
-static GIcon *
-themed_icon_from_name (const char *iconname)
-{
-  GIcon *icon;
-
-  if (!iconname)
-     return NULL;
-
-  if (g_path_is_absolute (iconname))
-    {
-      GFile *file;
-      file = g_file_new_for_path (iconname);
-      icon = G_ICON (g_file_icon_new (file));
-      g_object_unref (file);
-     }
-  else
-    {
-      char *tmp_name, *p;
-      tmp_name = strdup (iconname);
-      /* Work around a common mistake in desktop files */
-      if ((p = strrchr (tmp_name, '.')) != NULL &&
-          (strcmp (p, ".png") == 0 ||
-           strcmp (p, ".xpm") == 0 ||
-           strcmp (p, ".svg") == 0))
-        {
-          *p = 0;
-        }
-      icon = g_themed_icon_new (tmp_name);
-      g_free (tmp_name);
-    }
-
-  return icon;
-}
-
-/**
- * shell_app_info_get_icon:
- * @info: A #ShellAppInfo
- *
- * Get the #GIcon associated with this app; for apps "faked" from a #MetaWindow,
- * return %NULL.
- *
- * Returns: (transfer full): The icon for @info, or %NULL
- */
-GIcon *
-shell_app_info_get_icon (ShellAppInfo *info)
-{
-  char *iconname = NULL;
-  GIcon *icon;
-
-  /* This code adapted from gdesktopappinfo.c
-   * Copyright (C) 2006-2007 Red Hat, Inc.
-   * Copyright  2007 Ryan Lortie
-   * LGPL
-   */
-
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      return g_object_ref (g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (info->entry))));
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-      iconname = g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL);
-      icon = themed_icon_from_name (iconname);
-      g_free (iconname);
-      return icon;
-      break;
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      return NULL;
-  }
-  g_assert_not_reached ();
-  return NULL;
-}
-
-/**
- * shell_app_system_get_sections:
- *
- * return names of sections in applications menu.
- *
- * Returns: (element-type utf8) (transfer full): List of Names
- */
-GList *
-shell_app_system_get_sections (ShellAppSystem *system)
-{
-  GList *res = NULL;
-  GMenuTreeDirectory *root;
-  GMenuTreeIter *iter;
-  GMenuTreeItemType next_type;
-
-  root = gmenu_tree_get_root_directory (system->priv->apps_tree);
-
-  if (G_UNLIKELY (!root))
-    g_error ("applications.menu not found.");
-
-  iter = gmenu_tree_directory_iter (root);
-
-  while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
-    {
-      if (next_type == GMENU_TREE_ITEM_DIRECTORY)
-        {
-          GMenuTreeDirectory *dir = gmenu_tree_iter_get_directory (iter);
-          char *name = g_strdup (gmenu_tree_directory_get_name (dir));
-
-          g_assert (name);
-
-          res = g_list_append (res, name);
-          gmenu_tree_item_unref (dir);
-        }
-    }
-
-  gmenu_tree_iter_unref (iter);
-
-  return res;
-}
-
 /**
- * shell_app_info_get_section:
- *
- * return name of section, that contain this application.
- * Returns: (transfer full): section name
- */
-char *
-shell_app_info_get_section (ShellAppInfo *info)
-{
-  char *name;
-  GMenuTreeDirectory *dir, *parent;
-
-  if (info->type != SHELL_APP_INFO_TYPE_ENTRY)
-    return NULL;
-
-  dir = gmenu_tree_entry_get_parent (info->entry);
-  if (!dir)
-    return NULL;
-
-  parent = gmenu_tree_directory_get_parent (dir);
-  if (!parent)
-    return NULL;
-
-  while (TRUE)
-    {
-      GMenuTreeDirectory *pparent = gmenu_tree_directory_get_parent (parent);
-      if (!pparent)
-        break;
-      gmenu_tree_item_unref (dir);
-      dir = parent;
-      parent = pparent;
-    }
-
-  name = g_strdup (gmenu_tree_directory_get_name (dir));
-
-  gmenu_tree_item_unref (dir);
-  gmenu_tree_item_unref (parent);
-  return name;
-}
-
-gboolean
-shell_app_info_get_is_nodisplay (ShellAppInfo *info)
-{
-  switch (info->type)
-  {
-    case SHELL_APP_INFO_TYPE_ENTRY:
-      return g_desktop_app_info_get_nodisplay (gmenu_tree_entry_get_app_info (info->entry));
-    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
-    case SHELL_APP_INFO_TYPE_WINDOW:
-      return FALSE;
-  }
-  g_assert_not_reached ();
-  return TRUE;
-}
-
-/**
- * shell_app_info_is_transient:
- *
- * A "transient" application is one which represents
- * just an open window, i.e. we don't know how to launch it
- * again.
- */
-gboolean
-shell_app_info_is_transient (ShellAppInfo *info)
-{
-  return info->type == SHELL_APP_INFO_TYPE_WINDOW;
-}
-
-/**
- * shell_app_info_create_icon_texture:
- *
- * Look up the icon for this application, and create a #ClutterTexture
- * for it at the given size.
+ * shell_app_system_search_settings:
+ * @system: A #ShellAppSystem
+ * @terms: (element-type utf8): List of terms, logical AND
  *
- * Return value: (transfer none): A floating #ClutterActor
- */
-ClutterActor *
-shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
-{
-  GIcon *icon;
-  ClutterActor *ret;
-
-  ret = NULL;
-
-  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
-    {
-      ret = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
-                                                   G_OBJECT (info->window),
-                                                   "icon");
-    }
-  else
-    {
-      icon = shell_app_info_get_icon (info);
-      if (icon != NULL)
-        {
-          ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, (int)size);
-          g_object_unref (icon);
-        }
-    }
-
-  if (ret == NULL)
-    {
-      icon = g_themed_icon_new ("application-x-executable");
-      ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, (int)size);
-      g_object_unref (icon);
-    }
-
-  return ret;
-}
-
-/**
- * shell_app_info_get_source_window:
- * @info: A #ShellAppInfo
+ * Search through settings for the given search terms.
  *
- * Returns: (transfer none): If @info is tracking a #MetaWindow,
- *   return that window.  Otherwise, return %NULL.
- */
-MetaWindow *
-shell_app_info_get_source_window (ShellAppInfo *info)
-{
-  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
-    return info->window;
-  return NULL;
-}
-
-static void
-_gather_pid_callback (GDesktopAppInfo   *gapp,
-                      GPid               pid,
-                      gpointer           data)
-{
-  ShellApp *app;
-  ShellWindowTracker *tracker;
-
-  g_return_if_fail (data != NULL);
-
-  app = SHELL_APP (data);
-  tracker = shell_window_tracker_get_default ();
-
-  _shell_window_tracker_add_child_process_app (tracker,
-                                               pid,
-                                               app);
-}
-
-/**
- * shell_app_info_launch_full:
- * @timestamp: Event timestamp, or 0 for current event timestamp
- * @uris: List of uris to pass to application
- * @workspace: Start on this workspace, or -1 for default
- * @startup_id: (out): Returned startup notification ID, or %NULL if none
- * @error: A #GError
+ * Returns: (transfer container) (element-type ShellApp): List of setting applications
  */
-gboolean
-shell_app_info_launch_full (ShellAppInfo *info,
-                            guint         timestamp,
-                            GList        *uris,
-                            int           workspace,
-                            char        **startup_id,
-                            GError      **error)
-{
-  ShellApp *shell_app;
-  GDesktopAppInfo *gapp;
-  GdkAppLaunchContext *context;
-  gboolean ret;
-  ShellGlobal *global;
-  MetaScreen *screen;
-
-  if (startup_id)
-    *startup_id = NULL;
-
-  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
-    {
-      /* We can't pass URIs into a window; shouldn't hit this
-       * code path.  If we do, fix the caller to disallow it.
-       */
-      g_return_val_if_fail (uris == NULL, TRUE);
-
-      meta_window_activate (info->window, timestamp);
-      return TRUE;
-    }
-  else if (info->type == SHELL_APP_INFO_TYPE_ENTRY)
-    {
-      /* Can't use g_desktop_app_info_new, see bug 614879 */
-      const char *filename = gmenu_tree_entry_get_desktop_file_path (info->entry);
-      gapp = g_desktop_app_info_new_from_filename (filename);
-    }
-  else
-    {
-      char *filename = shell_app_info_get_desktop_file_path (info);
-      gapp = g_desktop_app_info_new_from_filename (filename);
-      g_free (filename);
-    }
-
-  if (!gapp)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
-      return FALSE;
-    }
-
-  global = shell_global_get ();
-  screen = shell_global_get_screen (global);
-
-  if (timestamp == 0)
-    timestamp = clutter_get_current_event_time ();
-
-  if (workspace < 0)
-    workspace = meta_screen_get_active_workspace_index (screen);
-
-  context = gdk_app_launch_context_new ();
-  gdk_app_launch_context_set_timestamp (context, timestamp);
-  gdk_app_launch_context_set_desktop (context, workspace);
-
-  shell_app = shell_app_system_get_app (shell_app_system_get_default (),
-                                        shell_app_info_get_id (info));
-
-  /* In the case where we know an app, we handle reaping the child internally,
-   * in the window tracker.
-   */
-  if (shell_app != NULL)
-    ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
-                                                     G_APP_LAUNCH_CONTEXT (context),
-                                                     G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-                                                     NULL, NULL,
-                                                     _gather_pid_callback, shell_app,
-                                                     error);
-  else
-    ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
-                                                     G_APP_LAUNCH_CONTEXT (context),
-                                                     G_SPAWN_SEARCH_PATH,
-                                                     NULL, NULL,
-                                                     NULL, NULL,
-                                                     error);
-
-  g_object_unref (G_OBJECT (gapp));
-
-  return ret;
-}
-
-gboolean
-shell_app_info_launch (ShellAppInfo    *info,
-                       GError         **error)
+GSList *
+shell_app_system_search_settings (ShellAppSystem  *self,
+                                  GSList          *terms)
 {
-  return shell_app_info_launch_full (info, 0, NULL, -1, NULL, error);
+  return search_tree (self, terms, self->priv->setting_entry_to_app);
 }
diff --git a/src/shell-app-system.h b/src/shell-app-system.h
index b53c78d..a2a55f6 100644
--- a/src/shell-app-system.h
+++ b/src/shell-app-system.h
@@ -5,6 +5,8 @@
 #include <gio/gio.h>
 #include <clutter/clutter.h>
 #include <meta/window.h>
+#define GMENU_I_KNOW_THIS_IS_UNSTABLE
+#include <gmenu-tree.h>
 
 #include "shell-app.h"
 
@@ -37,63 +39,33 @@ struct _ShellAppSystemClass
 GType           shell_app_system_get_type    (void) G_GNUC_CONST;
 ShellAppSystem *shell_app_system_get_default (void);
 
-typedef struct _ShellAppInfo ShellAppInfo;
-
-#define SHELL_TYPE_APP_INFO (shell_app_info_get_type ())
-GType           shell_app_info_get_type              (void);
-
-ShellAppInfo   *shell_app_info_ref                   (ShellAppInfo  *info);
-void            shell_app_info_unref                 (ShellAppInfo  *info);
-
-const char     *shell_app_info_get_id                (ShellAppInfo  *info);
-char           *shell_app_info_get_name              (ShellAppInfo  *info);
-char           *shell_app_info_get_description       (ShellAppInfo  *info);
-char           *shell_app_info_get_executable        (ShellAppInfo  *info);
-char           *shell_app_info_get_desktop_file_path (ShellAppInfo  *info);
-GIcon          *shell_app_info_get_icon              (ShellAppInfo  *info);
-ClutterActor   *shell_app_info_create_icon_texture   (ShellAppInfo  *info,
-                                                      float          size);
-char           *shell_app_info_get_section           (ShellAppInfo  *info);
-gboolean        shell_app_info_get_is_nodisplay      (ShellAppInfo  *info);
-gboolean        shell_app_info_is_transient          (ShellAppInfo  *info);
-MetaWindow     *shell_app_info_get_source_window     (ShellAppInfo  *info);
-
-gboolean        shell_app_info_launch                (ShellAppInfo  *info,
-                                                      GError       **error);
-gboolean        shell_app_info_launch_full           (ShellAppInfo  *info,
-                                                      guint          timestamp,
-                                                      GList         *uris,
-                                                      int            workspace,
-                                                      char         **startup_id,
-                                                      GError       **error);
-
-
-GList          *shell_app_system_get_sections              (ShellAppSystem  *system);
-GSList         *shell_app_system_get_flattened_apps        (ShellAppSystem  *system);
-GSList         *shell_app_system_get_all_settings          (ShellAppSystem  *system);
-
-ShellApp       *shell_app_system_get_app                   (ShellAppSystem  *system,
-                                                            const char      *id);
-ShellApp       *shell_app_system_get_app_for_path          (ShellAppSystem  *system,
-                                                            const char      *desktop_path);
-ShellApp       *shell_app_system_get_app_for_window        (ShellAppSystem  *self,
-                                                            MetaWindow      *window);
-ShellApp       *shell_app_system_lookup_heuristic_basename (ShellAppSystem  *system,
-                                                            const char      *id);
-
-ShellAppInfo   *shell_app_system_create_from_window        (ShellAppSystem  *system,
-                                                            MetaWindow      *window);
+GMenuTree      *shell_app_system_get_tree                     (ShellAppSystem *system);
+
+ShellApp       *shell_app_system_lookup_app                   (ShellAppSystem  *system,
+                                                               const char      *id);
+ShellApp       *shell_app_system_lookup_app_by_tree_entry     (ShellAppSystem  *system,
+                                                               GMenuTreeEntry  *entry);
+ShellApp       *shell_app_system_lookup_app_for_path          (ShellAppSystem  *system,
+                                                               const char      *desktop_path);
+ShellApp       *shell_app_system_lookup_heuristic_basename    (ShellAppSystem  *system,
+                                                               const char      *id);
+
+
+GSList         *shell_app_system_get_all                   (ShellAppSystem  *system);
 
 GSList         *shell_app_system_initial_search            (ShellAppSystem  *system,
-                                                            gboolean         prefs,
                                                             GSList          *terms);
 GSList         *shell_app_system_subsearch                 (ShellAppSystem  *system,
-                                                            gboolean         prefs,
                                                             GSList          *previous_results,
                                                             GSList          *terms);
 
-/* internal API */
-void _shell_app_system_register_app (ShellAppSystem  *self,
-                                     ShellApp        *app);
+GMenuTree      *shell_app_system_get_settings_tree         (ShellAppSystem *system);
+
+GSList         *shell_app_system_search_settings           (ShellAppSystem *system,
+                                                            GSList         *terms);
+
+ShellApp       *shell_app_system_lookup_setting            (ShellAppSystem *system,
+                                                            const char     *id);
+
 
 #endif /* __SHELL_APP_SYSTEM_H__ */
diff --git a/src/shell-app-usage.c b/src/shell-app-usage.c
index f9e91b0..698dc8f 100644
--- a/src/shell-app-usage.c
+++ b/src/shell-app-usage.c
@@ -105,7 +105,7 @@ G_DEFINE_TYPE (ShellAppUsage, shell_app_usage, G_TYPE_OBJECT);
 struct UsageData
 {
   /* Whether the application we're tracking is "transient", see
-   * shell_app_info_is_transient.
+   * shell_app_is_window_backed.
    */
   gboolean transient;
 
@@ -315,7 +315,7 @@ on_app_state_changed (ShellWindowTracker *tracker,
   UsageData *usage;
   gboolean running;
 
-  if (shell_app_is_transient (app))
+  if (shell_app_is_window_backed (app))
     return;
 
   usage = get_usage_for_app (self, app);
@@ -509,7 +509,7 @@ shell_app_usage_get_most_used (ShellAppUsage   *self,
       const char *appid = iter->data;
       ShellApp *app;
 
-      app = shell_app_system_get_app (appsys, appid);
+      app = shell_app_system_lookup_app (appsys, appid);
       if (!app)
         continue;
 
@@ -670,7 +670,7 @@ idle_save_application_usage (gpointer data)
     {
       ShellApp *app;
 
-      app = shell_app_system_get_app (shell_app_system_get_default(), id);
+      app = shell_app_system_lookup_app (shell_app_system_get_default(), id);
 
       if (!app)
         continue;
diff --git a/src/shell-app.c b/src/shell-app.c
index 301f01e..0f74716 100644
--- a/src/shell-app.c
+++ b/src/shell-app.c
@@ -14,6 +14,14 @@
 #include "shell-window-tracker-private.h"
 #include "st.h"
 
+typedef enum {
+  MATCH_NONE,
+  MATCH_SUBSTRING, /* Not prefix, substring */
+  MATCH_MULTIPLE_SUBSTRING, /* Matches multiple criteria with substrings */
+  MATCH_PREFIX, /* Strict prefix */
+  MATCH_MULTIPLE_PREFIX, /* Matches multiple criteria, at least one prefix */
+} ShellAppSearchMatch;
+
 /* This is mainly a memory usage optimization - the user is going to
  * be running far fewer of the applications at one time than they have
  * installed.  But it also just helps keep the code more logically
@@ -38,7 +46,7 @@ typedef struct {
  * SECTION:shell-app
  * @short_description: Object representing an application
  *
- * This object wraps a #ShellAppInfo, providing methods and signals
+ * This object wraps a #GMenuTreeEntry, providing methods and signals
  * primarily useful for running applications.
  */
 struct _ShellApp
@@ -49,9 +57,16 @@ struct _ShellApp
 
   ShellAppState state;
 
-  ShellAppInfo *info;
+  GMenuTreeEntry *entry; /* If NULL, this app is backend by exactly one MetaWindow */
 
   ShellAppRunningState *running_state;
+
+  char *window_id_string;
+
+  char *casefolded_name;
+  char *name_collation_key;
+  char *casefolded_description;
+  char *casefolded_exec;
 };
 
 G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
@@ -93,7 +108,18 @@ shell_app_get_property (GObject    *gobject,
 const char *
 shell_app_get_id (ShellApp *app)
 {
-  return shell_app_info_get_id (app->info);
+  if (app->entry)
+    return gmenu_tree_entry_get_desktop_file_id (app->entry);
+  return app->window_id_string;
+}
+
+static MetaWindow *
+window_backed_app_get_window (ShellApp     *app)
+{
+  g_assert (app->entry == NULL);
+  g_assert (app->running_state);
+  g_assert (app->running_state->windows);
+  return app->running_state->windows->data;
 }
 
 /**
@@ -108,8 +134,33 @@ ClutterActor *
 shell_app_create_icon_texture (ShellApp   *app,
                                float size)
 {
-  return shell_app_info_create_icon_texture (app->info, size);
+  GIcon *icon;
+  ClutterActor *ret;
+
+  ret = NULL;
+
+  if (app->entry == NULL)
+    {
+      MetaWindow *window = window_backed_app_get_window (app);
+      return st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
+                                                    G_OBJECT (window),
+                                                    "icon");
+    }
+
+  icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
+  if (icon != NULL)
+    ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, (int)size);
+
+  if (ret == NULL)
+    {
+      icon = g_themed_icon_new ("application-x-executable");
+      ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, (int)size);
+      g_object_unref (icon);
+    }
+
+  return ret;
 }
+
 typedef struct {
   ShellApp *app;
   int size;
@@ -143,13 +194,12 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
 
   info = NULL;
 
-  icon = shell_app_info_get_icon (app->info);
+  icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
   if (icon != NULL)
     {
       info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
                                              icon, (int) (size + 0.5),
                                              GTK_ICON_LOOKUP_FORCE_SIZE);
-      g_object_unref (icon);
     }
 
   if (info == NULL)
@@ -224,22 +274,23 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
 ClutterActor *
 shell_app_get_faded_icon (ShellApp *app, float size)
 {
-  MetaWindow *window;
   CoglHandle texture;
   ClutterActor *result;
   char *cache_key;
   CreateFadedIconData data;
 
-  /* Punt for WINDOW types for now...easier to reuse the property tracking bits,
-   * and this helps us visually distinguish app-tracked from not.
+  /* Don't fade for window backed apps for now...easier to reuse the
+   * property tracking bits, and this helps us visually distinguish
+   * app-tracked from not.
    */
-  window = shell_app_info_get_source_window (app->info);
-  if (window)
+  if (!app->entry)
     {
+      MetaWindow *window = window_backed_app_get_window (app);
       return st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
                                                     G_OBJECT (window),
                                                     "icon");
     }
+    
 
   cache_key = g_strdup_printf ("faded-icon:%s,size=%f", shell_app_get_id (app), size);
   data.app = app;
@@ -266,22 +317,43 @@ shell_app_get_faded_icon (ShellApp *app, float size)
   return result;
 }
 
-char *
+const char *
 shell_app_get_name (ShellApp *app)
 {
-  return shell_app_info_get_name (app->info);
+  if (app->entry)
+    return g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
+  else
+    {
+      MetaWindow *window = window_backed_app_get_window (app);
+      const char *name;
+
+      name = meta_window_get_wm_class (window);
+      if (!name)
+        name = _("Unknown");
+      return name;
+    }
 }
 
-char *
+const char *
 shell_app_get_description (ShellApp *app)
 {
-  return shell_app_info_get_description (app->info);
+  if (app->entry)
+    return g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry)));
+  else
+    return NULL;
 }
 
+/**
+ * shell_app_is_window_backed:
+ *
+ * A window backed application is one which represents just an open
+ * window, i.e. there's no .desktop file assocation, so we don't know
+ * how to launch it again.
+ */
 gboolean
-shell_app_is_transient (ShellApp *app)
+shell_app_is_window_backed (ShellApp *app)
 {
-  return shell_app_info_is_transient (app->info);
+  return app->entry == NULL;
 }
 
 typedef struct {
@@ -453,12 +525,12 @@ shell_app_activate (ShellApp      *app,
       case SHELL_APP_STATE_STOPPED:
         {
           GError *error = NULL;
-          if (!shell_app_info_launch_full (app->info,
-                                           0,
-                                           NULL,
-                                           workspace,
-                                           NULL,
-                                           &error))
+          if (!shell_app_launch (app,
+                                 0,
+                                 NULL,
+                                 workspace,
+                                 NULL,
+                                 &error))
             {
               char *msg;
               msg = g_strdup_printf (_("Failed to launch '%s'"), shell_app_get_name (app));
@@ -489,6 +561,8 @@ void
 shell_app_open_new_window (ShellApp      *app,
                            int            workspace)
 {
+  g_return_if_fail (app->entry != NULL);
+
   /* Here we just always launch the application again, even if we know
    * it was already running.  For most applications this
    * should have the effect of creating a new window, whether that's
@@ -497,12 +571,12 @@ shell_app_open_new_window (ShellApp      *app,
    * as say Pidgin.  Ideally, we have the application express to us
    * that it supports an explicit new-window action.
    */
-  shell_app_info_launch_full (app->info,
-                              0,
-                              NULL,
-                              workspace,
-                              NULL,
-                              NULL);
+  shell_app_launch (app,
+                    0,
+                    NULL,
+                    workspace,
+                    NULL,
+                    NULL);
 }
 
 /**
@@ -517,17 +591,6 @@ shell_app_get_state (ShellApp *app)
   return app->state;
 }
 
-/**
- * _shell_app_get_info:
- *
- * Returns: (transfer none): Associated app info
- */
-ShellAppInfo *
-_shell_app_get_info (ShellApp *app)
-{
-  return app->info;
-}
-
 typedef struct {
   ShellApp *app;
   MetaWorkspace *active_workspace;
@@ -697,21 +760,26 @@ _shell_app_new_for_window (MetaWindow      *window)
   ShellApp *app;
 
   app = g_object_new (SHELL_TYPE_APP, NULL);
-  app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
-  _shell_app_system_register_app (shell_app_system_get_default (), app);
+
+  /* For windows, its id is simply its pointer address as a string.
+   * There are various other alternatives, but the address is unique
+   * and unchanging, which is pretty much the best we can do.
+   */
+  app->window_id_string = g_strdup_printf ("window:%p", window);
+
   _shell_app_add_window (app, window);
 
   return app;
 }
 
 ShellApp *
-_shell_app_new (ShellAppInfo    *info)
+_shell_app_new (GMenuTreeEntry *info)
 {
   ShellApp *app;
 
   app = g_object_new (SHELL_TYPE_APP, NULL);
-  app->info = shell_app_info_ref (info);
-  _shell_app_system_register_app (shell_app_system_get_default (), app);
+  app->entry = gmenu_tree_item_ref (info);
+  app->name_collation_key = g_utf8_collate_key (shell_app_get_name (app), -1);
 
   return app;
 }
@@ -923,6 +991,112 @@ shell_app_request_quit (ShellApp   *app)
 }
 
 static void
+_gather_pid_callback (GDesktopAppInfo   *gapp,
+                      GPid               pid,
+                      gpointer           data)
+{
+  ShellApp *app;
+  ShellWindowTracker *tracker;
+
+  g_return_if_fail (data != NULL);
+
+  app = SHELL_APP (data);
+  tracker = shell_window_tracker_get_default ();
+
+  _shell_window_tracker_add_child_process_app (tracker,
+                                               pid,
+                                               app);
+}
+
+/**
+ * shell_app_launch:
+ * @timestamp: Event timestamp, or 0 for current event timestamp
+ * @uris: List of uris to pass to application
+ * @workspace: Start on this workspace, or -1 for default
+ * @startup_id: (out): Returned startup notification ID, or %NULL if none
+ * @error: A #GError
+ */
+gboolean
+shell_app_launch (ShellApp     *app,
+                  guint         timestamp,
+                  GList        *uris,
+                  int           workspace,
+                  char        **startup_id,
+                  GError      **error)
+{
+  GDesktopAppInfo *gapp;
+  GdkAppLaunchContext *context;
+  gboolean ret;
+  ShellGlobal *global;
+  MetaScreen *screen;
+
+  if (startup_id)
+    *startup_id = NULL;
+
+  if (app->entry == NULL)
+    {
+      MetaWindow *window = window_backed_app_get_window (app);
+      /* We can't pass URIs into a window; shouldn't hit this
+       * code path.  If we do, fix the caller to disallow it.
+       */
+      g_return_val_if_fail (uris == NULL, TRUE);
+
+      meta_window_activate (window, timestamp);
+      return TRUE;
+    }
+
+  global = shell_global_get ();
+  screen = shell_global_get_screen (global);
+
+  if (timestamp == 0)
+    timestamp = clutter_get_current_event_time ();
+
+  if (workspace < 0)
+    workspace = meta_screen_get_active_workspace_index (screen);
+
+  context = gdk_app_launch_context_new ();
+  gdk_app_launch_context_set_timestamp (context, timestamp);
+  gdk_app_launch_context_set_desktop (context, workspace);
+
+  gapp = gmenu_tree_entry_get_app_info (app->entry);
+  ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
+                                                   G_APP_LAUNCH_CONTEXT (context),
+                                                   G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                                   NULL, NULL,
+                                                   _gather_pid_callback, app,
+                                                   error);
+  g_object_unref (context);
+
+  return ret;
+}
+
+/**
+ * shell_app_get_app_info:
+ * @app: a #ShellApp
+ *
+ * Returns: (transfer none): The #GDesktopAppInfo for this app, or %NULL if backed by a window
+ */
+GDesktopAppInfo *
+shell_app_get_app_info (ShellApp *app)
+{
+  if (app->entry)
+    return gmenu_tree_entry_get_app_info (app->entry);
+  return NULL;
+}
+
+/**
+ * shell_app_get_tree_entry:
+ * @app: a #ShellApp
+ *
+ * Returns: (transfer none): The #GMenuTreeEntry for this app, or %NULL if backed by a window
+ */
+GMenuTreeEntry *
+shell_app_get_tree_entry (ShellApp *app)
+{
+  return app->entry;
+}
+
+static void
 create_running_state (ShellApp *app)
 {
   MetaScreen *screen;
@@ -951,6 +1125,171 @@ unref_running_state (ShellAppRunningState *state)
   g_slice_free (ShellAppRunningState, state);
 }
 
+static char *
+normalize_and_casefold (const char *str)
+{
+  char *normalized, *result;
+
+  if (str == NULL)
+    return NULL;
+
+  normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
+  result = g_utf8_casefold (normalized, -1);
+  g_free (normalized);
+  return result;
+}
+
+static char *
+trim_exec_line (const char *str)
+{
+  const char *start, *end, *pos;
+
+  end = strchr (str, ' ');
+  if (end == NULL)
+    end = str + strlen (str);
+
+  start = str;
+  while ((pos = strchr (start, '/')) && pos < end)
+    start = ++pos;
+
+  return g_strndup (start, end - start);
+}
+
+static void
+shell_app_init_search_data (ShellApp *app)
+{
+  const char *name;
+  const char *exec;
+  const char *comment;
+  char *normalized_exec;
+  GDesktopAppInfo *appinfo;
+
+  appinfo = gmenu_tree_entry_get_app_info (app->entry);
+  name = g_app_info_get_name (G_APP_INFO (appinfo));
+  app->casefolded_name = normalize_and_casefold (name);
+
+  comment = g_app_info_get_description (G_APP_INFO (appinfo));
+  app->casefolded_description = normalize_and_casefold (comment);
+
+  exec = g_app_info_get_executable (G_APP_INFO (appinfo));
+  normalized_exec = normalize_and_casefold (exec);
+  app->casefolded_exec = trim_exec_line (normalized_exec);
+  g_free (normalized_exec);
+}
+
+/**
+ * shell_app_compare_by_name:
+ * @app:
+ * @other:
+ *
+ * Order two applications by name.
+ *
+ * Returns: -1, 0, or 1; suitable for use as a comparison function for e.g. g_slist_sort()
+ */
+int
+shell_app_compare_by_name (ShellApp *app, ShellApp *other)
+{
+  return strcmp (app->name_collation_key, other->name_collation_key);
+}
+
+static ShellAppSearchMatch
+_shell_app_match_search_terms (ShellApp  *app,
+                               GSList    *terms)
+{
+  GSList *iter;
+  ShellAppSearchMatch match;
+
+  if (G_UNLIKELY (!app->casefolded_name))
+    shell_app_init_search_data (app);
+
+  match = MATCH_NONE;
+  for (iter = terms; iter; iter = iter->next)
+    {
+      ShellAppSearchMatch current_match;
+      const char *term = iter->data;
+      const char *p;
+
+      current_match = MATCH_NONE;
+
+      p = strstr (app->casefolded_name, term);
+      if (p == app->casefolded_name)
+        current_match = MATCH_PREFIX;
+      else if (p != NULL)
+        current_match = MATCH_SUBSTRING;
+
+      p = strstr (app->casefolded_exec, term);
+      if (p != NULL)
+        {
+          if (p == app->casefolded_exec)
+            current_match = (current_match == MATCH_NONE) ? MATCH_PREFIX
+                                                          : MATCH_MULTIPLE_PREFIX;
+          else if (current_match < MATCH_PREFIX)
+            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
+                                                          : MATCH_MULTIPLE_SUBSTRING;
+        }
+
+      if (app->casefolded_description && current_match < MATCH_PREFIX)
+        {
+          /* Only do substring matches, as prefix matches are not meaningful
+           * enough for descriptions
+           */
+          p = strstr (app->casefolded_description, term);
+          if (p != NULL)
+            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
+                                                          : MATCH_MULTIPLE_SUBSTRING;
+        }
+
+      if (current_match == MATCH_NONE)
+        return current_match;
+
+      if (current_match > match)
+        match = current_match;
+    }
+  return match;
+}
+
+void
+_shell_app_do_match (ShellApp         *app,
+                     GSList           *terms,
+                     GSList          **multiple_prefix_results,
+                     GSList          **prefix_results,
+                     GSList          **multiple_substring_results,
+                     GSList          **substring_results)
+{
+  ShellAppSearchMatch match;
+  GAppInfo *appinfo;
+
+  g_assert (app != NULL);
+
+  /* Skip window-backed apps */ 
+  appinfo = (GAppInfo*)shell_app_get_app_info (app);
+  if (appinfo == NULL)
+    return;
+  /* Skip not-visible apps */ 
+  if (!g_app_info_should_show (appinfo))
+    return;
+
+  match = _shell_app_match_search_terms (app, terms);
+  switch (match)
+    {
+      case MATCH_NONE:
+        break;
+      case MATCH_MULTIPLE_PREFIX:
+        *multiple_prefix_results = g_slist_prepend (*multiple_prefix_results, app);
+        break;
+      case MATCH_PREFIX:
+        *prefix_results = g_slist_prepend (*prefix_results, app);
+        break;
+      case MATCH_MULTIPLE_SUBSTRING:
+        *multiple_substring_results = g_slist_prepend (*multiple_substring_results, app);
+        break;
+      case MATCH_SUBSTRING:
+        *substring_results = g_slist_prepend (*substring_results, app);
+        break;
+    }
+}
+
+
 static void
 shell_app_init (ShellApp *self)
 {
@@ -962,10 +1301,10 @@ shell_app_dispose (GObject *object)
 {
   ShellApp *app = SHELL_APP (object);
 
-  if (app->info)
+  if (app->entry)
     {
-      shell_app_info_unref (app->info);
-      app->info = NULL;
+      gmenu_tree_item_unref (app->entry);
+      app->entry = NULL;
     }
 
   if (app->running_state)
@@ -978,12 +1317,27 @@ shell_app_dispose (GObject *object)
 }
 
 static void
+shell_app_finalize (GObject *object)
+{
+  ShellApp *app = SHELL_APP (object);
+
+  g_free (app->window_id_string);
+
+  g_free (app->casefolded_name);
+  g_free (app->name_collation_key);
+  g_free (app->casefolded_description);
+
+  G_OBJECT_CLASS(shell_app_parent_class)->finalize (object);
+}
+
+static void
 shell_app_class_init(ShellAppClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   gobject_class->get_property = shell_app_get_property;
   gobject_class->dispose = shell_app_dispose;
+  gobject_class->finalize = shell_app_finalize;
 
   shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
                                      SHELL_TYPE_APP,
diff --git a/src/shell-app.h b/src/shell-app.h
index 729cfec..d1aed02 100644
--- a/src/shell-app.h
+++ b/src/shell-app.h
@@ -5,6 +5,8 @@
 #include <clutter/clutter.h>
 #include <gio/gio.h>
 #include <meta/window.h>
+#define GMENU_I_KNOW_THIS_IS_UNSTABLE
+#include <gmenu-tree.h>
 
 G_BEGIN_DECLS
 
@@ -34,12 +36,14 @@ typedef enum {
 GType shell_app_get_type (void) G_GNUC_CONST;
 
 const char *shell_app_get_id (ShellApp *app);
+GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
+GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
 
 ClutterActor *shell_app_create_icon_texture (ShellApp *app, float size);
 ClutterActor *shell_app_get_faded_icon (ShellApp *app, float size);
-char *shell_app_get_name (ShellApp *app);
-char *shell_app_get_description (ShellApp *app);
-gboolean shell_app_is_transient (ShellApp *app);
+const char *shell_app_get_name (ShellApp *app);
+const char *shell_app_get_description (ShellApp *app);
+gboolean shell_app_is_window_backed (ShellApp *app);
 
 void shell_app_activate_window (ShellApp *app, MetaWindow *window, guint32 timestamp);
 
@@ -61,6 +65,15 @@ GSList *shell_app_get_pids (ShellApp *app);
 
 gboolean shell_app_is_on_workspace (ShellApp *app, MetaWorkspace *workspace);
 
+gboolean shell_app_launch (ShellApp     *app,
+                           guint         timestamp,
+                           GList        *uris,
+                           int           workspace,
+                           char        **startup_id,
+                           GError      **error);
+
+int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
+
 int shell_app_compare (ShellApp *app, ShellApp *other);
 
 G_END_DECLS
diff --git a/src/shell-window-tracker.c b/src/shell-window-tracker.c
index 31d3e00..b864f10 100644
--- a/src/shell-window-tracker.c
+++ b/src/shell-window-tracker.c
@@ -18,7 +18,6 @@
 #include <libsn/sn.h>
 
 #include "shell-window-tracker-private.h"
-#include "shell-app-system.h"
 #include "shell-app-private.h"
 #include "shell-global.h"
 #include "shell-marshal.h"
@@ -244,6 +243,8 @@ get_app_from_window_wmclass (MetaWindow  *window)
   g_free (wmclass);
 
   app = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
+  if (app != NULL)
+    g_object_ref (app);
   g_free (with_desktop);
 
   return app;
@@ -340,19 +341,16 @@ get_app_from_window_pid (ShellWindowTracker  *tracker,
  */
 static ShellApp *
 get_app_for_window (ShellWindowTracker    *monitor,
-                    MetaWindow         *window)
+                    MetaWindow            *window)
 {
-  ShellApp *result;
+  ShellApp *result = NULL;
   const char *startup_id;
 
-  if (meta_window_is_remote (window))
-    return shell_app_system_get_app_for_window (shell_app_system_get_default (), window);
-
-  result = NULL;
   /* First, we check whether we already know about this window,
    * if so, just return that.
    */
-  if (meta_window_get_window_type (window) == META_WINDOW_NORMAL)
+  if (meta_window_get_window_type (window) == META_WINDOW_NORMAL
+      || meta_window_is_remote (window))
     {
       result = g_hash_table_lookup (monitor->window_to_app, window);
       if (result != NULL)
@@ -362,6 +360,9 @@ get_app_for_window (ShellWindowTracker    *monitor,
         }
     }
 
+  if (meta_window_is_remote (window))
+    return _shell_app_new_for_window (window);
+
   /* Check if the app's WM_CLASS specifies an app; this is
    * canonical if it does.
    */
@@ -389,7 +390,10 @@ get_app_for_window (ShellWindowTracker    *monitor,
 
           result = shell_startup_sequence_get_app (sequence);
           if (result)
-            break;
+            {
+              result = g_object_ref (result);
+              break;
+            }
         }
     }
 
@@ -401,7 +405,7 @@ get_app_for_window (ShellWindowTracker    *monitor,
 
   /* Our last resort - we create a fake app from the window */
   if (result == NULL)
-    result = shell_app_system_get_app_for_window (shell_app_system_get_default (), window);
+    result = _shell_app_new_for_window (window);
 
   return result;
 }
@@ -855,7 +859,7 @@ shell_startup_sequence_get_id (ShellStartupSequence *sequence)
  * shell_startup_sequence_get_app:
  * @sequence: A #ShellStartupSequence
  *
- * Returns: (transfer full): The application being launched, or %NULL if unknown.
+ * Returns: (transfer none): The application being launched, or %NULL if unknown.
  */
 ShellApp *
 shell_startup_sequence_get_app (ShellStartupSequence *sequence)
@@ -869,7 +873,7 @@ shell_startup_sequence_get_app (ShellStartupSequence *sequence)
     return NULL;
 
   appsys = shell_app_system_get_default ();
-  app = shell_app_system_get_app_for_path (appsys, appid);
+  app = shell_app_system_lookup_app_for_path (appsys, appid);
   return app;
 }
 



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