[gnome-documents: 5/10] search: rework search infrastructure



commit a0f97d2db29c9be5659eb9a5ae2b26e88e21e1b4
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Fri Dec 7 20:24:29 2012 -0500

    search: rework search infrastructure
    
    The final goal is to use the same process for the search provider and
    the application, to make it easy to support the new shell search
    provider API. In order to do this, some things that are currently global
    in Documents cannot be assumed global anymore - for example, the currrent
    search string cannot be shared between the provider and an existing
    window; we basically need to keep the search provider state separate
    from the rest of the application.
    
    This commit:
    - moves all the code related to search attributes in search.js
    - removes global.js and moves the state and controllers to Application
    - adds support for a different "context" to Manager and the shared
      controllers
    - moves ShellSearchProvider to be created from the application, with a
      --no-default-window option, and separate states/controllers
    - modifies Application, Embed and others to handle the provider service
      and closing/reopening of a window

 data/gnome-documents.desktop.in.in                |    2 +-
 src/Makefile-js.am                                |    4 +-
 src/Makefile.am                                   |   11 +-
 src/application.js                                |  297 +++++++-----
 src/changeMonitor.js                              |    4 +-
 src/documents.js                                  |  106 +---
 src/embed.js                                      |   60 ++-
 src/global.js                                     |   60 ---
 src/gnome-documents-search-provider.in            |    7 -
 src/main.js                                       |    3 +
 src/mainToolbar.js                                |   75 ++--
 src/mainWindow.js                                 |   30 +-
 src/manager.js                                    |    4 +-
 src/notifications.js                              |   14 +-
 src/offsetController.js                           |   97 ----
 src/org.gnome.Documents.SearchProvider.service.in |    2 +-
 src/preview.js                                    |   26 +-
 src/properties.js                                 |    6 +-
 src/search.js                                     |  554 +++++++++++++++++++++
 src/searchbar.js                                  |  293 ++----------
 src/selections.js                                 |   84 ++--
 src/shellSearchProvider.js                        |  125 ++----
 src/sources.js                                    |  237 ---------
 src/trackerController.js                          |   42 +-
 src/trackerUtils.js                               |   56 ++-
 src/utils.js                                      |    6 +-
 src/view.js                                       |   65 ++--
 27 files changed, 1091 insertions(+), 1179 deletions(-)
---
diff --git a/data/gnome-documents.desktop.in.in b/data/gnome-documents.desktop.in.in
index bd6929a..8c74bfd 100644
--- a/data/gnome-documents.desktop.in.in
+++ b/data/gnome-documents.desktop.in.in
@@ -1,7 +1,7 @@
 [Desktop Entry]
 _Name=Documents
 _Comment=Access, manage and share documents
-Exec=gnome-documents %U
+Exec=gnome-documents
 Icon=gnome-documents
 Terminal=false
 Type=Application
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index 39889a8..4f7a576 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -4,21 +4,19 @@ dist_js_DATA = \
     changeMonitor.js \
     documents.js \
     embed.js \
-    global.js \
     main.js \
     mainToolbar.js \
     mainWindow.js \
     manager.js \
     miners.js \
     notifications.js \
-    offsetController.js \
     preview.js \
     properties.js\
     query.js \
+    search.js \
     searchbar.js \
     selections.js \
     shellSearchProvider.js \
-    sources.js \
     trackerController.js \
     trackerUtils.js \
     utils.js \
diff --git a/src/Makefile.am b/src/Makefile.am
index 8e96338..204fa88 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -76,15 +76,6 @@ EXTRA_DIST += \
     gnome-documents.in \
     gnome-documents-debug.in
 
-libexec_SCRIPTS += gnome-documents-search-provider
-
-gnome-documents-search-provider: gnome-documents-search-provider.in
-	$(AM_V_GEN) $(do_subst) $< > $@
-	chmod +x $@
-
-CLEANFILES += gnome-documents-search-provider
-EXTRA_DIST += gnome-documents-search-provider.in
-
 service_in_files = org.gnome.Documents.SearchProvider.service.in
 
 servicedir = $(datadir)/dbus-1/services
@@ -92,7 +83,7 @@ service_DATA = $(service_in_files:.service.in=.service)
 
 %.service: %.service.in Makefile
 	$(AM_V_GEN) [ -d $(@D) ] || $(mkdir_p) $(@D) ; \
-	            sed -e "s|\ libexecdir\@|$(libexecdir)|" $< > $  tmp && mv $  tmp $@
+	            sed -e "s|\ bindir\@|$(bindir)|" $< > $  tmp && mv $  tmp $@
 
 CLEANFILES += $(service_DATA)
 EXTRA_DIST += $(service_in_files)
diff --git a/src/application.js b/src/application.js
index ea89aa0..4a716e4 100644
--- a/src/application.js
+++ b/src/application.js
@@ -46,7 +46,6 @@ const Tracker = imports.gi.Tracker;
 const ChangeMonitor = imports.changeMonitor;
 const Documents = imports.documents;
 const Format = imports.format;
-const Global = imports.global;
 const Main = imports.main;
 const MainWindow = imports.mainWindow;
 const MainToolbar = imports.mainToolbar;
@@ -56,13 +55,38 @@ const Notifications = imports.notifications;
 const Path = imports.path;
 const Properties = imports.properties;
 const Query = imports.query;
+const Search = imports.search;
 const Selections = imports.selections;
-const Sources = imports.sources;
+const ShellSearchProvider = imports.shellSearchProvider;
 const TrackerController = imports.trackerController;
+const TrackerUtils = imports.trackerUtils;
 const Tweener = imports.util.tweener;
 const Utils = imports.utils;
 const WindowMode = imports.windowMode;
 
+// used globally
+let application = null;
+let connection = null;
+let connectionQueue = null;
+let goaClient = null;
+let settings = null;
+
+// used by the application, but not by the search provider
+let changeMonitor = null;
+let collectionManager = null;
+let documentManager = null;
+let modeController = null;
+let notificationManager = null;
+let offsetController = null;
+let queryBuilder = null;
+let searchCategoryManager = null;
+let searchController = null;
+let searchMatchManager = null;
+let searchTypeManager = null;
+let selectionController = null;
+let sourceManager = null;
+let trackerController = null;
+
 const MINER_REFRESH_TIMEOUT = 60; /* seconds */
 
 const Application = new Lang.Class({
@@ -76,24 +100,23 @@ const Application = new Lang.Class({
         Gettext.textdomain('gnome-documents');
         GLib.set_prgname('gnome-documents');
 
-        Global.settings = new Gio.Settings({ schema: 'org.gnome.documents' });
-
         this.parent({ application_id: 'org.gnome.Documents',
-                      flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE });
+                      flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
+                      inactivity_timeout: 12000 });
     },
 
     _fullscreenCreateHook: function(action) {
-        Global.modeController.connect('can-fullscreen-changed', Lang.bind(this,
+        modeController.connect('can-fullscreen-changed', Lang.bind(this,
             function() {
-                let canFullscreen = Global.modeController.getCanFullscreen();
+                let canFullscreen = modeController.getCanFullscreen();
                 action.set_enabled(canFullscreen);
             }));
     },
 
     _viewAsCreateHook: function(action) {
-        Global.settings.connect('changed::view-as', Lang.bind(this,
+        settings.connect('changed::view-as', Lang.bind(this,
             function() {
-                action.state = Global.settings.get_value('view-as');
+                action.state = settings.get_value('view-as');
             }));
     },
 
@@ -116,21 +139,21 @@ const Application = new Lang.Class({
     },
 
     _onActionFullscreen: function() {
-        Global.modeController.toggleFullscreen();
+        modeController.toggleFullscreen();
     },
 
     _onActionViewAs: function(action, parameter) {
-        Global.settings.set_value('view-as', parameter);
+        settings.set_value('view-as', parameter);
     },
 
     _onActionOpenCurrent: function() {
-        let doc = Global.documentManager.getActiveItem();
+        let doc = documentManager.getActiveItem();
         if (doc)
             doc.open(this._mainWindow.window.get_screen(), Gtk.get_current_event_time());
     },
 
     _onActionPrintCurrent: function() {
-        let doc = Global.documentManager.getActiveItem();
+        let doc = documentManager.getActiveItem();
         if (doc)
             doc.print(this._mainWindow.window);
     },
@@ -141,7 +164,7 @@ const Application = new Lang.Class({
     },
 
     _onActionProperties: function() {
-        let doc = Global.documentManager.getActiveItem();
+        let doc = documentManager.getActiveItem();
         if (!doc)
             return;
 
@@ -153,65 +176,7 @@ const Application = new Lang.Class({
     },
 
     _initActions: function() {
-        let actionEntries = [
-            { name: 'quit',
-              callback: this._onActionQuit,
-              accel: '<Primary>q' },
-            { name: 'about',
-              callback: this._onActionAbout },
-            { name: 'help',
-              callback: this._onActionHelp,
-              accel: 'F1' },
-            { name: 'fullscreen',
-              callback: this._onActionFullscreen,
-              create_hook: this._fullscreenCreateHook,
-              accel: 'F11',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'gear-menu',
-              callback: this._onActionToggle,
-              state: GLib.Variant.new('b', false),
-              accel: 'F10',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'view-as',
-              callback: this._onActionViewAs,
-              create_hook: this._viewAsCreateHook,
-              parameter_type: 's',
-              state: Global.settings.get_value('view-as'),
-              window_mode: WindowMode.WindowMode.OVERVIEW },
-            { name: 'open-current',
-              callback: this._onActionOpenCurrent,
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'print-current', accel: '<Primary>p',
-              callback: this._onActionPrintCurrent,
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'search',
-              callback: this._onActionToggle,
-              state: GLib.Variant.new('b', false),
-              accel: '<Primary>f' },
-            { name: 'find-next', accel: '<Primary>g',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'find-prev', accel: '<Shift><Primary>g',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'zoom-in', accel: '<Primary>plus',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'zoom-in', accel: '<Primary>equal',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'zoom-out', accel: '<Primary>minus',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'rotate-left', accel: '<Primary>Left',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'rotate-right', accel: '<Primary>Right',
-              window_mode: WindowMode.WindowMode.PREVIEW },
-            { name: 'select-all', accel: '<Primary>a',
-              window_mode: WindowMode.WindowMode.OVERVIEW },
-            { name: 'select-none',
-              window_mode: WindowMode.WindowMode.OVERVIEW },
-            { name: 'properties',
-              callback: this._onActionProperties,
-              window_mode: WindowMode.WindowMode.PREVIEW }
-        ];
-
-        actionEntries.forEach(Lang.bind(this,
+        this._actionEntries.forEach(Lang.bind(this,
             function(actionEntry) {
                 let state = actionEntry.state;
                 let parameterType = actionEntry.parameter_type ?
@@ -233,15 +198,21 @@ const Application = new Lang.Class({
                 if (actionEntry.accel)
                     this.add_accelerator(actionEntry.accel, 'app.' + actionEntry.name, null);
 
+                this.add_action(action);
+            }));
+    },
+
+    _connectActionsToMode: function() {
+        this._actionEntries.forEach(Lang.bind(this,
+            function(actionEntry) {
                 if (actionEntry.window_mode) {
-                    Global.modeController.connect('window-mode-changed', Lang.bind(this,
+                    modeController.connect('window-mode-changed', Lang.bind(this,
                         function() {
-                            let mode = Global.modeController.getWindowMode();
+                            let mode = modeController.getWindowMode();
+                            let action = this.lookup_action(actionEntry.name);
                             action.set_enabled(mode == actionEntry.window_mode);
                         }));
                 }
-
-                this.add_action(action);
             }));
     },
 
@@ -284,7 +255,7 @@ const Application = new Lang.Class({
     },
 
     _refreshMiners: function() {
-        if (Global.sourceManager.hasProviderType('google')) {
+        if (sourceManager.hasProviderType('google')) {
             try {
                 // startup a refresh of the gdocs cache
                 this._refreshMinerNow(this.gdataMiner);
@@ -293,7 +264,7 @@ const Application = new Lang.Class({
             }
         }
 
-        if (Global.sourceManager.hasProviderType('windows_live')) {
+        if (sourceManager.hasProviderType('windows_live')) {
             try {
                 // startup a refresh of the skydrive cache
                 this._refreshMinerNow(this.zpjMiner);
@@ -308,8 +279,8 @@ const Application = new Lang.Class({
         this.zpjMiner = new Miners.ZpjMiner();
         this._refreshMiners();
 
-        Global.sourceManager.connect('item-added', Lang.bind(this, this._refreshMiners));
-        Global.sourceManager.connect('item-removed', Lang.bind(this, this._refreshMiners));
+        sourceManager.connect('item-added', Lang.bind(this, this._refreshMiners));
+        sourceManager.connect('item-removed', Lang.bind(this, this._refreshMiners));
     },
 
     vfunc_startup: function() {
@@ -323,70 +294,162 @@ const Application = new Lang.Class({
         let resource = Gio.Resource.load(Path.RESOURCE_DIR + '/gnome-documents.gresource');
         resource._register();
 
-        Global.application = this;
+        application = this;
+        settings = new Gio.Settings({ schema: 'org.gnome.documents' });
 
         // connect to tracker
         try {
-            Global.connection = Tracker.SparqlConnection.get(null);
+            connection = Tracker.SparqlConnection.get(null);
         } catch (e) {
             log('Unable to connect to the tracker database: ' + e.toString());
             return;
         }
 
         try {
-            Global.goaClient = Goa.Client.new_sync(null);
+            goaClient = Goa.Client.new_sync(null);
         } catch (e) {
             log('Unable to create the GOA client: ' + e.toString());
             return;
         }
 
-        Global.connectionQueue = new TrackerController.TrackerConnectionQueue();
-        Global.initSearch();
+        connectionQueue = new TrackerController.TrackerConnectionQueue();
+        this._searchProvider = new ShellSearchProvider.ShellSearchProvider();
+        this._searchProvider.connect('activate-result', Lang.bind(this, this._onActivateResult));
 
-        Global.changeMonitor = new ChangeMonitor.TrackerChangeMonitor();
-        Global.documentManager = new Documents.DocumentManager();
-        Global.trackerController = new TrackerController.TrackerController();
-        Global.selectionController = new Selections.SelectionController();
-        Global.modeController = new WindowMode.ModeController();
-        Global.notificationManager = new Notifications.NotificationManager();
+        // now init application components
+        Search.initSearch(imports.application);
+
+        changeMonitor = new ChangeMonitor.TrackerChangeMonitor();
+        documentManager = new Documents.DocumentManager();
+        trackerController = new TrackerController.TrackerController();
+        selectionController = new Selections.SelectionController();
+        modeController = new WindowMode.ModeController();
+        notificationManager = new Notifications.NotificationManager();
+
+        this._actionEntries = [
+            { name: 'quit',
+              callback: this._onActionQuit,
+              accel: '<Primary>q' },
+            { name: 'about',
+              callback: this._onActionAbout },
+            { name: 'help',
+              callback: this._onActionHelp,
+              accel: 'F1' },
+            { name: 'fullscreen',
+              callback: this._onActionFullscreen,
+              create_hook: this._fullscreenCreateHook,
+              accel: 'F11',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'gear-menu',
+              callback: this._onActionToggle,
+              state: GLib.Variant.new('b', false),
+              accel: 'F10',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'view-as',
+              callback: this._onActionViewAs,
+              create_hook: this._viewAsCreateHook,
+              parameter_type: 's',
+              state: settings.get_value('view-as'),
+              window_mode: WindowMode.WindowMode.OVERVIEW },
+            { name: 'open-current',
+              callback: this._onActionOpenCurrent,
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'print-current', accel: '<Primary>p',
+              callback: this._onActionPrintCurrent,
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'search',
+              callback: this._onActionToggle,
+              state: GLib.Variant.new('b', false),
+              accel: '<Primary>f' },
+            { name: 'find-next', accel: '<Primary>g',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'find-prev', accel: '<Shift><Primary>g',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'zoom-in', accel: '<Primary>plus',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'zoom-in', accel: '<Primary>equal',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'zoom-out', accel: '<Primary>minus',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'rotate-left', accel: '<Primary>Left',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'rotate-right', accel: '<Primary>Right',
+              window_mode: WindowMode.WindowMode.PREVIEW },
+            { name: 'select-all', accel: '<Primary>a',
+              window_mode: WindowMode.WindowMode.OVERVIEW },
+            { name: 'select-none',
+              window_mode: WindowMode.WindowMode.OVERVIEW },
+            { name: 'properties',
+              callback: this._onActionProperties,
+              window_mode: WindowMode.WindowMode.PREVIEW }
+        ];
 
         this._initMiners();
         this._initActions();
         this._initAppMenu();
+    },
+
+    _createWindow: function() {
+        if (this._mainWindow)
+            return;
+
+        notificationManager = new Notifications.NotificationManager();
+        this._connectActionsToMode();
         this._mainWindow = new MainWindow.MainWindow(this);
     },
 
     vfunc_activate: function() {
-        this._mainWindow.window.present();
+        if (this._mainWindow)
+            this._mainWindow.window.present();
     },
 
-    vfunc_command_line: function(commandLine) {
-        let args = commandLine.get_arguments();
-        if (args.length) {
-            Global.modeController.setWindowMode(WindowMode.WindowMode.PREVIEW);
-
-            let urn = args[0]; // gjs eats argv[0]
-            let doc = Global.documentManager.getItemById(args[0]);
-            if (doc) {
-                Global.documentManager.setActiveItem(doc);
-            } else {
-                let job = new Documents.SingleItemJob(urn);
-                job.run(Query.QueryFlags.UNFILTERED, Lang.bind(this,
-                    function(cursor) {
-                        if (!cursor)
-                            return;
-
-                        let doc = Global.documentManager.addDocumentFromCursor(cursor);
-                        Global.documentManager.setActiveItem(doc);
-                    }));
-            }
-        } else {
-            Global.modeController.setWindowMode(WindowMode.WindowMode.OVERVIEW);
-        }
+    vfunc_command_line: function(cmdline) {
+        let args = cmdline.get_arguments();
+        if (args.indexOf('--no-default-window') == -1)
+            this._createWindow();
 
+        modeController.setWindowMode(WindowMode.WindowMode.OVERVIEW);
         this.activate();
-
         return 0;
+    },
+
+    vfunc_window_removed: function(window) {
+        this.parent(window);
+        this._mainWindow = null;
+
+        // clean up signals
+        changeMonitor.disconnectAll();
+        documentManager.disconnectAll();
+        trackerController.disconnectAll();
+        selectionController.disconnectAll();
+        modeController.disconnectAll();
+
+        // reset state
+        documentManager.setActiveItem(null);
+        modeController.setWindowMode(WindowMode.WindowMode.NONE);
+        selectionController.setSelection(null);
+        notificationManager = null;
+    },
+
+    _onActivateResult: function(provider, urn) {
+        this._createWindow();
+        modeController.setWindowMode(WindowMode.WindowMode.PREVIEW);
+        this.activate();
+
+        let doc = documentManager.getItemById(urn);
+        if (doc) {
+            documentManager.setActiveItem(doc);
+        } else {
+            let job = new TrackerUtils.SingleItemJob(urn, queryBuilder);
+            job.run(Query.QueryFlags.UNFILTERED, Lang.bind(this,
+                function(cursor) {
+                    if (!cursor)
+                        return;
+
+                    let doc = documentManager.addDocumentFromCursor(cursor);
+                    documentManager.setActiveItem(doc);
+                }));
+        }
     }
 });
 Utils.addJSSignalMethods(Application.prototype);
diff --git a/src/changeMonitor.js b/src/changeMonitor.js
index 8c70b6c..ccd743d 100644
--- a/src/changeMonitor.js
+++ b/src/changeMonitor.js
@@ -24,7 +24,7 @@ const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const Signals = imports.signals;
 
-const Global = imports.global;
+const Application = imports.application;
 
 const TrackerResourcesServiceIface = <interface name='org.freedesktop.Tracker1.Resources'>
     <signal name="GraphUpdated">
@@ -139,7 +139,7 @@ const TrackerChangeMonitor = new Lang.Class({
         sparql += ' {}';
 
         // resolve all the unresolved IDs we got so far
-        Global.connectionQueue.add(sparql, null, Lang.bind(this,
+        Application.connectionQueue.add(sparql, null, Lang.bind(this,
             function(object, res) {
                 let cursor = object.query_finish(res);
 
diff --git a/src/documents.js b/src/documents.js
index 4e3694e..7cdee47 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -34,66 +34,16 @@ const _ = imports.gettext.gettext;
 const Lang = imports.lang;
 const Signals = imports.signals;
 
+const Application = imports.application;
 const ChangeMonitor = imports.changeMonitor;
-const Global = imports.global;
 const Manager = imports.manager;
 const Notifications = imports.notifications;
 const Path = imports.path;
 const Query = imports.query;
-const Searchbar = imports.searchbar;
+const Search = imports.search;
 const TrackerUtils = imports.trackerUtils;
 const Utils = imports.utils;
 
-const SingleItemJob = new Lang.Class({
-    Name: 'SingleItemJob',
-
-    _init: function(urn) {
-        this._urn = urn;
-        this._cursor = null;
-    },
-
-    run: function(flags, callback) {
-        this._callback = callback;
-
-        let query = Global.queryBuilder.buildSingleQuery(flags, this._urn);
-        Global.connectionQueue.add(query.sparql, null, Lang.bind(this,
-            function(object, res) {
-                try {
-                    let cursor = object.query_finish(res);
-                    cursor.next_async(null, Lang.bind(this, this._onCursorNext));
-                } catch (e) {
-                    log('Unable to query single item ' + e.toString());
-                    this._emitCallback();
-                }
-            }));
-    },
-
-    _onCursorNext: function(cursor, res) {
-        let valid = false;
-
-        try {
-            valid = cursor.next_finish(res);
-        } catch (e) {
-            log('Unable to query single item ' + e.toString());
-        }
-
-        if (!valid) {
-            cursor.close();
-            this._emitCallback();
-
-            return;
-        }
-
-        this._cursor = cursor;
-        this._emitCallback();
-        cursor.close();
-    },
-
-    _emitCallback: function() {
-        this._callback(this._cursor);
-    }
-});
-
 const DeleteItemJob = new Lang.Class({
     Name: 'DeleteItemJob',
 // deletes the given resource
@@ -105,8 +55,8 @@ const DeleteItemJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let query = Global.queryBuilder.buildDeleteResourceQuery(this._urn);
-        Global.connectionQueue.update(query.sparql, null, Lang.bind(this,
+        let query = Application.queryBuilder.buildDeleteResourceQuery(this._urn);
+        Application.connectionQueue.update(query.sparql, null, Lang.bind(this,
             function(object, res) {
                 try {
                     object.update_finish(res);
@@ -140,8 +90,8 @@ const CollectionIconWatcher = new Lang.Class({
     _start: function() {
         this._clear();
 
-        let query = Global.queryBuilder.buildCollectionIconQuery(this._collection.id);
-        Global.connectionQueue.add(query.sparql, null, Lang.bind(this,
+        let query = Application.queryBuilder.buildCollectionIconQuery(this._collection.id);
+        Application.connectionQueue.add(query.sparql, null, Lang.bind(this,
             function(object, res) {
                 let cursor = null;
                 try {
@@ -188,7 +138,7 @@ const CollectionIconWatcher = new Lang.Class({
 
         this._urns.forEach(Lang.bind(this,
             function(urn) {
-                let doc = Global.documentManager.getItemById(urn);
+                let doc = Application.documentManager.getItemById(urn);
                 if (doc)
                     this._docs.push(doc);
                 else
@@ -203,11 +153,11 @@ const CollectionIconWatcher = new Lang.Class({
 
         toQuery.forEach(Lang.bind(this,
             function(urn) {
-                let job = new SingleItemJob(urn);
+                let job = new TrackerUtils.SingleItemJob(urn, Application.queryBuilder);
                 job.run(Query.QueryFlags.UNFILTERED, Lang.bind(this,
                     function(cursor) {
                         if (cursor) {
-                            let doc = Global.documentManager.createDocumentFromCursor(cursor);
+                            let doc = Application.documentManager.createDocumentFromCursor(cursor);
                             this._docs.push(doc);
                         }
 
@@ -297,15 +247,15 @@ const DocCommon = new Lang.Class({
         this.populateFromCursor(cursor);
 
         this._refreshIconId =
-            Global.settings.connect('changed::view-as',
-                                    Lang.bind(this, this.refreshIcon));
+            Application.settings.connect('changed::view-as',
+                                         Lang.bind(this, this.refreshIcon));
         this._filterId =
-            Global.searchCategoryManager.connect('active-changed',
-                                                 Lang.bind(this, this.refreshIcon));
+            Application.searchCategoryManager.connect('active-changed',
+                                                      Lang.bind(this, this.refreshIcon));
     },
 
     refresh: function() {
-        let job = new SingleItemJob(this.id);
+        let job = new TrackerUtils.SingleItemJob(this.id, Application.queryBuilder);
         job.run(Query.QueryFlags.NONE, Lang.bind(this,
             function(cursor) {
                 if (!cursor)
@@ -541,11 +491,11 @@ const DocCommon = new Lang.Class({
         // save the pixbuf before modifications, to use in collection icons
         this.pristinePixbuf = pixbuf;
 
-        activeItem = Global.searchCategoryManager.getActiveItem();
+        activeItem = Application.searchCategoryManager.getActiveItem();
 
         if (this.shared &&
             (!activeItem ||
-             (activeItem.id != Searchbar.SearchCategoryStock.SHARED)))
+             (activeItem.id != Search.SearchCategoryStock.SHARED)))
             emblemIcons.push(this._createSymbolicEmblem('emblem-shared'));
 
         if (emblemIcons.length > 0) {
@@ -589,8 +539,8 @@ const DocCommon = new Lang.Class({
             this._collectionIconWatcher = null;
         }
 
-        Global.settings.disconnect(this._refreshIconId);
-        Global.searchCategoryManager.disconnect(this._filterId);
+        Application.settings.disconnect(this._refreshIconId);
+        Application.searchCategoryManager.disconnect(this._filterId);
     },
 
     open: function(screen, timestamp) {
@@ -695,7 +645,7 @@ const GoogleDocument = new Lang.Class({
     },
 
     _createGDataEntry: function(cancellable, callback) {
-        let source = Global.sourceManager.getItemById(this.resourceUrn);
+        let source = Application.sourceManager.getItemById(this.resourceUrn);
 
         let authorizer = new GData.GoaAuthorizer({ goa_object: source.object });
         let service = new GData.DocumentsService({ authorizer: authorizer });
@@ -793,7 +743,7 @@ const SkydriveDocument = new Lang.Class({
     },
 
     _createZpjEntry: function(cancellable, callback) {
-        let source = Global.sourceManager.getItemById(this.resourceUrn);
+        let source = Application.sourceManager.getItemById(this.resourceUrn);
 
         let authorizer = new Zpj.GoaAuthorizer({ goa_object: source.object });
         let service = new Zpj.Skydrive({ authorizer: authorizer });
@@ -885,8 +835,8 @@ const DocumentManager = new Lang.Class({
         // navigate to the active document or collection
         this._collectionPath = [];
 
-        Global.changeMonitor.connect('changes-pending',
-                                     Lang.bind(this, this._onChangesPending));
+        Application.changeMonitor.connect('changes-pending',
+                                          Lang.bind(this, this._onChangesPending));
     },
 
     _onChangesPending: function(monitor, changes) {
@@ -908,14 +858,14 @@ const DocumentManager = new Lang.Class({
                     this.removeItemById(changeEvent.urn);
 
                     if (doc.collection)
-                        Global.collectionManager.removeItemById(changeEvent.urn);
+                        Application.collectionManager.removeItemById(changeEvent.urn);
                 }
             }
         }
     },
 
     _onDocumentCreated: function(urn) {
-        let job = new SingleItemJob(urn);
+        let job = new TrackerUtils.SingleItemJob(urn, Application.queryBuilder);
         job.run(Query.QueryFlags.NONE, Lang.bind(this,
             function(cursor) {
                 if (!cursor)
@@ -954,7 +904,7 @@ const DocumentManager = new Lang.Class({
         this.addItem(doc);
 
         if (doc.collection)
-            Global.collectionManager.addItem(doc);
+            Application.collectionManager.addItem(doc);
 
         return doc;
     },
@@ -1002,8 +952,8 @@ const DocumentManager = new Lang.Class({
             return;
 
         if (doc.collection) {
-            this._collectionPath.push(Global.collectionManager.getActiveItem());
-            Global.collectionManager.setActiveItem(doc);
+            this._collectionPath.push(Application.collectionManager.getActiveItem());
+            Application.collectionManager.setActiveItem(doc);
             return;
         }
 
@@ -1017,7 +967,7 @@ const DocumentManager = new Lang.Class({
 
     activatePreviousCollection: function() {
         this._clearActiveDocModel();
-        Global.collectionManager.setActiveItem(this._collectionPath.pop());
+        Application.collectionManager.setActiveItem(this._collectionPath.pop());
     },
 
     _clearActiveDocModel: function() {
diff --git a/src/embed.js b/src/embed.js
index 83cc0bc..ae5405f 100644
--- a/src/embed.js
+++ b/src/embed.js
@@ -23,11 +23,10 @@ const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const Tweener = imports.util.tweener;
 
-const Global = imports.global;
+const Application = imports.application;
 const MainToolbar = imports.mainToolbar;
 const Notifications = imports.notifications;
 const Preview = imports.preview;
-const Searchbar = imports.searchbar;
 const Selections = imports.selections;
 const View = imports.view;
 const WindowMode = imports.windowMode;
@@ -227,7 +226,7 @@ const EmptyResultsBox = new Lang.Class({
                                          vexpand: true });
         this._labelsGrid.add(titleLabel);
 
-        if (Global.sourceManager.hasOnlineSources()) {
+        if (Application.sourceManager.hasOnlineSources()) {
             titleLabel.valign = Gtk.Align.CENTER;
         } else {
             titleLabel.valign = Gtk.Align.START;
@@ -381,7 +380,7 @@ const Embed = new Lang.Class({
             Clutter.BinAlignment.FIXED, Clutter.BinAlignment.FIXED);
 
         // pack the OSD notification actor
-        this._viewActor.add_child(Global.notificationManager.actor);
+        this._viewActor.add_child(Application.notificationManager.actor);
 
         // now create the actual content widgets
         this._view = new View.ViewContainer();
@@ -390,32 +389,37 @@ const Embed = new Lang.Class({
         this._preview = new Preview.PreviewView();
         this._previewPage = this._notebook.append_page(this._preview.widget, null);
 
-        Global.modeController.connect('window-mode-changed',
-                                      Lang.bind(this, this._onWindowModeChanged));
-        Global.modeController.connect('fullscreen-changed',
-                                      Lang.bind(this, this._onFullscreenChanged));
-        Global.trackerController.connect('query-status-changed',
-                                         Lang.bind(this, this._onQueryStatusChanged));
-        Global.trackerController.connect('query-error',
-                                         Lang.bind(this, this._onQueryError));
+        Application.modeController.connect('window-mode-changed',
+                                           Lang.bind(this, this._onWindowModeChanged));
 
-        Global.offsetController.connect('item-count-changed',
+        Application.modeController.connect('fullscreen-changed',
+                                           Lang.bind(this, this._onFullscreenChanged));
+        Application.trackerController.connect('query-status-changed',
+                                              Lang.bind(this, this._onQueryStatusChanged));
+        Application.trackerController.connect('query-error',
+                                              Lang.bind(this, this._onQueryError));
+
+        Application.offsetController.connect('item-count-changed',
                                         Lang.bind(this, this._onItemCountChanged));
 
-        Global.documentManager.connect('active-changed',
-                                       Lang.bind(this, this._onActiveItemChanged));
-        Global.documentManager.connect('load-started',
-                                       Lang.bind(this, this._onLoadStarted));
-        Global.documentManager.connect('load-finished',
-                                       Lang.bind(this, this._onLoadFinished));
-        Global.documentManager.connect('load-error',
-                                       Lang.bind(this, this._onLoadError));
+        Application.documentManager.connect('active-changed',
+                                            Lang.bind(this, this._onActiveItemChanged));
+        Application.documentManager.connect('load-started',
+                                            Lang.bind(this, this._onLoadStarted));
+        Application.documentManager.connect('load-finished',
+                                            Lang.bind(this, this._onLoadFinished));
+        Application.documentManager.connect('load-error',
+                                            Lang.bind(this, this._onLoadError));
 
         this._onQueryStatusChanged();
+
+        let windowMode = Application.modeController.getWindowMode();
+        if (windowMode != WindowMode.WindowMode.NONE)
+            this._onWindowModeChanged(Application.modeController, windowMode, WindowMode.WindowMode.NONE);
     },
 
     _onQueryStatusChanged: function() {
-        let queryStatus = Global.trackerController.getQueryStatus();
+        let queryStatus = Application.trackerController.getQueryStatus();
 
         if (queryStatus) {
             this._errorBox.moveOut();
@@ -427,7 +431,7 @@ const Embed = new Lang.Class({
 
     _hideNoResultsPage: function() {
         if (this._noResultsChangeId != 0) {
-            Global.changeMonitor.disconnect(this._noResultsChangeId);
+            Application.changeMonitor.disconnect(this._noResultsChangeId);
             this._noResultsChangeId = 0;
         }
 
@@ -435,12 +439,12 @@ const Embed = new Lang.Class({
     },
 
     _onItemCountChanged: function() {
-        let itemCount = Global.offsetController.getItemCount();
+        let itemCount = Application.offsetController.getItemCount();
 
         if (itemCount == 0) {
             // also listen to changes-pending while in this mode
             this._noResultsChangeId =
-                Global.changeMonitor.connect('changes-pending', Lang.bind(this,
+                Application.changeMonitor.connect('changes-pending', Lang.bind(this,
                     function() {
                         this._hideNoResultsPage();
                     }));
@@ -498,12 +502,12 @@ const Embed = new Lang.Class({
         let newMode = WindowMode.WindowMode.OVERVIEW;
 
         if (doc) {
-            let collection = Global.collectionManager.getItemById(doc.id);
+            let collection = Application.collectionManager.getItemById(doc.id);
             if (!collection)
                 newMode = WindowMode.WindowMode.PREVIEW;
         }
 
-        Global.modeController.setWindowMode(newMode);
+        Application.modeController.setWindowMode(newMode);
     },
 
     _onLoadStarted: function() {
@@ -518,7 +522,7 @@ const Embed = new Lang.Class({
         this._preview.widget.grab_focus();
 
         this._spinnerBox.moveOut();
-        Global.modeController.setCanFullscreen(true);
+        Application.modeController.setCanFullscreen(true);
     },
 
     _onLoadError: function(manager, doc, message, exception) {
diff --git a/src/main.js b/src/main.js
index f3545c6..9a3adb3 100644
--- a/src/main.js
+++ b/src/main.js
@@ -20,8 +20,11 @@
  */
 
 const Application = imports.application;
+const GLib = imports.gi.GLib;
 
 function start() {
     let application = new Application.Application();
+    if (GLib.getenv('DOCUMENTS_PERSIST'))
+        application.hold();
     return application.run(ARGV);
 }
diff --git a/src/mainToolbar.js b/src/mainToolbar.js
index 73cb0fa..a9c3c4c 100644
--- a/src/mainToolbar.js
+++ b/src/mainToolbar.js
@@ -33,7 +33,7 @@ const _ = imports.gettext.gettext;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 
-const Global = imports.global;
+const Application = imports.application;
 const Searchbar = imports.searchbar;
 const Tweener = imports.util.tweener;
 
@@ -88,21 +88,16 @@ const OverviewToolbar = new Lang.Class({
         this.parent();
 
         // setup listeners to mode changes that affect the toolbar layout
-        this._searchStringId =
-            Global.searchController.connect('search-string-changed',
-                                            Lang.bind(this, this._setToolbarTitle));
-        this._searchTypeId =
-            Global.searchTypeManager.connect('active-changed',
-                                             Lang.bind(this, this._setToolbarTitle));
-        this._searchMatchId =
-            Global.searchMatchManager.connect('active-changed',
-                                              Lang.bind(this, this._setToolbarTitle));
-        this._searchSourceId =
-            Global.sourceManager.connect('active-changed',
-                                         Lang.bind(this, this._setToolbarTitle));
-        this._selectionModeId =
-            Global.selectionController.connect('selection-mode-changed',
-                                               Lang.bind(this, this._resetToolbarMode));
+        this._searchStringId = Application.searchController.connect('search-string-changed',
+            Lang.bind(this, this._setToolbarTitle));
+        this._searchTypeId = Application.searchTypeManager.connect('active-changed',
+            Lang.bind(this, this._setToolbarTitle));
+        this._searchMatchId = Application.searchMatchManager.connect('active-changed',
+            Lang.bind(this, this._setToolbarTitle));
+        this._searchSourceId = Application.sourceManager.connect('active-changed',
+            Lang.bind(this, this._setToolbarTitle));
+        this._selectionModeId = Application.selectionController.connect('selection-mode-changed',
+            Lang.bind(this, this._resetToolbarMode));
         this._resetToolbarMode();
 
         this.widget.connect('destroy', Lang.bind(this,
@@ -110,35 +105,35 @@ const OverviewToolbar = new Lang.Class({
                 this._clearStateData();
 
                 if (this._selectionModeId != 0) {
-                    Global.selectionController.disconnect(this._selectionModeId);
+                    Application.selectionController.disconnect(this._selectionModeId);
                     this._selectionModeId = 0;
                 }
 
                 if (this._searchStringId != 0) {
-                    Global.searchController.disconnect(this._searchStringId);
+                    Application.searchController.disconnect(this._searchStringId);
                     this._searchStringId = 0;
                 }
 
                 if (this._searchTypeId != 0) {
-                    Global.searchTypeManager.disconnect(this._searchTypeId);
+                    Application.searchTypeManager.disconnect(this._searchTypeId);
                     this._searchTypeId = 0;
                 }
 
                 if (this._searchMatchId != 0) {
-                    Global.searchMatchManager.disconnect(this._searchMatchId);
+                    Application.searchMatchManager.disconnect(this._searchMatchId);
                     this._searchMatchId = 0;
                 }
 
                 if (this._searchSourceId != 0) {
-                    Global.sourceManager.disconnect(this._searchSourceId);
+                    Application.sourceManager.disconnect(this._searchSourceId);
                     this._searchSourceId = 0;
                 }
             }));
     },
 
     _setToolbarTitle: function() {
-        let selectionMode = Global.selectionController.getSelectionMode();
-        let activeCollection = Global.collectionManager.getActiveItem();
+        let selectionMode = Application.selectionController.getSelectionMode();
+        let activeCollection = Application.collectionManager.getActiveItem();
         let primary = null;
         let detail = null;
 
@@ -146,11 +141,11 @@ const OverviewToolbar = new Lang.Class({
             if (activeCollection) {
                 primary = activeCollection.name;
             } else {
-                let string = Global.searchController.getString();
+                let string = Application.searchController.getString();
 
                 if (string == '') {
-                    let searchType = Global.searchTypeManager.getActiveItem();
-                    let searchSource = Global.sourceManager.getActiveItem();
+                    let searchType = Application.searchTypeManager.getActiveItem();
+                    let searchSource = Application.sourceManager.getActiveItem();
 
                     if (searchType.id != 'all')
                         primary = searchType.name;
@@ -160,7 +155,7 @@ const OverviewToolbar = new Lang.Class({
                     if (searchSource.id != 'all')
                         detail = searchSource.name;
                 } else {
-                    let searchMatch = Global.searchMatchManager.getActiveItem();
+                    let searchMatch = Application.searchMatchManager.getActiveItem();
 
                     primary = _("Results for \"%s\"").format(string);
                     if (searchMatch.id == 'title')
@@ -170,7 +165,7 @@ const OverviewToolbar = new Lang.Class({
                 }
             }
         } else {
-            let length = Global.selectionController.getSelection().length;
+            let length = Application.selectionController.getSelection().length;
 
             if (length == 0)
                 detail = _("Click on items to select them");
@@ -209,24 +204,24 @@ const OverviewToolbar = new Lang.Class({
         selectionButton.get_style_context().add_class('suggested-action');
         selectionButton.connect('clicked', Lang.bind(this,
             function() {
-                Global.selectionController.setSelectionMode(false);
+                Application.selectionController.setSelectionMode(false);
             }));
 
         // connect to selection changes while in this mode
         this._selectionChangedId =
-            Global.selectionController.connect('selection-changed',
+            Application.selectionController.connect('selection-changed',
                                                Lang.bind(this, this._setToolbarTitle));
     },
 
     _checkCollectionBackButton: function() {
-        let item = Global.collectionManager.getActiveItem();
+        let item = Application.collectionManager.getActiveItem();
 
         if (item && !this._collBackButton) {
             this._collBackButton =
                 this.widget.add_button('go-previous-symbolic', _("Back"), true);
             this._collBackButton.connect('clicked', Lang.bind(this,
                 function() {
-                    Global.documentManager.activatePreviousCollection();
+                    Application.documentManager.activatePreviousCollection();
                 }));
         } else if (!item && this._collBackButton) {
             this._collBackButton.destroy();
@@ -237,7 +232,7 @@ const OverviewToolbar = new Lang.Class({
     _onActiveCollectionChanged: function() {
         this._checkCollectionBackButton();
         this._setToolbarTitle();
-        Global.application.change_action_state('search', GLib.Variant.new('b', false));
+        Application.application.change_action_state('search', GLib.Variant.new('b', false));
     },
 
     _populateForOverview: function() {
@@ -248,12 +243,12 @@ const OverviewToolbar = new Lang.Class({
             this.widget.add_button('object-select-symbolic', _("Select Items"), false);
         selectionButton.connect('clicked', Lang.bind(this,
             function() {
-                Global.selectionController.setSelectionMode(true);
+                Application.selectionController.setSelectionMode(true);
             }));
 
         // connect to active collection changes while in this mode
         this._collectionId =
-            Global.collectionManager.connect('active-changed',
+            Application.collectionManager.connect('active-changed',
                                              Lang.bind(this, this._onActiveCollectionChanged));
     },
 
@@ -262,12 +257,12 @@ const OverviewToolbar = new Lang.Class({
         this.widget.set_labels_menu(null);
 
         if (this._collectionId != 0) {
-            Global.collectionManager.disconnect(this._collectionId);
+            Application.collectionManager.disconnect(this._collectionId);
             this._collectionId = 0;
         }
 
         if (this._selectionChangedId != 0) {
-            Global.selectionController.disconnect(this._selectionChangedId);
+            Application.selectionController.disconnect(this._selectionChangedId);
             this._selectionChangedId = 0;
         }
     },
@@ -283,7 +278,7 @@ const OverviewToolbar = new Lang.Class({
     _resetToolbarMode: function() {
         this._clearToolbar();
 
-        let selectionMode = Global.selectionController.getSelectionMode();
+        let selectionMode = Application.selectionController.getSelectionMode();
         if (selectionMode)
             this._populateForSelectionMode();
         else
@@ -292,8 +287,8 @@ const OverviewToolbar = new Lang.Class({
         this._setToolbarTitle();
         this.widget.show_all();
 
-        if (Global.searchController.getString() != '')
-            Global.application.change_action_state('search', GLib.Variant.new('b', true));
+        if (Application.searchController.getString() != '')
+            Application.application.change_action_state('search', GLib.Variant.new('b', true));
     },
 
     createSearchbar: function() {
diff --git a/src/mainWindow.js b/src/mainWindow.js
index cc0b702..9c8b707 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -28,9 +28,9 @@ const GtkClutter = imports.gi.GtkClutter;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 
+const Application = imports.application;
 const Config = imports.config;
 const Embed = imports.embed;
-const Global = imports.global;
 const Selections = imports.selections;
 const Utils = imports.utils;
 const WindowMode = imports.windowMode;
@@ -51,7 +51,7 @@ const MainWindow = new Lang.Class({
 						  title: _("Documents") });
 
         // apply the last saved window size and position
-        let size = Global.settings.get_value('window-size');
+        let size = Application.settings.get_value('window-size');
         if (size.n_children() == 2) {
             let width = size.get_child_value(0);
             let height = size.get_child_value(1);
@@ -60,7 +60,7 @@ const MainWindow = new Lang.Class({
                                          height.get_int32());
         }
 
-        let position = Global.settings.get_value('window-position');
+        let position = Application.settings.get_value('window-position');
         if (position.n_children() == 2) {
             let x = position.get_child_value(0);
             let y = position.get_child_value(1);
@@ -69,7 +69,7 @@ const MainWindow = new Lang.Class({
                              y.get_int32());
         }
 
-        if (Global.settings.get_boolean('window-maximized'))
+        if (Application.settings.get_boolean('window-maximized'))
             this.window.maximize();
 
         this.window.connect('delete-event',
@@ -81,8 +81,8 @@ const MainWindow = new Lang.Class({
         this.window.connect('window-state-event',
                             Lang.bind(this, this._onWindowStateEvent));
 
-        Global.modeController.connect('fullscreen-changed',
-                                      Lang.bind(this, this._onFullscreenChanged));
+        this._fsId = Application.modeController.connect('fullscreen-changed',
+            Lang.bind(this, this._onFullscreenChanged));
 
         this._embed = new Embed.Embed();
         this.window.add(this._embed.widget);
@@ -98,15 +98,15 @@ const MainWindow = new Lang.Class({
         // GLib.Variant.new() can handle arrays just fine
         let size = this.window.get_size();
         let variant = GLib.Variant.new ('ai', size);
-        Global.settings.set_value('window-size', variant);
+        Application.settings.set_value('window-size', variant);
 
         let position = this.window.get_position();
         variant = GLib.Variant.new ('ai', position);
-        Global.settings.set_value('window-position', variant);
+        Application.settings.set_value('window-position', variant);
     },
 
     _onConfigureEvent: function(widget, event) {
-        if (Global.modeController.getFullscreen())
+        if (Application.modeController.getFullscreen())
             return;
 
         if (this._configureId != 0) {
@@ -129,7 +129,7 @@ const MainWindow = new Lang.Class({
             return;
 
         let maximized = (state & Gdk.WindowState.MAXIMIZED);
-        Global.settings.set_boolean('window-maximized', maximized);
+        Application.settings.set_boolean('window-maximized', maximized);
     },
 
     _onFullscreenChanged: function(controller, fullscreen) {
@@ -145,7 +145,7 @@ const MainWindow = new Lang.Class({
         if (toolbar.handleEvent(event))
             return true;
 
-        if (Global.modeController.getWindowMode() == WindowMode.WindowMode.PREVIEW)
+        if (Application.modeController.getWindowMode() == WindowMode.WindowMode.PREVIEW)
             return this._handleKeyPreview(event);
         else
             return this._handleKeyOverview(event);
@@ -154,7 +154,7 @@ const MainWindow = new Lang.Class({
     _handleKeyPreview: function(event) {
         let keyval = event.get_keyval()[1];
         let state = event.get_state()[1];
-        let fullscreen = Global.modeController.getFullscreen();
+        let fullscreen = Application.modeController.getFullscreen();
         let direction = this.window.get_direction();
 
         if ((fullscreen && keyval == Gdk.KEY_Escape) ||
@@ -162,7 +162,7 @@ const MainWindow = new Lang.Class({
              (direction == Gtk.TextDirection.LTR && keyval == Gdk.KEY_Left) ||
              (direction == Gtk.TextDirection.RTL && keyval == Gdk.KEY_Right)) ||
             keyval == Gdk.KEY_Back) {
-            Global.documentManager.setActiveItem(null);
+            Application.documentManager.setActiveItem(null);
             return true;
         }
 
@@ -172,9 +172,9 @@ const MainWindow = new Lang.Class({
     _handleKeyOverview: function(event) {
         let keyval = event.get_keyval()[1];
 
-        if (Global.selectionController.getSelectionMode() &&
+        if (Application.selectionController.getSelectionMode() &&
             keyval == Gdk.KEY_Escape) {
-            Global.selectionController.setSelectionMode(false);
+            Application.selectionController.setSelectionMode(false);
             return true;
         }
 
diff --git a/src/manager.js b/src/manager.js
index 17171f3..16d050c 100644
--- a/src/manager.js
+++ b/src/manager.js
@@ -31,13 +31,15 @@ const Signals = imports.signals;
 const BaseManager = new Lang.Class({
     Name: 'BaseManager',
 
-    _init: function(title) {
+    _init: function(title, context) {
         this._items = {};
         this._activeItem = null;
         this._title = null;
 
         if (title)
             this._title = title;
+
+        this.context = context;
     },
 
     getTitle: function() {
diff --git a/src/notifications.js b/src/notifications.js
index b001b07..634334f 100644
--- a/src/notifications.js
+++ b/src/notifications.js
@@ -28,7 +28,7 @@ const GtkClutter = imports.gi.GtkClutter;
 const TrackerMiner = imports.gi.TrackerMiner;
 const _ = imports.gettext.gettext;
 
-const Global = imports.global;
+const Application = imports.application;
 const Utils = imports.utils;
 
 const Lang = imports.lang;
@@ -75,7 +75,7 @@ const PrintNotification = new Lang.Class({
                 this.widget.destroy();
             }));
 
-        Global.notificationManager.addNotification(this);
+        Application.notificationManager.addNotification(this);
     },
 
     _onPrintStatus: function() {
@@ -114,7 +114,7 @@ const IndexingNotification = new Lang.Class({
             return;
         }
 
-        Global.application.connectJS('miners-changed', Lang.bind(this, this._checkNotification));
+        Application.application.connectJS('miners-changed', Lang.bind(this, this._checkNotification));
         Mainloop.idle_add(Lang.bind(this,
             function() {
                 this._checkNotification();
@@ -136,7 +136,7 @@ const IndexingNotification = new Lang.Class({
             }
         }
 
-        if (Global.application.minersRunning.length > 0)
+        if (Application.application.minersRunning.length > 0)
             isIndexingRemote = true;
 
         if (isIndexingLocal) {
@@ -155,8 +155,8 @@ const IndexingNotification = new Lang.Class({
 
         let primary = null;
 
-        if (Global.application.minersRunning.length == 1) {
-            let miner = Global.application.minersRunning[0];
+        if (Application.application.minersRunning.length == 1) {
+            let miner = Application.application.minersRunning[0];
             primary = _("Fetching documents from %s").format(miner.DisplayName);
         } else {
             primary = _("Fetching documents from online accounts");
@@ -207,7 +207,7 @@ const IndexingNotification = new Lang.Class({
             }));
         this.widget.add(close);
 
-        Global.notificationManager.addNotification(this);
+        Application.notificationManager.addNotification(this);
     },
 
     _update: function(primaryText, secondaryText) {
diff --git a/src/org.gnome.Documents.SearchProvider.service.in b/src/org.gnome.Documents.SearchProvider.service.in
index 0d520d6..fdb7e97 100644
--- a/src/org.gnome.Documents.SearchProvider.service.in
+++ b/src/org.gnome.Documents.SearchProvider.service.in
@@ -1,3 +1,3 @@
 [D-BUS Service]
 Name=org.gnome.Documents.SearchProvider
-Exec= libexecdir@/gnome-documents-search-provider
+Exec= bindir@/gnome-documents --no-default-window
diff --git a/src/preview.js b/src/preview.js
index 1a57371..eaa8860 100644
--- a/src/preview.js
+++ b/src/preview.js
@@ -31,7 +31,7 @@ const _ = imports.gettext.gettext;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 
-const Global = imports.global;
+const Application = imports.application;
 const Tweener = imports.util.tweener;
 const MainToolbar = imports.mainToolbar;
 const Searchbar = imports.searchbar;
@@ -54,37 +54,37 @@ const PreviewView = new Lang.Class({
         this._createView();
         this.widget.show_all();
 
-        this._zoomIn = Global.application.lookup_action('zoom-in');
+        this._zoomIn = Application.application.lookup_action('zoom-in');
         this._zoomIn.connect('activate', Lang.bind(this,
             function() {
                 this._model.set_sizing_mode(EvView.SizingMode.FREE);
                 this.view.zoom_in();
             }));
 
-        this._zoomOut = Global.application.lookup_action('zoom-out');
+        this._zoomOut = Application.application.lookup_action('zoom-out');
         this._zoomOut.connect('activate', Lang.bind(this,
             function() {
                 this._model.set_sizing_mode(EvView.SizingMode.FREE);
                 this.view.zoom_out();
             }));
 
-        this._findPrev = Global.application.lookup_action('find-prev');
+        this._findPrev = Application.application.lookup_action('find-prev');
         this._findPrev.connect('activate', Lang.bind(this,
             function() {
                 this.view.find_previous();
             }));
-        this._findNext = Global.application.lookup_action('find-next');
+        this._findNext = Application.application.lookup_action('find-next');
         this._findNext.connect('activate', Lang.bind(this,
             function() {
                 this.view.find_next();
             }));
 
-        let rotLeft = Global.application.lookup_action('rotate-left');
+        let rotLeft = Application.application.lookup_action('rotate-left');
         rotLeft.connect('activate', Lang.bind(this,
             function() {
                 this._changeRotation(-90);
             }));
-        let rotRight = Global.application.lookup_action('rotate-right');
+        let rotRight = Application.application.lookup_action('rotate-right');
         rotRight.connect('activate', Lang.bind(this,
             function() {
                 this._changeRotation(90);
@@ -137,7 +137,7 @@ const PreviewView = new Lang.Class({
         let clickCount = event.get_click_count()[1];
 
         if (button == 1 && clickCount == 2) {
-            Global.modeController.toggleFullscreen();
+            Application.modeController.toggleFullscreen();
             return true;
         }
 
@@ -324,7 +324,7 @@ const PreviewFullscreen = new Lang.Class({
     },
 
     _fullscreenMotionHandler: function() {
-        if (!Global.modeController.getFullscreen())
+        if (!Application.modeController.getFullscreen())
             return;
 
         // if we were idle fade in the toolbar, otherwise reset
@@ -363,7 +363,7 @@ const PreviewToolbar = new Lang.Class({
             this.widget.add_button(iconName, _("Back"), true);
         backButton.connect('clicked', Lang.bind(this,
             function() {
-                Global.documentManager.setActiveItem(null);
+                Application.documentManager.setActiveItem(null);
             }));
 
         // search button, on the right of the toolbar
@@ -384,7 +384,7 @@ const PreviewToolbar = new Lang.Class({
         builder.add_from_resource('/org/gnome/documents/preview-menu.ui');
         let menu = builder.get_object('preview-menu');
 
-        let doc = Global.documentManager.getActiveItem();
+        let doc = Application.documentManager.getActiveItem();
         if (doc && doc.defaultAppName) {
             let section = builder.get_object('open-section');
             section.remove(0);
@@ -404,7 +404,7 @@ const PreviewToolbar = new Lang.Class({
     _setToolbarTitle: function() {
         let primary = null;
         let detail = null;
-        let doc = Global.documentManager.getActiveItem();
+        let doc = Application.documentManager.getActiveItem();
 
         if (doc)
             primary = doc.name;
@@ -457,7 +457,7 @@ const PreviewSearchbar = new Lang.Class({
         this._searchEntry = new Gtk.SearchEntry({ width_request: 500 });
         this._searchEntry.connect('activate', Lang.bind(this,
             function() {
-                Global.application.activate_action('find-next', null);
+                Application.application.activate_action('find-next', null);
             }));
         this._searchContainer.add(this._searchEntry);
 
diff --git a/src/properties.js b/src/properties.js
index 31590e6..541f937 100644
--- a/src/properties.js
+++ b/src/properties.js
@@ -24,8 +24,8 @@ const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 const _ = imports.gettext.gettext;
 
+const Application = imports.application;
 const Documents = imports.documents;
-const Global = imports.global;
 const Mainloop = imports.mainloop;
 const TrackerUtils = imports.trackerUtils;
 
@@ -37,7 +37,7 @@ const PropertiesDialog = new Lang.Class({
     Name: 'PropertiesDialog',
 
     _init: function(urn) {
-        let doc = Global.documentManager.getItemById(urn);
+        let doc = Application.documentManager.getItemById(urn);
 
         let dateModified = GLib.DateTime.new_from_unix_local(doc.mtime);
         let dateModifiedString = dateModified.format('%c');
@@ -48,7 +48,7 @@ const PropertiesDialog = new Lang.Class({
             dateCreatedString = dateCreated.format('%c');
         }
 
-        let toplevel = Global.application.get_windows()[0];
+        let toplevel = Application.application.get_windows()[0];
         this.widget = new Gtk.Dialog ({ resizable: false,
                                         transient_for: toplevel,
                                         modal: true,
diff --git a/src/search.js b/src/search.js
new file mode 100644
index 0000000..c50ec88
--- /dev/null
+++ b/src/search.js
@@ -0,0 +1,554 @@
+/*
+ * Copyright (c) 2011, 2012 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+const Application = imports.application;
+const Manager = imports.manager;
+const Query = imports.query;
+
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Tracker = imports.gi.Tracker;
+const _ = imports.gettext.gettext;
+
+function initSearch(context) {
+    context.collectionManager = new Manager.BaseManager(context);
+    context.sourceManager = new SourceManager(context);
+    context.searchCategoryManager = new SearchCategoryManager(context);
+    context.searchMatchManager = new SearchMatchManager(context);
+    context.searchTypeManager = new SearchTypeManager(context);
+    context.searchController = new SearchController(context);
+    context.offsetController = new OffsetController(context);
+    context.queryBuilder = new Query.QueryBuilder(context);
+};
+
+const SearchController = new Lang.Class({
+    Name: 'SearchController',
+
+    _init: function() {
+        this._string = '';
+    },
+
+    setString: function(string) {
+        if (this._string == string)
+            return;
+
+        this._string = string;
+        this.emit('search-string-changed', this._string);
+    },
+
+    getString: function() {
+        return this._string;
+    },
+
+    getTerms: function() {
+        let str = Tracker.sparql_escape_string(this._string);
+        return str.replace(/ +/g, ' ').split(' ');
+    }
+});
+Signals.addSignalMethods(SearchController.prototype);
+
+const SearchCategoryStock = {
+    ALL: 'all',
+    FAVORITES: 'favorites',
+    SHARED: 'shared',
+    PRIVATE: 'private'
+};
+
+const SearchCategory = new Lang.Class({
+    Name: 'SearchCategory',
+
+    _init: function(params) {
+        this.id = params.id;
+        this.name = params.name;
+        this.icon = params.icon;
+    },
+
+    getWhere: function() {
+        if (this.id == SearchCategoryStock.FAVORITES)
+            return '{ ?urn nao:hasTag nao:predefined-tag-favorite }';
+
+        // require to have a contributor, and creator, and they should be different
+        if (this.id == SearchCategoryStock.SHARED)
+            return '{ ?urn nco:contributor ?contributor . ?urn nco:creator ?creator FILTER (?contributor != ?creator ) }';
+
+        return '';
+    },
+
+    getFilter: function() {
+        // require to be not local
+        if (this.id == SearchCategoryStock.SHARED)
+            return this._manager.context.sourceManager.getFilterNotLocal();
+
+        return '(true)';
+    }
+});
+
+const SearchCategoryManager = new Lang.Class({
+    Name: 'SearchCategoryManager',
+    Extends: Manager.BaseManager,
+
+    _init: function(context) {
+        this.parent(_("Category"), context);
+
+        let category, recent;
+        // Translators: this refers to new and recent documents
+        recent = new SearchCategory({ id: SearchCategoryStock.ALL,
+                                      name: _("All"),
+                                      icon: '' });
+        this.addItem(recent);
+
+        // Translators: this refers to favorite documents
+        category = new SearchCategory({ id: SearchCategoryStock.FAVORITES,
+                                        name: _("Favorites"),
+                                        icon: 'emblem-favorite-symbolic' });
+        this.addItem(category);
+        // Translators: this refers to shared documents
+        category = new SearchCategory({ id: SearchCategoryStock.SHARED,
+                                        name: _("Shared with you"),
+                                        icon: 'emblem-shared-symbolic' });
+        this.addItem(category);
+
+        // Private category: currently unimplemented
+        // category = new SearchCategory(SearchCategoryStock.PRIVATE, _("Private"), 'channel-secure-symbolic');
+        // this._categories[category.id] = category;
+
+        this.setActiveItem(recent);
+    }
+});
+
+const SearchType = new Lang.Class({
+    Name: 'SearchType',
+
+    _init: function(params) {
+        this.id = params.id;
+        this.name = params.name;
+        this._filter = (params.filter) ? (params.filter) : '(true)';
+        this._where = (params.where) ? (params.where) : '';
+    },
+
+    getFilter: function() {
+        return this._filter;
+    },
+
+    getWhere: function() {
+        return this._where;
+    }
+});
+
+const SearchTypeManager = new Lang.Class({
+    Name: 'SearchTypeManager',
+    Extends: Manager.BaseManager,
+
+    _init: function(context) {
+        // Translators: "Type" refers to a search filter on the document type
+        // (PDF, spreadsheet, ...)
+        this.parent(_("Type"), context);
+
+        this.addItem(new SearchType({ id: 'all',
+                                      name: _("All") }));
+        this.addItem(new SearchType({ id: 'collections',
+                                      name: _("Collections"),
+                                      filter: 'fn:starts-with(nao:identifier(?urn), \"gd:collection\")',
+                                      where: '?urn rdf:type nfo:DataContainer .' }));
+        this.addItem(new SearchType({ id: 'pdf',
+                                      name: _("PDF Documents"),
+                                      filter: 'fn:contains(nie:mimeType(?urn), \"application/pdf\")',
+                                      where: '?urn rdf:type nfo:PaginatedTextDocument .' }));
+        this.addItem(new SearchType({ id: 'presentations',
+                                      name: _("Presentations"),
+                                      where: '?urn rdf:type nfo:Presentation .' }));
+        this.addItem(new SearchType({ id: 'spreadsheets',
+                                      name: _("Spreadsheets"),
+                                      where: '?urn rdf:type nfo:Spreadsheet .' }));
+        this.addItem(new SearchType({ id: 'textdocs',
+                                      name: _("Text Documents"),
+                                      where: '?urn rdf:type nfo:PaginatedTextDocument .' }));
+
+        this.setActiveItemById('all');
+    },
+
+    getCurrentTypes: function() {
+        let activeItem = this.getActiveItem();
+
+        if (activeItem.id == 'all')
+            return this.getAllTypes();
+
+        return [ activeItem ];
+    },
+
+    getAllTypes: function() {
+        let types = [];
+
+        this.forEachItem(function(item) {
+            if (item.id != 'all')
+                types.push(item);
+            });
+
+        return types;
+    }
+});
+
+const SearchMatchStock = {
+    ALL: 'all',
+    TITLE: 'title',
+    AUTHOR: 'author'
+};
+
+const SearchMatch = new Lang.Class({
+    Name: 'SearchMatch',
+
+    _init: function(params) {
+        this.id = params.id;
+        this.name = params.name;
+        this._term = '';
+    },
+
+    setFilterTerm: function(term) {
+        this._term = term;
+    },
+
+    getFilter: function() {
+        if (this.id == SearchMatchStock.TITLE)
+            return ('fn:contains ' +
+                    '(fn:lower-case (tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))), ' +
+                    '"%s")').format(this._term);
+        if (this.id == SearchMatchStock.AUTHOR)
+            return ('fn:contains ' +
+                    '(fn:lower-case (tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher))), ' +
+                    '"%s")').format(this._term);
+        return '';
+    }
+});
+
+const SearchMatchManager = new Lang.Class({
+    Name: 'SearchMatchManager',
+    Extends: Manager.BaseManager,
+
+    _init: function(context) {
+        // Translators: this is a verb that refers to "All", "Title" and "Author",
+        // as in "Match All", "Match Title" and "Match Author"
+        this.parent(_("Match"), context);
+
+        this.addItem(new SearchMatch({ id: SearchMatchStock.ALL,
+                                       name: _("All") }));
+        this.addItem(new SearchMatch({ id: SearchMatchStock.TITLE,
+        //Translators: "Title" refers to "Match Title" when searching
+                                       name: _("Title") }));
+        this.addItem(new SearchMatch({ id: SearchMatchStock.AUTHOR,
+        //Translators: "Author" refers to "Match Author" when searching
+                                       name: _("Author") }));
+
+        this.setActiveItemById(SearchMatchStock.ALL);
+    },
+
+    getFilter: function() {
+        let terms = this.context.searchController.getTerms();
+        let filters = [];
+
+        for (let i = 0; i < terms.length; i++) {
+            this.forEachItem(function(item) {
+                item.setFilterTerm(terms[i]);
+            });
+            filters.push(this.parent());
+        }
+        return filters.length ? '( ' + filters.join(' && ') + ')' : '';
+    }
+});
+
+const SourceStock = {
+    ALL: 'all',
+    LOCAL: 'local'
+};
+
+const TRACKER_SCHEMA = 'org.freedesktop.Tracker.Miner.Files';
+const TRACKER_KEY_RECURSIVE_DIRECTORIES = 'index-recursive-directories';
+
+const Source = new Lang.Class({
+    Name: 'Source',
+
+    _init: function(params) {
+        this.id = null;
+        this.name = null;
+        this.icon = null;
+
+        if (params.object) {
+            this.object = params.object;
+            let account = params.object.get_account();
+
+            this.id = 'gd:goa-account:' + account.id;
+            this.name = account.provider_name;
+            this.icon = Gio.icon_new_for_string(account.provider_icon);
+        } else {
+            this.id = params.id;
+            this.name = params.name;
+        }
+
+        this.builtin = params.builtin;
+    },
+
+    _getTrackerLocations: function() {
+        let settings = new Gio.Settings({ schema: TRACKER_SCHEMA });
+        let locations = settings.get_strv(TRACKER_KEY_RECURSIVE_DIRECTORIES);
+        let files = [];
+
+        locations.forEach(Lang.bind(this,
+            function(location) {
+                // ignore special XDG placeholders, since we handle those internally
+                if (location[0] == '&' || location[0] == '$')
+                    return;
+
+                let trackerFile = Gio.file_new_for_commandline_arg(location);
+
+                // also ignore XDG locations if they are present with their full path
+                for (let idx = 0; idx < GLib.UserDirectory.N_DIRECTORIES; idx++) {
+                    let file = Gio.file_new_for_path(GLib.get_user_special_dir(idx));
+                    if (trackerFile.equal(file))
+                        return;
+                }
+
+                files.push(trackerFile);
+            }));
+
+        return files;
+    },
+
+    _getBuiltinLocations: function() {
+        let files = [];
+        let xdgDirs = [GLib.UserDirectory.DIRECTORY_DESKTOP,
+                       GLib.UserDirectory.DIRECTORY_DOCUMENTS,
+                       GLib.UserDirectory.DIRECTORY_DOWNLOAD];
+
+        xdgDirs.forEach(Lang.bind(this,
+            function(dir) {
+                let path = GLib.get_user_special_dir(dir);
+                if (path)
+                    files.push(Gio.file_new_for_path(path));
+            }));
+
+        return files;
+    },
+
+    _buildFilterLocal: function() {
+        let locations = this._getBuiltinLocations();
+        locations = locations.concat(this._getTrackerLocations());
+
+        let filters = [];
+        locations.forEach(Lang.bind(this,
+            function(location) {
+                filters.push('(fn:contains (nie:url(?urn), "%s"))'.format(location.get_uri()));
+            }));
+
+        filters.push('(fn:starts-with (nao:identifier(?urn), "gd:collection:local:"))');
+
+        return '(' + filters.join(' || ') + ')';
+    },
+
+    getFilter: function() {
+        let filters = [];
+
+        if (this.id == SourceStock.LOCAL) {
+            filters.push(this._buildFilterLocal());
+        } else if (this.id == SourceStock.ALL) {
+            filters.push(this._buildFilterLocal());
+            filters.push(this._manager.getFilterNotLocal());
+        } else {
+            filters.push(this._buildFilterResource());
+        }
+
+        return '(' + filters.join(' || ') + ')';
+    },
+
+    _buildFilterResource: function() {
+        let filter = '(false)';
+
+        if (!this.builtin)
+            filter = ('(nie:dataSource(?urn) = "%s")').format(this.id);
+
+        return filter;
+    }
+});
+
+const SourceManager = new Lang.Class({
+    Name: 'SourceManager',
+    Extends: Manager.BaseManager,
+
+    _init: function(context) {
+        this.parent(_("Sources"), context);
+
+        // Translators: this refers to documents
+        let source = new Source({ id: SourceStock.ALL,
+                                  name: _("All"),
+                                  builtin: true });
+        this.addItem(source);
+
+        // Translators: this refers to local documents
+        source = new Source({ id: SourceStock.LOCAL,
+                              name: _("Local"),
+                              builtin: true });
+        this.addItem(source);
+
+        Application.goaClient.connect('account-added', Lang.bind(this, this._refreshGoaAccounts));
+        Application.goaClient.connect('account-changed', Lang.bind(this, this._refreshGoaAccounts));
+        Application.goaClient.connect('account-removed', Lang.bind(this, this._refreshGoaAccounts));
+
+        this._refreshGoaAccounts();
+        this.setActiveItemById(SourceStock.ALL);
+    },
+
+    _refreshGoaAccounts: function() {
+        let newItems = {};
+        let accounts = Application.goaClient.get_accounts();
+
+        accounts.forEach(Lang.bind(this,
+            function(object) {
+                if (!object.get_account())
+                    return;
+
+                if (!object.get_documents())
+                    return;
+
+                let source = new Source({ object: object });
+                newItems[source.id] = source;
+            }));
+
+        this.processNewItems(newItems);
+    },
+
+    getFilterNotLocal: function() {
+        let sources = this.getItems();
+        let filters = [];
+
+        for (idx in sources) {
+            let source = sources[idx];
+            if (!source.builtin)
+                filters.push(source.getFilter());
+        }
+
+        if (filters.length == 0)
+            filters.push('false');
+
+        return '(' + filters.join(' || ') + ')';
+    },
+
+    hasOnlineSources: function() {
+        let hasOnline = false;
+        this.forEachItem(
+            function(source) {
+                if (source.object)
+                    hasOnline = true;
+            });
+
+        return hasOnline;
+    },
+
+    hasProviderType: function(providerType) {
+        let found = false;
+        this.forEachItem(Lang.bind(this,
+            function(source) {
+                if (!source.object)
+                    return;
+
+                let account = source.object.get_account();
+                if (!account)
+                    return;
+
+                if (found)
+                    return;
+
+                if (account.provider_type == providerType)
+                    found = true;
+            }));
+
+        return found;
+    }
+});
+
+const _OFFSET_STEP = 50;
+
+const OffsetController = new Lang.Class({
+    Name: 'OffsetController',
+
+    _init: function(context) {
+        this._offset = 0;
+        this._itemCount = 0;
+        this._context = context;
+    },
+
+    // to be called by the view
+    increaseOffset: function() {
+        this._offset += _OFFSET_STEP;
+        this.emit('offset-changed', this._offset);
+    },
+
+    // to be called by the model
+    resetItemCount: function() {
+        let query = this._context.queryBuilder.buildCountQuery();
+
+        Application.connectionQueue.add
+            (query.sparql, null, Lang.bind(this,
+                function(object, res) {
+                    let cursor = null;
+                    try {
+                        cursor = object.query_finish(res);
+                    } catch (e) {
+                        log('Unable to execute count query: ' + e.toString());
+                        return;
+                    }
+
+                    cursor.next_async(null, Lang.bind(this,
+                        function(object, res) {
+                            let valid = object.next_finish(res);
+
+                            if (valid) {
+                                this._itemCount = cursor.get_integer(0);
+                                this.emit('item-count-changed', this._itemCount);
+                            }
+
+                            cursor.close();
+                        }));
+                }));
+    },
+
+    // to be called by the model
+    resetOffset: function() {
+        this._offset = 0;
+    },
+
+    getItemCount: function() {
+        return this._itemCount;
+    },
+
+    getRemainingDocs: function() {
+        return (this._itemCount - (this._offset + _OFFSET_STEP));
+    },
+
+    getOffsetStep: function() {
+        return _OFFSET_STEP;
+    },
+
+    getOffset: function() {
+        return this._offset;
+    }
+});
+Signals.addSignalMethods(OffsetController.prototype);
diff --git a/src/searchbar.js b/src/searchbar.js
index c760ec4..47d524d 100644
--- a/src/searchbar.js
+++ b/src/searchbar.js
@@ -31,248 +31,13 @@ const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const Signals = imports.signals;
 
-const Global = imports.global;
+const Application = imports.application;
 const Manager = imports.manager;
-const Sources = imports.sources;
 const Tweener = imports.util.tweener;
 const Utils = imports.utils;
 
 const _SEARCH_ENTRY_TIMEOUT = 200;
 
-const SearchCategoryStock = {
-    ALL: 'all',
-    FAVORITES: 'favorites',
-    SHARED: 'shared',
-    PRIVATE: 'private'
-};
-
-const SearchCategory = new Lang.Class({
-    Name: 'SearchCategory',
-
-    _init: function(params) {
-        this.id = params.id;
-        this.name = params.name;
-        this.icon = params.icon;
-    },
-
-    getWhere: function() {
-        if (this.id == SearchCategoryStock.FAVORITES)
-            return '{ ?urn nao:hasTag nao:predefined-tag-favorite }';
-
-        // require to have a contributor, and creator, and they should be different
-        if (this.id == SearchCategoryStock.SHARED)
-            return '{ ?urn nco:contributor ?contributor . ?urn nco:creator ?creator FILTER (?contributor != ?creator ) }';
-
-        return '';
-    },
-
-    getFilter: function() {
-        // require to be not local
-        if (this.id == SearchCategoryStock.SHARED)
-            return Global.sourceManager.getFilterNotLocal();
-
-        return '(true)';
-    }
-});
-
-const SearchCategoryManager = new Lang.Class({
-    Name: 'SearchCategoryManager',
-    Extends: Manager.BaseManager,
-
-    _init: function() {
-        this.parent(_("Category"));
-
-        let category, recent;
-        // Translators: this refers to new and recent documents
-        recent = new SearchCategory({ id: SearchCategoryStock.ALL,
-                                      name: _("All"),
-                                      icon: '' });
-        this.addItem(recent);
-
-        // Translators: this refers to favorite documents
-        category = new SearchCategory({ id: SearchCategoryStock.FAVORITES,
-                                        name: _("Favorites"),
-                                        icon: 'emblem-favorite-symbolic' });
-        this.addItem(category);
-        // Translators: this refers to shared documents
-        category = new SearchCategory({ id: SearchCategoryStock.SHARED,
-                                        name: _("Shared with you"),
-                                        icon: 'emblem-shared-symbolic' });
-        this.addItem(category);
-
-        // Private category: currently unimplemented
-        // category = new SearchCategory(SearchCategoryStock.PRIVATE, _("Private"), 'channel-secure-symbolic');
-        // this._categories[category.id] = category;
-
-        this.setActiveItem(recent);
-    }
-});
-
-const SearchType = new Lang.Class({
-    Name: 'SearchType',
-
-    _init: function(params) {
-        this.id = params.id;
-        this.name = params.name;
-        this._filter = (params.filter) ? (params.filter) : '(true)';
-        this._where = (params.where) ? (params.where) : '';
-    },
-
-    getFilter: function() {
-        return this._filter;
-    },
-
-    getWhere: function() {
-        return this._where;
-    }
-});
-
-const SearchTypeManager = new Lang.Class({
-    Name: 'SearchTypeManager',
-    Extends: Manager.BaseManager,
-
-    _init: function() {
-        // Translators: "Type" refers to a search filter on the document type
-        // (PDF, spreadsheet, ...)
-        this.parent(_("Type"));
-
-        this.addItem(new SearchType({ id: 'all',
-                                      name: _("All") }));
-        this.addItem(new SearchType({ id: 'collections',
-                                      name: _("Collections"),
-                                      filter: 'fn:starts-with(nao:identifier(?urn), \"gd:collection\")',
-                                      where: '?urn rdf:type nfo:DataContainer .' }));
-        this.addItem(new SearchType({ id: 'pdf',
-                                      name: _("PDF Documents"),
-                                      filter: 'fn:contains(nie:mimeType(?urn), \"application/pdf\")',
-                                      where: '?urn rdf:type nfo:PaginatedTextDocument .' }));
-        this.addItem(new SearchType({ id: 'presentations',
-                                      name: _("Presentations"),
-                                      where: '?urn rdf:type nfo:Presentation .' }));
-        this.addItem(new SearchType({ id: 'spreadsheets',
-                                      name: _("Spreadsheets"),
-                                      where: '?urn rdf:type nfo:Spreadsheet .' }));
-        this.addItem(new SearchType({ id: 'textdocs',
-                                      name: _("Text Documents"),
-                                      where: '?urn rdf:type nfo:PaginatedTextDocument .' }));
-
-        this.setActiveItemById('all');
-    },
-
-    getCurrentTypes: function() {
-        let activeItem = this.getActiveItem();
-
-        if (activeItem.id == 'all')
-            return this.getAllTypes();
-
-        return [ activeItem ];
-    },
-
-    getAllTypes: function() {
-        let types = [];
-
-        this.forEachItem(function(item) {
-            if (item.id != 'all')
-                types.push(item);
-            });
-
-        return types;
-    }
-});
-
-const SearchMatchStock = {
-    ALL: 'all',
-    TITLE: 'title',
-    AUTHOR: 'author'
-};
-
-const SearchMatch = new Lang.Class({
-    Name: 'SearchMatch',
-
-    _init: function(params) {
-        this.id = params.id;
-        this.name = params.name;
-        this._term = '';
-    },
-
-    setFilterTerm: function(term) {
-        this._term = term;
-    },
-
-    getFilter: function() {
-        if (this.id == SearchMatchStock.TITLE)
-            return ('fn:contains ' +
-                    '(fn:lower-case (tracker:coalesce(nie:title(?urn), nfo:fileName(?urn))), ' +
-                    '"%s")').format(this._term);
-        if (this.id == SearchMatchStock.AUTHOR)
-            return ('fn:contains ' +
-                    '(fn:lower-case (tracker:coalesce(nco:fullname(?creator), nco:fullname(?publisher))), ' +
-                    '"%s")').format(this._term);
-        return '';
-    }
-});
-
-const SearchMatchManager = new Lang.Class({
-    Name: 'SearchMatchManager',
-    Extends: Manager.BaseManager,
-
-    _init: function() {
-        // Translators: this is a verb that refers to "All", "Title" and "Author",
-        // as in "Match All", "Match Title" and "Match Author"
-        this.parent(_("Match"));
-
-        this.addItem(new SearchMatch({ id: SearchMatchStock.ALL,
-                                       name: _("All") }));
-        this.addItem(new SearchMatch({ id: SearchMatchStock.TITLE,
-        //Translators: "Title" refers to "Match Title" when searching
-                                       name: _("Title") }));
-        this.addItem(new SearchMatch({ id: SearchMatchStock.AUTHOR,
-        //Translators: "Author" refers to "Match Author" when searching
-                                       name: _("Author") }));
-
-        this.setActiveItemById(SearchMatchStock.ALL);
-    },
-
-    getFilter: function() {
-        let terms = Global.searchController.getTerms();
-        let filters = [];
-
-        for (let i = 0; i < terms.length; i++) {
-            this.forEachItem(function(item) {
-                item.setFilterTerm(terms[i]);
-            });
-            filters.push(this.parent());
-        }
-        return filters.length ? '( ' + filters.join(' && ') + ')' : '';
-    }
-});
-
-const SearchController = new Lang.Class({
-    Name: 'SearchController',
-
-    _init: function() {
-        this._string = '';
-    },
-
-    setString: function(string) {
-        if (this._string == string)
-            return;
-
-        this._string = string;
-        this.emit('search-string-changed', this._string);
-    },
-
-    getString: function() {
-        return this._string;
-    },
-
-    getTerms: function() {
-        let str = Tracker.sparql_escape_string(this._string);
-        return str.replace(/ +/g, ' ').split(' ');
-    }
-});
-Signals.addSignalMethods(SearchController.prototype);
-
 const Searchbar = new Lang.Class({
     Name: 'Searchbar',
 
@@ -303,7 +68,7 @@ const Searchbar = new Lang.Class({
                 let keyval = event.get_keyval()[1];
 
                 if (keyval == Gdk.KEY_Escape) {
-                    Global.application.change_action_state('search', GLib.Variant.new('b', false));
+                    Application.application.change_action_state('search', GLib.Variant.new('b', false));
                     return true;
                 }
 
@@ -325,7 +90,7 @@ const Searchbar = new Lang.Class({
             }));
 
         // connect to the search action state for visibility
-        let searchStateId = Global.application.connect('action-state-changed::search', Lang.bind(this,
+        let searchStateId = Application.application.connect('action-state-changed::search', Lang.bind(this,
             function(source, actionName, state) {
                 if (state.get_boolean())
                     this.show();
@@ -334,8 +99,8 @@ const Searchbar = new Lang.Class({
             }));
         this.widget.connect('destroy', Lang.bind(this,
             function() {
-                Global.application.disconnect(searchStateId);
-                Global.application.change_action_state('search', GLib.Variant.new('b', false));
+                Application.application.disconnect(searchStateId);
+                Application.application.change_action_state('search', GLib.Variant.new('b', false));
             }));
 
         this.widget.show_all();
@@ -419,7 +184,7 @@ const Searchbar = new Lang.Class({
             handled = true;
 
             if (!this._in)
-                Global.application.change_action_state('search', GLib.Variant.new('b', true));
+                Application.application.change_action_state('search', GLib.Variant.new('b', true));
         }
 
         return handled;
@@ -458,12 +223,12 @@ const Dropdown = new Lang.Class({
     Name: 'Dropdown',
 
     _init: function() {
-        this._sourceView = new Manager.BaseView(Global.sourceManager);
-        this._typeView = new Manager.BaseView(Global.searchTypeManager);
-        this._matchView = new Manager.BaseView(Global.searchMatchManager);
+        this._sourceView = new Manager.BaseView(Application.sourceManager);
+        this._typeView = new Manager.BaseView(Application.searchTypeManager);
+        this._matchView = new Manager.BaseView(Application.searchMatchManager);
         // TODO: this is out for now, but should we move it somewhere
         // else?
-        // this._categoryView = new Manager.BaseView(Global.searchCategoryManager);
+        // this._categoryView = new Manager.BaseView(Application.searchCategoryManager);
 
         this._sourceView.connect('item-activated',
                                  Lang.bind(this, this._onItemActivated));
@@ -527,13 +292,13 @@ const OverviewSearchbar = new Lang.Class({
 
         this.parent();
 
-        this._sourcesId = Global.sourceManager.connect('active-changed',
+        this._sourcesId = Application.sourceManager.connect('active-changed',
             Lang.bind(this, this._onActiveSourceChanged));
-        this._searchTypeId = Global.searchTypeManager.connect('active-changed',
+        this._searchTypeId = Application.searchTypeManager.connect('active-changed',
             Lang.bind(this, this._onActiveTypeChanged));
-        this._searchMatchId = Global.searchMatchManager.connect('active-changed',
+        this._searchMatchId = Application.searchMatchManager.connect('active-changed',
             Lang.bind(this, this._onActiveMatchChanged));
-        this._collectionId = Global.collectionManager.connect('active-changed',
+        this._collectionId = Application.collectionManager.connect('active-changed',
             Lang.bind(this, this._onActiveCollectionChanged));
 
         this._onActiveSourceChanged();
@@ -546,7 +311,7 @@ const OverviewSearchbar = new Lang.Class({
         this._searchEntry = new Gd.TaggedEntry({ width_request: 500 });
         this._searchEntry.connect('tag-clicked',
             Lang.bind(this, this._onTagClicked));
-        this._searchEntry.set_text(Global.searchController.getString());
+        this._searchEntry.set_text(Application.searchController.getString());
 
         // create the dropdown button
         this._dropdownButton = new Gtk.ToggleButton(
@@ -574,15 +339,15 @@ const OverviewSearchbar = new Lang.Class({
 
     entryChanged: function() {
         let currentText = this._searchEntry.get_text().toLowerCase();
-        Global.searchController.setString(currentText);
+        Application.searchController.setString(currentText);
     },
 
     _onActiveCollectionChanged: function() {
-        let searchType = Global.searchTypeManager.getActiveItem();
+        let searchType = Application.searchTypeManager.getActiveItem();
 
-        if (Global.searchController.getString() != '' ||
+        if (Application.searchController.getString() != '' ||
             searchType.id != 'all') {
-            Global.searchTypeManager.setActiveItemById('all');
+            Application.searchTypeManager.setActiveItemById('all');
             this._searchEntry.set_text('');
         }
     },
@@ -607,15 +372,15 @@ const OverviewSearchbar = new Lang.Class({
     },
 
     _onActiveSourceChanged: function() {
-        this._onActiveChangedCommon('source', Global.sourceManager);
+        this._onActiveChangedCommon('source', Application.sourceManager);
     },
 
     _onActiveTypeChanged: function() {
-        this._onActiveChangedCommon('type', Global.searchTypeManager);
+        this._onActiveChangedCommon('type', Application.searchTypeManager);
     },
 
     _onActiveMatchChanged: function() {
-        this._onActiveChangedCommon('match', Global.searchMatchManager);
+        this._onActiveChangedCommon('match', Application.searchMatchManager);
     },
 
     _onTagClicked: function() {
@@ -624,22 +389,22 @@ const OverviewSearchbar = new Lang.Class({
 
     destroy: function() {
         if (this._sourcesId != 0) {
-            Global.sourceManager.disconnect(this._sourcesId);
+            Application.sourceManager.disconnect(this._sourcesId);
             this._sourcesId = 0;
         }
 
         if (this._searchTypeId != 0) {
-            Global.searchTypeManager.disconnect(this._searchTypeId);
+            Application.searchTypeManager.disconnect(this._searchTypeId);
             this._searchTypeId = 0;
         }
 
         if (this._searchMatchId != 0) {
-            Global.searchMatchManager.disconnect(this._searchMatchId);
+            Application.searchMatchManager.disconnect(this._searchMatchId);
             this._searchMatchId = 0;
         }
 
         if (this._collectionId != 0) {
-            Global.collectionManager.disconnect(this._collectionId);
+            Application.collectionManager.disconnect(this._collectionId);
             this._collectionId = 0;
         }
 
@@ -649,9 +414,9 @@ const OverviewSearchbar = new Lang.Class({
     hide: function() {
         this._dropdownButton.set_active(false);
 
-        Global.searchTypeManager.setActiveItemById('all');
-        Global.searchMatchManager.setActiveItemById('all');
-        Global.sourceManager.setActiveItemById('all');
+        Application.searchTypeManager.setActiveItemById('all');
+        Application.searchMatchManager.setActiveItemById('all');
+        Application.sourceManager.setActiveItemById('all');
 
         this.parent();
     }
diff --git a/src/selections.js b/src/selections.js
index d9f78d1..435bbe1 100644
--- a/src/selections.js
+++ b/src/selections.js
@@ -30,8 +30,8 @@ const GtkClutter = imports.gi.GtkClutter;
 const Pango = imports.gi.Pango;
 const _ = imports.gettext.gettext;
 
+const Application = imports.application;
 const Documents = imports.documents;
-const Global = imports.global;
 const Manager = imports.manager;
 const Notifications = imports.notifications;
 const Properties = imports.properties;
@@ -56,8 +56,8 @@ const FetchCollectionsJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let query = Global.queryBuilder.buildFetchCollectionsQuery(this._urn);
-        Global.connectionQueue.add(query.sparql, null, Lang.bind(this,
+        let query = Application.queryBuilder.buildFetchCollectionsQuery(this._urn);
+        Application.connectionQueue.add(query.sparql, null, Lang.bind(this,
             function(object, res) {
                 let cursor = null;
 
@@ -118,7 +118,7 @@ const FetchCollectionStateForSelectionJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let urns = Global.selectionController.getSelection();
+        let urns = Application.selectionController.getSelection();
         urns.forEach(Lang.bind(this,
             function(urn) {
                 let job = new FetchCollectionsJob(urn);
@@ -138,7 +138,7 @@ const FetchCollectionStateForSelectionJob = new Lang.Class({
 
     _emitCallback: function() {
         let collectionState = {};
-        let collections = Global.collectionManager.getItems();
+        let collections = Application.collectionManager.getItems();
 
         // for all the registered collections...
         for (collIdx in collections) {
@@ -152,14 +152,14 @@ const FetchCollectionStateForSelectionJob = new Lang.Class({
             // collection itself, hide this if it's the same collection.
             if (Object.keys(this._collectionsForItems).length == 1) {
                 let itemIdx = Object.keys(this._collectionsForItems)[0];
-                let item = Global.documentManager.getItemById(itemIdx);
+                let item = Application.documentManager.getItemById(itemIdx);
 
                 if (item.id == collection.id)
                     hidden = true;
             }
 
             for (itemIdx in this._collectionsForItems) {
-                let item = Global.documentManager.getItemById(itemIdx);
+                let item = Application.documentManager.getItemById(itemIdx);
                 let collectionsForItem = this._collectionsForItems[itemIdx];
 
                 // if one of the selected items is part of this collection...
@@ -205,8 +205,8 @@ const UpdateMtimeJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let query = Global.queryBuilder.buildUpdateMtimeQuery(this._urn);
-        Global.connectionQueue.update(query.sparql, null, Lang.bind(this,
+        let query = Application.queryBuilder.buildUpdateMtimeQuery(this._urn);
+        Application.connectionQueue.update(query.sparql, null, Lang.bind(this,
             function(object, res) {
                 try {
                     object.update_finish(res);
@@ -233,18 +233,18 @@ const SetCollectionForSelectionJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let urns = Global.selectionController.getSelection();
+        let urns = Application.selectionController.getSelection();
         urns.forEach(Lang.bind(this,
             function(urn) {
                 // never add a collection to itself!!
                 if (urn == this._collectionUrn)
                     return;
 
-                let query = Global.queryBuilder.buildSetCollectionQuery(urn,
+                let query = Application.queryBuilder.buildSetCollectionQuery(urn,
                     this._collectionUrn, this._setting);
                 this._runningJobs++;
 
-                Global.connectionQueue.update(query.sparql, null, Lang.bind(this,
+                Application.connectionQueue.update(query.sparql, null, Lang.bind(this,
                     function(object, res) {
                         try {
                             object.update_finish(res);
@@ -284,8 +284,8 @@ const CreateCollectionJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let query = Global.queryBuilder.buildCreateCollectionQuery(this._name);
-        Global.connectionQueue.updateBlank(query.sparql, null, Lang.bind(this,
+        let query = Application.queryBuilder.buildCreateCollectionQuery(this._name);
+        Application.connectionQueue.updateBlank(query.sparql, null, Lang.bind(this,
             function(object, res) {
                 let variant = null;
                 try {
@@ -326,12 +326,10 @@ const OrganizeCollectionModel = new Lang.Class({
               GObject.TYPE_INT ]);
         this._placeholderRef = null;
 
-        this._collAddedId =
-            Global.collectionManager.connect('item-added',
-                                             Lang.bind(this, this._onCollectionAdded));
-        this._collRemovedId =
-            Global.collectionManager.connect('item-removed',
-                                             Lang.bind(this, this._onCollectionRemoved));
+        this._collAddedId = Application.collectionManager.connect('item-added',
+            Lang.bind(this, this._onCollectionAdded));
+        this._collRemovedId = Application.collectionManager.connect('item-removed',
+            Lang.bind(this, this._onCollectionRemoved));
 
         // populate the model
         let job = new FetchCollectionStateForSelectionJob();
@@ -363,7 +361,7 @@ const OrganizeCollectionModel = new Lang.Class({
         this.removePlaceholder();
 
         for (idx in collectionState) {
-            let item = Global.collectionManager.getItemById(idx);
+            let item = Application.collectionManager.getItemById(idx);
 
             if ((collectionState[item.id] & OrganizeCollectionState.HIDDEN) != 0)
                 continue;
@@ -440,12 +438,12 @@ const OrganizeCollectionModel = new Lang.Class({
 
     destroy: function() {
         if (this._collAddedId != 0) {
-            Global.collectionManager.disconnect(this._collAddedId);
+            Application.collectionManager.disconnect(this._collAddedId);
             this._collAddedId = 0;
         }
 
         if (this._collRemovedId != 0) {
-            Global.collectionManager.disconnect(this._collRemovedId);
+            Application.collectionManager.disconnect(this._collRemovedId);
             this._collRemovedId = 0;
         }
     }
@@ -574,10 +572,10 @@ const OrganizeCollectionView = new Lang.Class({
 
     _detailCellFunc: function(col, cell, model, iter) {
         let id = model.get_value(iter, OrganizeModelColumns.ID);
-        let item = Global.collectionManager.getItemById(id);
+        let item = Application.collectionManager.getItemById(id);
 
         if (item && item.identifier.indexOf(Query.LOCAL_COLLECTIONS_IDENTIFIER) == -1) {
-            cell.text = Global.sourceManager.getItemById(item.resourceUrn).name;
+            cell.text = Application.sourceManager.getItemById(item.resourceUrn).name;
             cell.visible = true;
         } else {
             cell.text = '';
@@ -660,8 +658,8 @@ const SelectionController = new Lang.Class({
         this._selection = [];
         this._selectionMode = false;
 
-        Global.documentManager.connect('item-removed',
-                                       Lang.bind(this, this._onDocumentRemoved));
+        Application.documentManager.connect('item-removed',
+            Lang.bind(this, this._onDocumentRemoved));
     },
 
     _onDocumentRemoved: function(manager, item) {
@@ -796,10 +794,10 @@ const SelectionToolbar = new Lang.Class({
 
         this.widget.show_all();
 
-        Global.selectionController.connect('selection-mode-changed',
-                                           Lang.bind(this, this._onSelectionModeChanged));
-        Global.selectionController.connect('selection-changed',
-                                           Lang.bind(this, this._onSelectionChanged));
+        Application.selectionController.connect('selection-mode-changed',
+            Lang.bind(this, this._onSelectionModeChanged));
+        Application.selectionController.connect('selection-changed',
+            Lang.bind(this, this._onSelectionChanged));
     },
 
     _onSelectionModeChanged: function(controller, mode) {
@@ -810,10 +808,10 @@ const SelectionToolbar = new Lang.Class({
     },
 
     _onSelectionChanged: function() {
-        if (!Global.selectionController.getSelectionMode())
+        if (!Application.selectionController.getSelectionMode())
             return;
 
-        let selection = Global.selectionController.getSelection();
+        let selection = Application.selectionController.getSelection();
         this._setItemListeners(selection);
 
         if (selection.length > 0) {
@@ -833,7 +831,7 @@ const SelectionToolbar = new Lang.Class({
 
         selection.forEach(Lang.bind(this,
             function(urn) {
-                let doc = Global.documentManager.getItemById(urn);
+                let doc = Application.documentManager.getItemById(urn);
                 let id = doc.connect('info-updated', Lang.bind(this, this._setItemVisibility));
                 this._itemListeners[id] = doc;
             }));
@@ -848,10 +846,10 @@ const SelectionToolbar = new Lang.Class({
 
         this._insideRefresh = true;
 
-        let selection = Global.selectionController.getSelection();
+        let selection = Application.selectionController.getSelection();
         selection.forEach(Lang.bind(this,
             function(urn) {
-                let doc = Global.documentManager.getItemById(urn);
+                let doc = Application.documentManager.getItemById(urn);
 
                 if ((doc.defaultAppName) &&
                     (apps.indexOf(doc.defaultAppName) == -1))
@@ -904,27 +902,27 @@ const SelectionToolbar = new Lang.Class({
     },
 
     _onToolbarOpen: function(widget) {
-        let selection = Global.selectionController.getSelection();
+        let selection = Application.selectionController.getSelection();
 
         selection.forEach(Lang.bind(this,
             function(urn) {
-                let doc = Global.documentManager.getItemById(urn);
+                let doc = Application.documentManager.getItemById(urn);
                 doc.open(widget.get_screen(), Gtk.get_current_event_time());
             }));
     },
 
     _onToolbarTrash: function(widget) {
-        let selection = Global.selectionController.getSelection();
+        let selection = Application.selectionController.getSelection();
 
         selection.forEach(Lang.bind(this,
             function(urn) {
-                let doc = Global.documentManager.getItemById(urn);
+                let doc = Application.documentManager.getItemById(urn);
                 doc.trash();
             }));
     },
 
     _onToolbarProperties: function(widget) {
-        let selection = Global.selectionController.getSelection();
+        let selection = Application.selectionController.getSelection();
         let dialog = new Properties.PropertiesDialog(selection[0]);
         this._fadeOut();
 
@@ -936,12 +934,12 @@ const SelectionToolbar = new Lang.Class({
     },
 
     _onToolbarPrint: function(widget) {
-        let selection = Global.selectionController.getSelection();
+        let selection = Application.selectionController.getSelection();
 
         if (selection.length != 1)
             return;
 
-        let doc = Global.documentManager.getItemById(selection[0]);
+        let doc = Application.documentManager.getItemById(selection[0]);
         doc.print(this.widget.get_toplevel());
     },
 
diff --git a/src/shellSearchProvider.js b/src/shellSearchProvider.js
index fb0309b..070939e 100644
--- a/src/shellSearchProvider.js
+++ b/src/shellSearchProvider.js
@@ -20,8 +20,7 @@
  */
 
 const Lang = imports.lang;
-const Mainloop = imports.mainloop;
-const Gettext = imports.gettext;
+const Signals = imports.signals;
 
 const GdPrivate = imports.gi.GdPrivate;
 const GdkPixbuf = imports.gi.GdkPixbuf;
@@ -31,15 +30,22 @@ const Gtk = imports.gi.Gtk;
 const GLib = imports.gi.GLib;
 const Tracker = imports.gi.Tracker;
 
-const Documents = imports.documents;
+const Application = imports.application;
 const Format = imports.format;
-const Global = imports.global;
 const Path = imports.path;
 const Query = imports.query;
+const Search = imports.search;
+const TrackerUtils = imports.trackerUtils;
 const Utils = imports.utils;
 
-const MAINLOOP_ID = "documents-search-provider";
-const AUTOQUIT_TIMEOUT = 120;
+let collectionManager = null;
+let offsetController = null;
+let queryBuilder = null;
+let searchCategoryManager = null;
+let searchMatchManager = null;
+let searchTypeManager = null;
+let searchController = null;
+let sourceManager = null;
 
 const SEARCH_PROVIDER_IFACE = 'org.gnome.Shell.SearchProvider';
 const SEARCH_PROVIDER_NAME  = 'org.gnome.Documents.SearchProvider';
@@ -126,8 +132,8 @@ const CreateCollectionIconJob = new Lang.Class({
     run: function(callback) {
         this._callback = callback;
 
-        let query = Global.queryBuilder.buildCollectionIconQuery(this._id);
-        Global.connectionQueue.add(query.sparql, null, Lang.bind(this,
+        let query = queryBuilder.buildCollectionIconQuery(this._id);
+        Application.connectionQueue.add(query.sparql, null, Lang.bind(this,
             function(object, res) {
                 let cursor = null;
 
@@ -201,7 +207,7 @@ const CreateCollectionIconJob = new Lang.Class({
 
         this._itemIds.forEach(Lang.bind(this,
             function(itemId) {
-                let job = new Documents.SingleItemJob(itemId);
+                let job = new TrackerUtils.SingleItemJob(itemId, queryBuilder);
                 this._itemJobs++;
                 job.run(Query.QueryFlags.UNFILTERED, Lang.bind(this,
                     function(cursor) {
@@ -258,7 +264,7 @@ const FetchMetasJob = new Lang.Class({
 
         this._ids.forEach(Lang.bind(this,
             function(id) {
-                let single = new Documents.SingleItemJob(id);
+                let single = new TrackerUtils.SingleItemJob(id, queryBuilder);
                 single.run(Query.QueryFlags.UNFILTERED, Lang.bind(this,
                     function(cursor) {
                         let title =    cursor.get_string(Query.QueryColumns.TITLE)[0];
@@ -304,10 +310,10 @@ const FetchIdsJob = new Lang.Class({
     run: function(callback, cancellable) {
         this._callback = callback;
         this._cancellable = cancellable;
-        Global.searchController.setString(this._terms.join(' ').toLowerCase());
+        searchController.setString(this._terms.join(' ').toLowerCase());
 
-        let query = Global.queryBuilder.buildGlobalQuery();
-        Global.connectionQueue.add(query.sparql, this._cancellable, Lang.bind(this,
+        let query = queryBuilder.buildGlobalQuery();
+        Application.connectionQueue.add(query.sparql, this._cancellable, Lang.bind(this,
             function(object, res) {
                 let cursor = null;
 
@@ -347,71 +353,23 @@ const ShellSearchProvider = new Lang.Class({
     Name: 'ShellSearchProvider',
 
     _init: function() {
+        Application.application.hold();
         Gio.DBus.own_name(Gio.BusType.SESSION,
                           SEARCH_PROVIDER_NAME,
                           Gio.BusNameOwnerFlags.NONE,
                           Lang.bind(this, this._onBusAcquired),
-                          Lang.bind(this, this._onNameAcquired),
-                          Lang.bind(this, this._onNameLost));
+                          null, null);
 
         this._cache = {};
-        this._initReal();
-
-        this._timeoutId = 0;
         this._cancellable = new Gio.Cancellable();
+
+        Search.initSearch(imports.shellSearchProvider);
     },
 
     _onBusAcquired: function() {
         let dbusImpl = Gio.DBusExportedObject.wrapJSObject(SearchProviderIface, this);
         dbusImpl.export(Gio.DBus.session, SEARCH_PROVIDER_PATH);
-    },
-
-    _onNameAcquired: function() {
-        this._resetTimeout();
-    },
-
-    _onNameLost: function() {
-        this.quit();
-    },
-
-    _initReal: function() {
-        String.prototype.format = Format.format;
-
-        Gtk.init(null, null);
-
-        Global.application = this;
-        Global.settings = new Gio.Settings({ schema: 'org.gnome.documents' });
-
-        // connect to tracker
-        try {
-            Global.connection = Tracker.SparqlConnection.get(null);
-        } catch (e) {
-            log('Unable to connect to the tracker database: ' + e.toString());
-            this.quit();
-        }
-
-        try {
-            Global.goaClient = Goa.Client.new_sync(null);
-        } catch (e) {
-            log('Unable to create the GOA client: ' + e.toString());
-            this.quit();
-        }
-
-        Global.initSearch();
-    },
-
-    _resetTimeout: function() {
-        if (this._timeoutId) {
-            Mainloop.source_remove(this._timeoutId);
-            this._timeoutId = 0;
-        }
-
-        if (GLib.getenv('DOCUMENTS_SEARCH_PROVIDER_PERSIST'))
-            return;
-
-        this._timeoutId = Mainloop.timeout_add_seconds(AUTOQUIT_TIMEOUT,
-                                                       Lang.bind(this,
-                                                                 this.quit));
+        Application.application.release();
     },
 
     _returnMetasFromCache: function(ids, invocation) {
@@ -435,12 +393,14 @@ const ShellSearchProvider = new Lang.Class({
 
             metas.push(meta);
         }
+
+        Application.application.release();
         invocation.return_value(GLib.Variant.new('(aa{sv})', [ metas ]));
     },
 
     GetInitialResultSetAsync: function(params, invocation) {
         let terms = params[0];
-        this._resetTimeout();
+        Application.application.hold();
 
         this._cancellable.cancel();
         this._cancellable.reset();
@@ -448,13 +408,14 @@ const ShellSearchProvider = new Lang.Class({
         let job = new FetchIdsJob(terms);
         job.run(Lang.bind(this,
             function(ids) {
+                Application.application.release();
                 invocation.return_value(GLib.Variant.new('(as)', [ ids ]));
             }), this._cancellable);
     },
 
     GetSubsearchResultSetAsync: function(params, invocation) {
         let [previousResults, terms] = params;
-        this._resetTimeout();
+        Application.application.hold();
 
         this._cancellable.cancel();
         this._cancellable.reset();
@@ -462,13 +423,14 @@ const ShellSearchProvider = new Lang.Class({
         let job = new FetchIdsJob(terms);
         job.run(Lang.bind(this,
             function(ids) {
+                Application.application.release();
                 invocation.return_value(GLib.Variant.new('(as)', [ ids ]));
             }), this._cancellable);
     },
 
     GetResultMetasAsync: function(params, invocation) {
         let ids = params[0];
-        this._resetTimeout();
+        Application.application.hold();
 
         let toFetch = ids.filter(Lang.bind(this,
             function(id) {
@@ -493,28 +455,7 @@ const ShellSearchProvider = new Lang.Class({
     },
 
     ActivateResult: function(id) {
-        let app = Gio.DesktopAppInfo.new('gnome-documents.desktop');
-        if (!app)
-            return;
-
-        try {
-            if (!app.launch_uris([id], null))
-                log('Activating result "' + id + '" failed');
-        } catch(e) {
-            log('Activating result "' + id + '" failed - ' + e);
-        }
-    },
-
-    quit: function() {
-        Mainloop.quit(MAINLOOP_ID);
-    },
-
-    run: function() {
-        Mainloop.run(MAINLOOP_ID);
+        this.emit('activate-result', id);
     }
 });
-
-function start() {
-    let searchProvider = new ShellSearchProvider();
-    searchProvider.run();
-}
+Signals.addSignalMethods(ShellSearchProvider.prototype);
diff --git a/src/trackerController.js b/src/trackerController.js
index 4f20073..bc22206 100644
--- a/src/trackerController.js
+++ b/src/trackerController.js
@@ -22,7 +22,7 @@
 const Lang = imports.lang;
 const Signals = imports.signals;
 
-const Global = imports.global;
+const Application = imports.application;
 const Query = imports.query;
 const Utils = imports.utils;
 
@@ -85,13 +85,13 @@ const TrackerConnectionQueue = new Lang.Class({
         this._running = true;
 
         if (params.queryType == QueryType.SELECT)
-            Global.connection.query_async(params.query, params.cancellable,
+            Application.connection.query_async(params.query, params.cancellable,
                                           Lang.bind(this, this._queueCollector, params));
         else if (params.queryType == QueryType.UPDATE)
-            Global.connection.update_async(params.query, GLib.PRIORITY_DEFAULT, params.cancellable,
+            Application.connection.update_async(params.query, GLib.PRIORITY_DEFAULT, params.cancellable,
                                            Lang.bind(this, this._queueCollector, params));
         else if (params.queryType == QueryType.UPDATE_BLANK)
-            Global.connection.update_blank_async(params.query, GLib.PRIORITY_DEFAULT, params.cancellable,
+            Application.connection.update_blank_async(params.query, GLib.PRIORITY_DEFAULT, params.cancellable,
                                                  Lang.bind(this, this._queueCollector, params));
     },
 
@@ -121,18 +121,18 @@ const TrackerController = new Lang.Class({
         // useful for debugging
         this._lastQueryTime = 0;
 
-        Global.sourceManager.connect('item-added', Lang.bind(this, this._onSourceAddedRemoved));
-        Global.sourceManager.connect('item-removed', Lang.bind(this, this._onSourceAddedRemoved));
-        Global.sourceManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
+        Application.sourceManager.connect('item-added', Lang.bind(this, this._onSourceAddedRemoved));
+        Application.sourceManager.connect('item-removed', Lang.bind(this, this._onSourceAddedRemoved));
+        Application.sourceManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
 
-        Global.offsetController.connect('offset-changed', Lang.bind(this, this._performCurrentQuery));
+        Application.offsetController.connect('offset-changed', Lang.bind(this, this._performCurrentQuery));
 
-        Global.collectionManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
-        Global.searchController.connect('search-string-changed', Lang.bind(this, this._refreshForObject));
-        Global.searchCategoryManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
-        Global.searchTypeManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
+        Application.collectionManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
+        Application.searchController.connect('search-string-changed', Lang.bind(this, this._refreshForObject));
+        Application.searchCategoryManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
+        Application.searchTypeManager.connect('active-changed', Lang.bind(this, this._refreshForObject));
 
-        Global.searchMatchManager.connect('active-changed', Lang.bind(this, this._onSearchMatchChanged));
+        Application.searchMatchManager.connect('active-changed', Lang.bind(this, this._onSearchMatchChanged));
     },
 
     _setQueryStatus: function(status) {
@@ -169,7 +169,7 @@ const TrackerController = new Lang.Class({
         if (exception)
             this._onQueryError(exception);
         else
-            Global.offsetController.resetItemCount();
+            Application.offsetController.resetItemCount();
 
         if (this._queryQueued) {
             this._queryQueued = false;
@@ -195,7 +195,7 @@ const TrackerController = new Lang.Class({
 
         Utils.debug('Query Cursor: '
                     + (GLib.get_monotonic_time() - this._lastQueryTime) / 1000000);
-        Global.documentManager.addDocumentFromCursor(cursor);
+        Application.documentManager.addDocumentFromCursor(cursor);
         cursor.next_async(this._cancellable, Lang.bind(this, this._onCursorNext));
     },
 
@@ -212,18 +212,18 @@ const TrackerController = new Lang.Class({
     },
 
     _performCurrentQuery: function() {
-        this._currentQuery = Global.queryBuilder.buildGlobalQuery();
+        this._currentQuery = Application.queryBuilder.buildGlobalQuery();
         this._cancellable.reset();
 
-        Global.connectionQueue.add(this._currentQuery.sparql,
-                                   this._cancellable, Lang.bind(this, this._onQueryExecuted));
+        Application.connectionQueue.add(this._currentQuery.sparql,
+                                        this._cancellable, Lang.bind(this, this._onQueryExecuted));
     },
 
     _refreshInternal: function(flags) {
         this._isStarted = true;
 
         if (flags & RefreshFlags.RESET_OFFSET)
-            Global.offsetController.resetOffset();
+            Application.offsetController.resetOffset();
 
         if (this.getQueryStatus()) {
             this._cancellable.cancel();
@@ -234,7 +234,7 @@ const TrackerController = new Lang.Class({
         }
 
         this._setQueryStatus(true);
-        Global.documentManager.clear();
+        Application.documentManager.clear();
 
         this._performCurrentQuery();
     },
@@ -246,7 +246,7 @@ const TrackerController = new Lang.Class({
     _onSearchMatchChanged: function() {
         // when the "match" search setting changes, refresh only if
         // the search string is not empty
-        if (Global.searchController.getString() != '')
+        if (Application.searchController.getString() != '')
             this._refreshInternal(RefreshFlags.RESET_OFFSET);
     },
 
diff --git a/src/trackerUtils.js b/src/trackerUtils.js
index c7d5c8e..6ae64b6 100644
--- a/src/trackerUtils.js
+++ b/src/trackerUtils.js
@@ -20,13 +20,14 @@
  */
 
 const GLib = imports.gi.GLib;
+const Lang = imports.lang;
 
-const Global = imports.global;
+const Application = imports.application;
 
 function setEditedName(newTitle, docId, callback) {
     let sparql = ('INSERT OR REPLACE { <%s> nie:title \"%s\" }'.format(docId, newTitle));
 
-    Global.connectionQueue.update(sparql, null,
+    Application.connectionQueue.update(sparql, null,
         function(object, res) {
             try {
                 object.update_finish(res);
@@ -39,3 +40,54 @@ function setEditedName(newTitle, docId, callback) {
         });
 
 }
+
+const SingleItemJob = new Lang.Class({
+    Name: 'SingleItemJob',
+
+    _init: function(urn, queryBuilder) {
+        this._urn = urn;
+        this._cursor = null;
+        this._builder = queryBuilder;
+    },
+
+    run: function(flags, callback) {
+        this._callback = callback;
+
+        let query = this._builder.buildSingleQuery(flags, this._urn);
+        Application.connectionQueue.add(query.sparql, null, Lang.bind(this,
+            function(object, res) {
+                try {
+                    let cursor = object.query_finish(res);
+                    cursor.next_async(null, Lang.bind(this, this._onCursorNext));
+                } catch (e) {
+                    log('Unable to query single item ' + e.message);
+                    this._emitCallback();
+                }
+            }));
+    },
+
+    _onCursorNext: function(cursor, res) {
+        let valid = false;
+
+        try {
+            valid = cursor.next_finish(res);
+        } catch (e) {
+            log('Unable to query single item ' + e.message);
+        }
+
+        if (!valid) {
+            cursor.close();
+            this._emitCallback();
+
+            return;
+        }
+
+        this._cursor = cursor;
+        this._emitCallback();
+        cursor.close();
+    },
+
+    _emitCallback: function() {
+        this._callback(this._cursor);
+    }
+});
diff --git a/src/utils.js b/src/utils.js
index 8d3e8ef..1bbde88 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -27,7 +27,7 @@ const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 
 const Documents = imports.documents;
-const Global = imports.global;
+const Application = imports.application;
 
 const Lang = imports.lang;
 const Signals = imports.signals;
@@ -39,7 +39,7 @@ let debugInit = false;
 let debugEnabled = false;
 
 function getIconSize() {
-    let viewType = Global.settings.get_enum('view-as');
+    let viewType = Application.settings.get_enum('view-as');
 
     if (viewType == Gd.MainViewType.LIST)
         return _LIST_VIEW_SIZE;
@@ -48,7 +48,7 @@ function getIconSize() {
 }
 
 function getThumbnailFrameBorder() {
-    let viewType = Global.settings.get_enum('view-as');
+    let viewType = Application.settings.get_enum('view-as');
     let slice = new Gtk.Border();
     let border = null;
 
diff --git a/src/view.js b/src/view.js
index 902e85b..a81127d 100644
--- a/src/view.js
+++ b/src/view.js
@@ -31,8 +31,8 @@ const _ = imports.gettext.gettext;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 
+const Application = imports.application;
 const Documents = imports.documents;
-const Global = imports.global;
 const TrackerUtils = imports.trackerUtils;
 const WindowMode = imports.windowMode;
 const Utils = imports.utils;
@@ -43,7 +43,7 @@ const LoadMoreButton = new Lang.Class({
     _init: function() {
         this._block = false;
 
-        this._controller = Global.offsetController;
+        this._controller = Application.offsetController;
         this._controllerId =
             this._controller.connect('item-count-changed',
                                      Lang.bind(this, this._onItemCountChanged));
@@ -118,15 +118,15 @@ const ViewModel = new Lang.Class({
         this.model.set_sort_column_id(Gd.MainColumns.MTIME,
                                       Gtk.SortType.DESCENDING);
 
-        Global.documentManager.connect('item-added',
+        Application.documentManager.connect('item-added',
             Lang.bind(this, this._onItemAdded));
-        Global.documentManager.connect('item-removed',
+        Application.documentManager.connect('item-removed',
             Lang.bind(this, this._onItemRemoved));
-        Global.documentManager.connect('clear',
+        Application.documentManager.connect('clear',
             Lang.bind(this, this._onClear));
 
         // populate with the intial items
-        let items = Global.documentManager.getItems();
+        let items = Application.documentManager.getItems();
         for (let idx in items) {
             this._onItemAdded(null, items[idx]);
         }
@@ -203,45 +203,42 @@ const ViewContainer = new Lang.Class({
                             Lang.bind(this, this._onViewSelectionChanged));
 
         // connect to settings change for list/grid view
-        this._viewSettingsId =
-            Global.settings.connect('changed::view-as',
-                                    Lang.bind(this, this._updateTypeForSettings));
+        this._viewSettingsId = Application.settings.connect('changed::view-as',
+            Lang.bind(this, this._updateTypeForSettings));
         this._updateTypeForSettings();
 
         // setup selection controller => view
-        this._selectionModeId =
-            Global.selectionController.connect('selection-mode-changed',
-                                               Lang.bind(this, this._onSelectionModeChanged));
+        this._selectionModeId = Application.selectionController.connect('selection-mode-changed',
+            Lang.bind(this, this._onSelectionModeChanged));
         this._onSelectionModeChanged();
 
-        Global.modeController.connect('window-mode-changed',
-                                      Lang.bind(this, this._onWindowModeChanged));
+        Application.modeController.connect('window-mode-changed',
+            Lang.bind(this, this._onWindowModeChanged));
         this._onWindowModeChanged();
 
-        let selectAll = Global.application.lookup_action('select-all');
+        let selectAll = Application.application.lookup_action('select-all');
         selectAll.connect('activate', Lang.bind(this,
             function() {
                 this.view.select_all();
             }));
 
-        let selectNone = Global.application.lookup_action('select-none');
+        let selectNone = Application.application.lookup_action('select-none');
         selectNone.connect('activate', Lang.bind(this,
             function() {
                 this.view.unselect_all();
             }));
 
-        this._queryId =
-            Global.trackerController.connect('query-status-changed',
-                                             Lang.bind(this, this._onQueryStatusChanged));
+        this._queryId = Application.trackerController.connect('query-status-changed',
+            Lang.bind(this, this._onQueryStatusChanged));
         // ensure the tracker controller is started
-        Global.trackerController.start();
+        Application.trackerController.start();
 
         // this will create the model if we're done querying
         this._onQueryStatusChanged();
     },
 
     _updateTypeForSettings: function() {
-        let viewType = Global.settings.get_enum('view-as');
+        let viewType = Application.settings.get_enum('view-as');
         this.view.set_view_type(viewType);
 
         if (viewType == Gd.MainViewType.LIST)
@@ -257,7 +254,7 @@ const ViewContainer = new Lang.Class({
         listWidget.add_renderer(typeRenderer, Lang.bind(this,
             function(col, cell, model, iter) {
                 let id = model.get_value(iter, Gd.MainColumns.ID);
-                let doc = Global.documentManager.getItemById(id);
+                let doc = Application.documentManager.getItemById(id);
 
                 typeRenderer.text = doc.typeDescription;
             }));
@@ -268,7 +265,7 @@ const ViewContainer = new Lang.Class({
         listWidget.add_renderer(whereRenderer, Lang.bind(this,
             function(col, cell, model, iter) {
                 let id = model.get_value(iter, Gd.MainColumns.ID);
-                let doc = Global.documentManager.getItemById(id);
+                let doc = Application.documentManager.getItemById(id);
 
                 whereRenderer.text = doc.sourceName;
             }));
@@ -278,7 +275,7 @@ const ViewContainer = new Lang.Class({
         listWidget.add_renderer(dateRenderer, Lang.bind(this,
             function(col, cell, model, iter) {
                 let id = model.get_value(iter, Gd.MainColumns.ID);
-                let doc = Global.documentManager.getItemById(id);
+                let doc = Application.documentManager.getItemById(id);
                 let DAY = 86400000000;
 
                 let now = GLib.DateTime.new_now_local();
@@ -320,26 +317,26 @@ const ViewContainer = new Lang.Class({
     },
 
     _onSelectionModeRequest: function() {
-        Global.selectionController.setSelectionMode(true);
+        Application.selectionController.setSelectionMode(true);
     },
 
     _onItemActivated: function(widget, id, path) {
-        Global.documentManager.setActiveItemById(id);
+        Application.documentManager.setActiveItemById(id);
     },
 
     _onQueryStatusChanged: function() {
-        let status = Global.trackerController.getQueryStatus();
+        let status = Application.trackerController.getQueryStatus();
 
         if (!status) {
             // setup a model if we're not querying
             this.view.set_model(this._model.model);
 
             // unfreeze selection
-            Global.selectionController.freezeSelection(false);
+            Application.selectionController.freezeSelection(false);
             this._updateSelection();
         } else {
             // save the last selection
-            Global.selectionController.freezeSelection(true);
+            Application.selectionController.freezeSelection(true);
 
             // if we're querying, clear the model from the view,
             // so that we don't uselessly refresh the rows
@@ -348,7 +345,7 @@ const ViewContainer = new Lang.Class({
     },
 
     _updateSelection: function() {
-        let selected = Global.selectionController.getSelection();
+        let selected = Application.selectionController.getSelection();
         let newSelection = [];
 
         if (!selected.length)
@@ -377,11 +374,11 @@ const ViewContainer = new Lang.Class({
                 return false;
             }));
 
-        Global.selectionController.setSelection(newSelection);
+        Application.selectionController.setSelection(newSelection);
     },
 
     _onSelectionModeChanged: function() {
-        let selectionMode = Global.selectionController.getSelectionMode();
+        let selectionMode = Application.selectionController.getSelectionMode();
         this.view.set_selection_mode(selectionMode);
     },
 
@@ -389,11 +386,11 @@ const ViewContainer = new Lang.Class({
         // update the selection on the controller when the view signals a change
         let selectedURNs = Utils.getURNsFromPaths(this.view.get_selection(),
                                                   this._model.model);
-        Global.selectionController.setSelection(selectedURNs);
+        Application.selectionController.setSelection(selectedURNs);
     },
 
     _onWindowModeChanged: function() {
-        let mode = Global.modeController.getWindowMode();
+        let mode = Application.modeController.getWindowMode();
         if (mode == WindowMode.WindowMode.OVERVIEW)
             this._connectView();
         else



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