[gnome-documents/wip/rishi/split-view: 13/15] Rework the guts of the query engine to accomodate multiple views



commit 158ae6f375e4b874c5d766d9832f61c717cef97a
Author: Debarshi Ray <debarshir gnome org>
Date:   Fri Oct 17 13:52:47 2014 +0200

    Rework the guts of the query engine to accomodate multiple views
    
    The objective is to have separate views for documents, collections and
    search results. The first two views will always show only documents and
    collections arranged in reverse chronological order. Whenever search
    constraints are applied the results are supposed to show up in the
    search view.
    
    Each view will have its separate TrackerController and OffsetController
    implementations to decide when to refresh its contents and what the
    current offsets are. The OffsetController is no longer a part of the
    search context because the different views need to use their own
    implementations. The search provider does not have a way to change the
    offsets, so it can just use the initial value of [0, 50).
    
    New flags corresponding to each of the views have been added to
    QueryFlags. These are passed around to the classes that play a part in
    constructing the queries.
    
    The meaning of the NONE and UNFILTERED flags have also changed.
    Earlier, UNFILTERED was used to completely turn off search categories,
    search matches, search types and collections, while NONE had them
    enabled. Now, NONE will disable the search type WHERE clauses but keep
    everything else enabled so that it can be used to create queries for
    browsing the contents of a collection.
    
    Since the getFilter method of the various BaseManager classes are now
    affected by their roles in the individual views, it no longer makes
    sense to have a generic implementation in the parent. Instead,
    getAllFilter has been made public so that the children can use it to
    construct their query snippets.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=686461

 src/application.js         |   21 ++++++++--
 src/embed.js               |    6 +-
 src/manager.js             |   14 +-----
 src/query.js               |   47 +++++++++++++++--------
 src/search.js              |   92 ++++++++++++++++++++++++++++++++++++++------
 src/shellSearchProvider.js |    3 +-
 src/trackerController.js   |   70 ++++++++++++++++++++++++++++++---
 src/view.js                |   55 ++++++++++++++++++++------
 8 files changed, 240 insertions(+), 68 deletions(-)
---
diff --git a/src/application.js b/src/application.js
index e5ee0ec..5de17a2 100644
--- a/src/application.js
+++ b/src/application.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012 Red Hat, Inc.
+ * Copyright (c) 2011, 2012, 2014 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
@@ -77,7 +77,9 @@ let cssProvider = null;
 let documentManager = null;
 let modeController = null;
 let notificationManager = null;
-let offsetController = null;
+let offsetCollectionsController = null;
+let offsetDocumentsController = null;
+let offsetSearchController = null;
 let queryBuilder = null;
 let searchCategoryManager = null;
 let searchController = null;
@@ -85,7 +87,9 @@ let searchMatchManager = null;
 let searchTypeManager = null;
 let selectionController = null;
 let sourceManager = null;
-let trackerController = null;
+let trackerCollectionsController = null;
+let trackerDocumentsController = null;
+let trackerSearchController = null;
 
 const TrackerExtractPriorityIface = '<node> \
 <interface name="org.freedesktop.Tracker1.Extract.Priority"> \
@@ -488,7 +492,12 @@ const Application = new Lang.Class({
         Search.initSearch(imports.shellSearchProvider);
 
         modeController = new WindowMode.ModeController();
-        trackerController = new TrackerController.TrackerOverviewController();
+        offsetCollectionsController = new Search.OffsetCollectionsController();
+        offsetDocumentsController = new Search.OffsetDocumentsController();
+        offsetSearchController = new Search.OffsetSearchController();
+        trackerCollectionsController = new TrackerController.TrackerCollectionsController();
+        trackerDocumentsController = new TrackerController.TrackerDocumentsController();
+        trackerSearchController = new TrackerController.TrackerSearchController();
         selectionController = new Selections.SelectionController();
 
         this._actionEntries = [
@@ -639,7 +648,9 @@ const Application = new Lang.Class({
         // clean up signals
         changeMonitor.disconnectAll();
         documentManager.disconnectAll();
-        trackerController.disconnectAll();
+        trackerCollectionsController.disconnectAll();
+        trackerDocumentsController.disconnectAll();
+        trackerSearchController.disconnectAll();
         selectionController.disconnectAll();
         modeController.disconnectAll();
         this.disconnectAllJS();
diff --git a/src/embed.js b/src/embed.js
index 41aa8c4..1a56c79 100644
--- a/src/embed.js
+++ b/src/embed.js
@@ -121,8 +121,8 @@ const Embed = new Lang.Class({
 
         Application.modeController.connect('fullscreen-changed',
                                            Lang.bind(this, this._onFullscreenChanged));
-        Application.trackerController.connect('query-status-changed',
-                                              Lang.bind(this, this._onQueryStatusChanged));
+        Application.trackerDocumentsController.connect('query-status-changed',
+                                                       Lang.bind(this, this._onQueryStatusChanged));
 
         Application.documentManager.connect('active-changed',
                                             Lang.bind(this, this._onActiveItemChanged));
@@ -156,7 +156,7 @@ const Embed = new Lang.Class({
         if (windowMode != WindowMode.WindowMode.OVERVIEW)
             return;
 
-        let queryStatus = Application.trackerController.getQueryStatus();
+        let queryStatus = Application.trackerDocumentsController.getQueryStatus();
 
         if (queryStatus) {
             this._spinnerBox.start();
diff --git a/src/manager.js b/src/manager.js
index 03bbe84..9dac51b 100644
--- a/src/manager.js
+++ b/src/manager.js
@@ -124,16 +124,8 @@ const BaseManager = new Lang.Class({
         this.emit('clear');
     },
 
-    getFilter: function() {
-        let item = this.getActiveItem();
-        let retval = '';
-
-        if (item.id == 'all')
-            retval = this._getAllFilter();
-        else if (item && item.getFilter)
-            retval = item.getFilter();
-
-        return retval;
+    getFilter: function(flags) {
+        log('Error: BaseManager implementations must override getFilter');
     },
 
     getWhere: function() {
@@ -151,7 +143,7 @@ const BaseManager = new Lang.Class({
             func(this._items[idx]);
     },
 
-    _getAllFilter: function() {
+    getAllFilter: function() {
         let filters = [];
 
         this.forEachItem(function(item) {
diff --git a/src/query.js b/src/query.js
index 467a556..6543152 100644
--- a/src/query.js
+++ b/src/query.js
@@ -23,6 +23,7 @@ const GdPrivate = imports.gi.GdPrivate;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const Lang = imports.lang;
+const Search = imports.search;
 
 const QueryColumns = {
     URN: 0,
@@ -41,7 +42,10 @@ const QueryColumns = {
 
 const QueryFlags = {
     NONE: 0,
-    UNFILTERED: 1 << 0
+    UNFILTERED: 1 << 0,
+    COLLECTIONS: 1 << 1,
+    DOCUMENTS: 1 << 2,
+    SEARCH: 1 << 3
 };
 
 const LOCAL_DOCUMENTS_COLLECTIONS_IDENTIFIER = 'gd:collection:local:';
@@ -59,12 +63,12 @@ const QueryBuilder = new Lang.Class({
                  activeSource: this._context.sourceManager.getActiveItem() };
     },
 
-    _buildFilterString: function(currentType) {
+    _buildFilterString: function(currentType, flags) {
         let filters = [];
 
-        filters.push(this._context.searchMatchManager.getFilter());
-        filters.push(this._context.sourceManager.getFilter());
-        filters.push(this._context.searchCategoryManager.getFilter());
+        filters.push(this._context.searchMatchManager.getFilter(flags));
+        filters.push(this._context.sourceManager.getFilter(flags));
+        filters.push(this._context.searchCategoryManager.getFilter(flags));
 
         if (currentType) {
             filters.push(currentType.getFilter());
@@ -86,10 +90,14 @@ const QueryBuilder = new Lang.Class({
         let whereParts = [];
         let searchTypes = [];
 
-        if (flags & QueryFlags.UNFILTERED)
-            searchTypes = this._context.searchTypeManager.getAllTypes();
-        else
+        if (flags & QueryFlags.COLLECTIONS)
+            searchTypes = [this._context.searchTypeManager.getItemById(Search.SearchTypeStock.COLLECTIONS)];
+        else if (flags & QueryFlags.DOCUMENTS)
+            searchTypes = this._context.searchTypeManager.getDocumentTypes();
+        else if (flags & QueryFlags.SEARCH)
             searchTypes = this._context.searchTypeManager.getCurrentTypes();
+        else
+            searchTypes = this._context.searchTypeManager.getAllTypes();
 
         // build an array of WHERE clauses; each clause maps to one
         // type of resource we're looking for.
@@ -102,7 +110,7 @@ const QueryBuilder = new Lang.Class({
                         part += this._context.searchCategoryManager.getWhere() +
                                 this._context.documentManager.getWhere();
 
-                    part += this._buildFilterString(currentType);
+                    part += this._buildFilterString(currentType, flags);
                 }
 
                 part += ' }';
@@ -116,16 +124,23 @@ const QueryBuilder = new Lang.Class({
         return whereSparql;
     },
 
-    _buildQueryInternal: function(global, flags) {
+    _buildQueryInternal: function(global, flags, offsetController) {
         let whereSparql = this._buildWhere(global, flags);
         let tailSparql = '';
 
         // order results by mtime
         if (global) {
+            let offset = 0;
+            let step = Search.OFFSET_STEP;
+
+            if (offsetController) {
+                offset = offsetController.getOffset();
+                step = offsetController.getOffsetStep();
+            }
+
             tailSparql +=
                 'ORDER BY DESC (?mtime)' +
-                ('LIMIT %d OFFSET %d').format(this._context.offsetController.getOffsetStep(),
-                                              this._context.offsetController.getOffset());
+                ('LIMIT %d OFFSET %d').format(step, offset);
         }
 
         let sparql =
@@ -153,13 +168,13 @@ const QueryBuilder = new Lang.Class({
         return this._createQuery(sparql);
     },
 
-    buildGlobalQuery: function() {
-        return this._createQuery(this._buildQueryInternal(true, QueryFlags.NONE));
+    buildGlobalQuery: function(flags, offsetController) {
+        return this._createQuery(this._buildQueryInternal(true, flags, offsetController));
     },
 
-    buildCountQuery: function() {
+    buildCountQuery: function(flags) {
         let sparql = 'SELECT DISTINCT COUNT(?urn) ' +
-            this._buildWhere(true, QueryFlags.NONE);
+            this._buildWhere(true, flags);
 
         return this._createQuery(sparql);
     },
diff --git a/src/search.js b/src/search.js
index af5c096..557ffeb 100644
--- a/src/search.js
+++ b/src/search.js
@@ -41,7 +41,6 @@ function initSearch(context) {
     context.searchMatchManager = new SearchMatchManager(context);
     context.searchTypeManager = new SearchTypeManager(context);
     context.searchController = new SearchController(context);
-    context.offsetController = new OffsetOverviewController(context);
     context.queryBuilder = new Query.QueryBuilder(context);
 };
 
@@ -138,6 +137,12 @@ const SearchCategoryManager = new Lang.Class({
         // this._categories[category.id] = category;
 
         this.setActiveItem(recent);
+    },
+
+    getFilter: function(flags) {
+        // Since we don't expose the SearchCategoryManager in the UI,
+        // this is a placeholder for the moment.
+        return '(true)';
     }
 });
 
@@ -320,7 +325,10 @@ const SearchMatchManager = new Lang.Class({
         this.setActiveItemById(SearchMatchStock.ALL);
     },
 
-    getFilter: function() {
+    getFilter: function(flags) {
+        if ((flags & Query.QueryFlags.SEARCH) == 0)
+            return '(true)';
+
         let terms = this.context.searchController.getTerms();
         let filters = [];
 
@@ -328,7 +336,16 @@ const SearchMatchManager = new Lang.Class({
             this.forEachItem(function(item) {
                 item.setFilterTerm(terms[i]);
             });
-            filters.push(this.parent());
+
+            let filter;
+            let item = this.getActiveItem();
+
+            if (item.id == SearchMatchStock.ALL)
+                filter = this.getAllFilter();
+            else
+                filter = item.getFilter();
+
+            filters.push(filter);
         }
         return filters.length ? '( ' + filters.join(' && ') + ')' : '(true)';
     }
@@ -507,6 +524,24 @@ const SourceManager = new Lang.Class({
         this.processNewItems(newItems);
     },
 
+    getFilter: function(flags) {
+        let item;
+
+        if (flags & Query.QueryFlags.SEARCH)
+            item = this.getActiveItem();
+        else
+            item = this.getItemById(SearchSourceStock.ALL);
+
+        let filter;
+
+        if (item.id == SearchSourceStock.ALL)
+            filter = this.getAllFilter();
+        else
+            filter = item.getFilter();
+
+        return filter;
+    },
+
     getFilterNotLocal: function() {
         let sources = this.getItems();
         let filters = [];
@@ -555,7 +590,7 @@ const SourceManager = new Lang.Class({
     }
 });
 
-const _OFFSET_STEP = 50;
+const OFFSET_STEP = 50;
 
 const OffsetController = new Lang.Class({
     Name: 'OffsetController',
@@ -567,7 +602,7 @@ const OffsetController = new Lang.Class({
 
     // to be called by the view
     increaseOffset: function() {
-        this._offset += _OFFSET_STEP;
+        this._offset += OFFSET_STEP;
         this.emit('offset-changed', this._offset);
     },
 
@@ -614,11 +649,11 @@ const OffsetController = new Lang.Class({
     },
 
     getRemainingDocs: function() {
-        return (this._itemCount - (this._offset + _OFFSET_STEP));
+        return (this._itemCount - (this._offset + OFFSET_STEP));
     },
 
     getOffsetStep: function() {
-        return _OFFSET_STEP;
+        return OFFSET_STEP;
     },
 
     getOffset: function() {
@@ -627,16 +662,49 @@ const OffsetController = new Lang.Class({
 });
 Signals.addSignalMethods(OffsetController.prototype);
 
-const OffsetOverviewController = new Lang.Class({
-    Name: 'OffsetOverviewController',
+const OffsetCollectionsController = new Lang.Class({
+    Name: 'OffsetCollectionsController',
     Extends: OffsetController,
 
-    _init: function(context) {
+    _init: function() {
+        this.parent();
+    },
+
+    getQuery: function() {
+        let activeCollection = Application.documentManager.getActiveCollection();
+        let flags;
+
+        if (activeCollection)
+            flags = Query.QueryFlags.NONE;
+        else
+            flags = Query.QueryFlags.COLLECTIONS;
+
+        return Application.queryBuilder.buildCountQuery(flags);
+    }
+});
+
+const OffsetDocumentsController = new Lang.Class({
+    Name: 'OffsetDocumentsController',
+    Extends: OffsetController,
+
+    _init: function() {
+        this.parent();
+    },
+
+    getQuery: function() {
+        return Application.queryBuilder.buildCountQuery(Query.QueryFlags.DOCUMENTS);
+    }
+});
+
+const OffsetSearchController = new Lang.Class({
+    Name: 'OffsetSearchController',
+    Extends: OffsetController,
+
+    _init: function() {
         this.parent();
-        this._context = context;
     },
 
     getQuery: function() {
-        return this._context.queryBuilder.buildCountQuery();
+        return Application.queryBuilder.buildCountQuery(Query.QueryFlags.SEARCH);
     }
 });
diff --git a/src/shellSearchProvider.js b/src/shellSearchProvider.js
index 2711714..6f77a24 100644
--- a/src/shellSearchProvider.js
+++ b/src/shellSearchProvider.js
@@ -39,7 +39,6 @@ const TrackerUtils = imports.trackerUtils;
 const Utils = imports.utils;
 
 let documentManager = null;
-let offsetController = null;
 let queryBuilder = null;
 let searchCategoryManager = null;
 let searchMatchManager = null;
@@ -319,7 +318,7 @@ const FetchIdsJob = new Lang.Class({
         this._cancellable = cancellable;
         searchController.setString(this._terms.join(' '));
 
-        let query = queryBuilder.buildGlobalQuery();
+        let query = queryBuilder.buildGlobalQuery(Query.QueryFlags.SEARCH, null);
         Application.connectionQueue.add(query.sparql, this._cancellable, Lang.bind(this,
             function(object, res) {
                 let cursor = null;
diff --git a/src/trackerController.js b/src/trackerController.js
index 38e374c..3a71a4c 100644
--- a/src/trackerController.js
+++ b/src/trackerController.js
@@ -281,15 +281,72 @@ const TrackerController = new Lang.Class({
 });
 Signals.addSignalMethods(TrackerController.prototype);
 
-const TrackerOverviewController = new Lang.Class({
-    Name: 'TrackerOverviewController',
+const TrackerCollectionsController = new Lang.Class({
+    Name: 'TrackerCollectionsController',
     Extends: TrackerController,
 
     _init: function() {
-        this.parent(WindowMode.WindowMode.OVERVIEW);
+        this.parent(WindowMode.WindowMode.COLLECTIONS);
+
+        Application.documentManager.connect('active-collection-changed', Lang.bind(this,
+            function() {
+                let windowMode = Application.modeController.getWindowMode();
+                if (windowMode == WindowMode.WindowMode.COLLECTIONS)
+                    this.refreshForObject();
+            }));
+    },
+
+    getOffsetController: function() {
+        return Application.offsetCollectionsController;
+    },
+
+    getQuery: function() {
+        let flags;
+        let activeCollection = Application.documentManager.getActiveCollection();
+
+        if (activeCollection)
+            flags = Query.QueryFlags.NONE;
+        else
+            flags = Query.QueryFlags.COLLECTIONS;
+
+        return Application.queryBuilder.buildGlobalQuery(flags,
+                                                         Application.offsetCollectionsController);
+    },
+});
+
+const TrackerDocumentsController = new Lang.Class({
+    Name: 'TrackerDocumentsController',
+    Extends: TrackerController,
+
+    _init: function() {
+        this.parent(WindowMode.WindowMode.DOCUMENTS);
+    },
+
+    getOffsetController: function() {
+        return Application.offsetDocumentsController;
+    },
+
+    getQuery: function() {
+        return Application.queryBuilder.buildGlobalQuery(Query.QueryFlags.DOCUMENTS,
+                                                         Application.offsetDocumentsController);
+    },
+});
+
+const TrackerSearchController = new Lang.Class({
+    Name: 'TrackerSearchController',
+    Extends: TrackerController,
+
+    _init: function() {
+        this.parent(WindowMode.WindowMode.SEARCH);
+
+        Application.documentManager.connect('active-collection-changed', Lang.bind(this,
+            function() {
+                let windowMode = Application.modeController.getWindowMode();
+                if (windowMode == WindowMode.WindowMode.SEARCH)
+                    this.refreshForObject();
+            }));
 
         Application.sourceManager.connect('active-changed', Lang.bind(this, this.refreshForObject));
-        Application.documentManager.connect('active-collection-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));
@@ -305,10 +362,11 @@ const TrackerOverviewController = new Lang.Class({
     },
 
     getOffsetController: function() {
-        return Application.offsetController;
+        return Application.offsetSearchController;
     },
 
     getQuery: function() {
-        return Application.queryBuilder.buildGlobalQuery();
+        return Application.queryBuilder.buildGlobalQuery(Query.QueryFlags.SEARCH,
+                                                         Application.offsetSearchController);
     },
 });
diff --git a/src/view.js b/src/view.js
index faacaab..4d19a8b 100644
--- a/src/view.js
+++ b/src/view.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Red Hat, Inc.
+ * Copyright (c) 2011, 2015 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
@@ -37,12 +37,37 @@ const TrackerUtils = imports.trackerUtils;
 const WindowMode = imports.windowMode;
 const Utils = imports.utils;
 
+function getController(windowMode) {
+    let offsetController;
+    let trackerController;
+
+    switch (windowMode) {
+    case WindowMode.WindowMode.COLLECTIONS:
+        offsetController = Application.offsetCollectionsController;
+        trackerController = Application.trackerCollectionsController;
+        break;
+    case WindowMode.WindowMode.DOCUMENTS:
+        offsetController = Application.offsetDocumentsController;
+        trackerController = Application.trackerDocumentsController;
+        break;
+    case WindowMode.WindowMode.SEARCH:
+        offsetController = Application.offsetSearchController;
+        trackerController = Application.trackerSearchController;
+        break;
+    default:
+        throw(new Error('Not handled'));
+        break;
+    }
+
+    return [ offsetController, trackerController ];
+}
+
 const _RESET_COUNT_TIMEOUT = 500; // msecs
 
 const ViewModel = new Lang.Class({
     Name: 'ViewModel',
 
-    _init: function() {
+    _init: function(windowMode) {
         this.model = Gtk.ListStore.new(
             [ GObject.TYPE_STRING,
               GObject.TYPE_STRING,
@@ -62,7 +87,8 @@ const ViewModel = new Lang.Class({
         Application.documentManager.connect('item-removed',
             Lang.bind(this, this._onItemRemoved));
 
-        Application.trackerController.connect('query-status-changed', Lang.bind(this,
+        [ this._offsetController, this._trackerController ] = getController(windowMode);
+        this._trackerController.connect('query-status-changed', Lang.bind(this,
             function(o, status) {
                 if (!status)
                     return;
@@ -129,7 +155,7 @@ const ViewModel = new Lang.Class({
             this._resetCountId = Mainloop.timeout_add(_RESET_COUNT_TIMEOUT, Lang.bind(this,
                 function() {
                     this._resetCountId = 0;
-                    Application.offsetController.resetItemCount();
+                    this._offsetController.resetItemCount();
                     return false;
                 }));
         }
@@ -271,10 +297,11 @@ const ErrorBox = new Lang.Class({
 const ViewContainer = new Lang.Class({
     Name: 'ViewContainer',
 
-    _init: function() {
+    _init: function(windowMode) {
         this._edgeHitId = 0;
+        this._mode = windowMode;
 
-        this._model = new ViewModel();
+        this._model = new ViewModel(windowMode);
 
         this.widget = new Gtk.Stack({ homogeneous: true,
                                       transition_type: Gtk.StackTransitionType.CROSSFADE });
@@ -327,7 +354,9 @@ const ViewContainer = new Lang.Class({
                 this.view.unselect_all();
             }));
 
-        Application.offsetController.connect('item-count-changed', Lang.bind(this,
+        [ this._offsetController, this._trackerController ] = getController(windowMode);
+
+        this._offsetController.connect('item-count-changed', Lang.bind(this,
             function(controller, count) {
                 if (count == 0)
                     this.widget.set_visible_child_name('no-results');
@@ -335,12 +364,12 @@ const ViewContainer = new Lang.Class({
                     this.widget.set_visible_child_name('view');
             }));
 
-        Application.trackerController.connect('query-error',
+        this._trackerController.connect('query-error',
             Lang.bind(this, this._onQueryError));
-        this._queryId = Application.trackerController.connect('query-status-changed',
+        this._queryId = this._trackerController.connect('query-status-changed',
             Lang.bind(this, this._onQueryStatusChanged));
         // ensure the tracker controller is started
-        Application.trackerController.start();
+        this._trackerController.start();
 
         // this will create the model if we're done querying
         this._onQueryStatusChanged();
@@ -456,7 +485,7 @@ const ViewContainer = new Lang.Class({
     },
 
     _onQueryStatusChanged: function() {
-        let status = Application.trackerController.getQueryStatus();
+        let status = this._trackerController.getQueryStatus();
 
         if (!status) {
             // setup a model if we're not querying
@@ -527,7 +556,7 @@ const ViewContainer = new Lang.Class({
 
     _onWindowModeChanged: function() {
         let mode = Application.modeController.getWindowMode();
-        if (mode == WindowMode.WindowMode.OVERVIEW)
+        if (mode == this._mode)
             this._connectView();
         else
             this._disconnectView();
@@ -537,7 +566,7 @@ const ViewContainer = new Lang.Class({
         this._edgeHitId = this.view.connect('edge-reached', Lang.bind(this,
             function (view, pos) {
                 if (pos == Gtk.PositionType.BOTTOM)
-                    Application.offsetController.increaseOffset();
+                    this._offsetController.increaseOffset();
             }));
     },
 


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