[gnome-documents] all: add a controller for item selection
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-documents] all: add a controller for item selection
- Date: Thu, 25 Aug 2011 00:09:01 +0000 (UTC)
commit 5c90ddadd071068af9638e27879c778d101acf2a
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Wed Aug 24 19:36:35 2011 -0400
all: add a controller for item selection
This allows further cleanups, and make the selection permanent across
model, filter or view mode changes.
src/Makefile-js.am | 1 +
src/application.js | 3 ++
src/docFactory.js | 16 ++++++++++
src/global.js | 2 +
src/iconView.js | 43 ++++++++--------------------
src/listView.js | 49 +++++++++-----------------------
src/loadMore.js | 10 +++++-
src/mainWindow.js | 9 +----
src/selectionController.js | 52 ++++++++++++++++++++++++++++++++++
src/trackerModel.js | 25 ++++++++++++----
src/utils.js | 13 ++++++++
src/view.js | 67 ++++++++++++++++++++++++++-----------------
12 files changed, 181 insertions(+), 109 deletions(-)
---
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index 23585b2..efa6c44 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -13,6 +13,7 @@ dist_js_DATA = \
mainWindow.js \
offsetController.js \
preview.js \
+ selectionController.js \
sidebar.js \
sources.js \
spinnerBox.js \
diff --git a/src/application.js b/src/application.js
index 3c58c90..d2330fb 100644
--- a/src/application.js
+++ b/src/application.js
@@ -37,6 +37,7 @@ const Main = imports.main;
const MainWindow = imports.mainWindow;
const OffsetController = imports.offsetController;
const Path = imports.path;
+const SelectionController = imports.selectionController;
const Sources = imports.sources;
const TrackerModel = imports.trackerModel;
@@ -124,6 +125,8 @@ Application.prototype = {
},
_onSourceManagerCreated: function() {
+ Global.selectionController = new SelectionController.SelectionController();
+ Global.model = new TrackerModel.TrackerModel(Global.connection);
this._mainWindow = new MainWindow.MainWindow();
this.activate();
},
diff --git a/src/docFactory.js b/src/docFactory.js
index 111aeeb..38bc5ba 100644
--- a/src/docFactory.js
+++ b/src/docFactory.js
@@ -27,6 +27,7 @@ const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
+const Global = imports.global;
const TrackerModel = imports.trackerModel;
const Utils = imports.utils;
@@ -52,6 +53,18 @@ DocCommon.prototype = {
// overridden in subclasses
this.uri = null;
+
+ this._refreshIconId =
+ Global.settings.connect('changed::list-view',
+ Lang.bind(this, this.refreshIcon));
+ },
+
+ refreshIcon: function() {
+ // fallback
+ },
+
+ destroy: function() {
+ Global.settings.disconnect(this._refreshIconId);
}
};
Signals.addSignalMethods(DocCommon.prototype);
@@ -70,7 +83,10 @@ LocalDocument.prototype = {
// overridden
this.uri = cursor.get_string(TrackerModel.TrackerColumns.URI)[0];
+ this.refreshIcon();
+ },
+ refreshIcon: function() {
this._file = Gio.file_new_for_uri(this.uri);
this._file.query_info_async(_FILE_ATTRIBUTES,
0, 0, null,
diff --git a/src/global.js b/src/global.js
index 11e1607..769ed7f 100644
--- a/src/global.js
+++ b/src/global.js
@@ -24,3 +24,5 @@ let sourceManager = null;
let connection = null;
let settings = null;
let offsetController = null;
+let selectionController = null;
+let model = null;
diff --git a/src/iconView.js b/src/iconView.js
index 99ac3f3..110a9e4 100644
--- a/src/iconView.js
+++ b/src/iconView.js
@@ -32,16 +32,14 @@ const _VIEW_ITEM_WRAP_WIDTH = 128;
const _VIEW_COLUMN_SPACING = 20;
const _VIEW_MARGIN = 16;
-function IconView(window) {
- this._init(window);
+function IconView() {
+ this._init();
}
IconView.prototype = {
__proto__: View.View.prototype,
- _init: function(window) {
- View.View.prototype._init.call(this, window);
-
+ _init: function() {
this.widget = new Gtk.IconView({ hexpand: true,
vexpand: true });
@@ -51,38 +49,21 @@ IconView.prototype = {
this.widget.set_selection_mode(Gtk.SelectionMode.MULTIPLE);
this.widget.connect('item-activated',
- Lang.bind(this, this._onItemActivated));
+ Lang.bind(this, this._onItemActivated));
this.widget.show();
- },
- preUpdate: function() {
- let selection = this.widget.get_selected_items();
-
- View.View.prototype.preUpdate.call(this, selection);
+ View.View.prototype._init.call(this);
+ this.widget.connect('selection-changed',
+ Lang.bind(this, this.onSelectionChanged));
},
- postUpdate: function() {
- if (!this._selectedURNs)
- return;
-
- this._treeModel.foreach(Lang.bind(this,
- function(model, path, iter) {
- let urn = this._treeModel.get_value(iter, TrackerModel.ModelColumns.URN);
- let urnIndex = this._selectedURNs.indexOf(urn);
-
- if (urnIndex != -1) {
- this.widget.select_path(path);
- this._selectedURNs.splice(urnIndex, 1);
- }
-
- if (this._selectedURNs.length == 0)
- return true;
-
- return false;
- }));
+ getSelection: function() {
+ return this.getSelectionObject().get_selected_items();
+ },
- View.View.prototype.postUpdate.call(this);
+ getSelectionObject: function() {
+ return this.widget;
},
createRenderers: function() {
diff --git a/src/listView.js b/src/listView.js
index eb2fb4b..d6d76fe 100644
--- a/src/listView.js
+++ b/src/listView.js
@@ -27,62 +27,41 @@ const TrackerModel = imports.trackerModel;
const View = imports.view;
const Lang = imports.lang;
-function ListView(window) {
- this._init(window);
+function ListView() {
+ this._init();
}
ListView.prototype = {
__proto__: View.View.prototype,
- _init: function(window) {
- View.View.prototype._init.call(this, window);
-
+ _init: function() {
this.widget = new Gtk.TreeView({ hexpand: true,
vexpand: true,
headers_visible: false });
- this.widget.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
-
this.widget.connect('row-activated',
Lang.bind(this, this._onItemActivated));
this.widget.show();
+
+ View.View.prototype._init.call(this);
+
+ let selection = this.widget.get_selection();
+ selection.set_mode(Gtk.SelectionMode.MULTIPLE);
+ selection.connect('changed',
+ Lang.bind(this, this.onSelectionChanged));
},
_onItemActivated: function(view, path, column) {
this.activateItem(path);
},
- preUpdate: function() {
- let treeSelection = this.widget.get_selection();
- let selection = this.widget.get_selected_rows();
-
- View.View.prototype.preUpdate.call(this, selection);
+ getSelectionObject: function() {
+ return this.widget.get_selection();
},
- postUpdate: function() {
- if (!this._selectedURNs)
- return;
-
- let treeSelection = this.widget.get_selection();
-
- this._treeModel.foreach(Lang.bind(this,
- function(model, path, iter) {
- let urn = this._treeModel.get_value(iter, TrackerModel.ModelColumns.URN);
- let urnIndex = this._selectedURNs.indexOf(urn);
-
- if (urnIndex != -1) {
- treeSelection.select_path(path);
- this._selectedURNs.splice(urnIndex, 1);
- }
-
- if (this._selectedURNs.length == 0)
- return true;
-
- return false;
- }));
-
- View.View.prototype.postUpdate.call(this);
+ getSelection: function() {
+ return this.getSelectionObject().get_selected_rows()[0];
},
createRenderers: function() {
diff --git a/src/loadMore.js b/src/loadMore.js
index f0e1c97..f9d5b93 100644
--- a/src/loadMore.js
+++ b/src/loadMore.js
@@ -33,14 +33,20 @@ function LoadMoreButton() {
LoadMoreButton.prototype = {
_init: function() {
this._controller = Global.offsetController;
- this._controller.connect('item-count-changed',
- Lang.bind(this, this._onItemCountChanged));
+ this._controllerId =
+ this._controller.connect('item-count-changed',
+ Lang.bind(this, this._onItemCountChanged));
this.widget = new Gtk.Button();
this.widget.connect('clicked', Lang.bind(this, function() {
this._controller.increaseOffset();
}));
+ this.widget.connect('destroy', Lang.bind(this,
+ function() {
+ this._controller.disconnect(this._controllerId);
+ }));
+
this._onItemCountChanged();
},
diff --git a/src/mainWindow.js b/src/mainWindow.js
index 1b3761e..ee160e8 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -33,7 +33,6 @@ const Global = imports.global;
const LoadMore = imports.loadMore;
const MainToolbar = imports.mainToolbar;
const Sidebar = imports.sidebar;
-const TrackerModel = imports.trackerModel;
const IconView = imports.iconView;
const ListView = imports.listView;
const Preview = imports.preview;
@@ -67,9 +66,8 @@ MainWindow.prototype = {
this.window.connect('delete-event',
Lang.bind(this, this._onDeleteEvent));
- Global.settings.connect('changed::list-view', Lang.bind(this, function() {
- this._refreshViewSettings(true);
- }));
+ Global.settings.connect('changed::list-view',
+ Lang.bind(this, this._refreshViewSettings));
this._grid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL });
this.window.add(this._grid);
@@ -91,8 +89,6 @@ MainWindow.prototype = {
this._viewContainer.add(this._scrolledWin);
this._grid.show_all();
-
- this._model = new TrackerModel.TrackerModel(Global.connection);
this._prepareForOverview();
},
@@ -128,7 +124,6 @@ MainWindow.prototype = {
_refreshViewSettings: function() {
this._initView();
- this.view.setModel(this._model);
},
_prepareForPreview: function(model, document) {
diff --git a/src/selectionController.js b/src/selectionController.js
new file mode 100644
index 0000000..e245db6
--- /dev/null
+++ b/src/selectionController.js
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011 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 Signals = imports.signals;
+
+function SelectionController() {
+ this._init();
+};
+
+SelectionController.prototype = {
+ _init: function() {
+ this._selection = [];
+ },
+
+ setSelection: function(selection) {
+ if (this._isFreezed)
+ return;
+
+ this._selection = selection;
+ this.emit('selection-changed', this._selection);
+ },
+
+ getSelection: function() {
+ return this._selection;
+ },
+
+ freezeSelection: function(freeze) {
+ this._isFreezed = freeze;
+
+ if (!this._isFreezed)
+ this.emit('selection-check');
+ }
+};
+Signals.addSignalMethods(SelectionController.prototype);
diff --git a/src/trackerModel.js b/src/trackerModel.js
index c9c7f80..3c80e73 100644
--- a/src/trackerModel.js
+++ b/src/trackerModel.js
@@ -192,9 +192,10 @@ function TrackerModel(connection) {
TrackerModel.prototype = {
_init: function(connection) {
+ this._docs = [];
+
this._builder = new QueryBuilder();
this._factory = new DocFactory.DocFactory();
- Global.settings.connect('changed::list-view', Lang.bind(this, this._refresh));
this.model = Gd.create_list_store();
this._connection = connection;
@@ -230,7 +231,6 @@ TrackerModel.prototype = {
// no changes processed, to avoid uselessly refreshing the view.
// That requires support for the Changes feed in libgdata, see
// https://bugzilla.gnome.org/show_bug.cgi?id=654652
- this._emitModelUpdatePending();
this._refresh();
Mainloop.timeout_add_seconds(MINER_REFRESH_TIMEOUT,
@@ -259,6 +259,12 @@ TrackerModel.prototype = {
if (objectIter)
Gd.store_update_icon(this.model, objectIter, newDoc.pixbuf);
}));
+
+ this._docs.push(newDoc);
+ },
+
+ _onQueryFinished: function() {
+ Global.selectionController.freezeSelection(false);
},
_onCursorNext: function(cursor, res) {
@@ -267,12 +273,13 @@ TrackerModel.prototype = {
if (!valid) {
// signal the total count update and return
- this._emitModelUpdateDone();
+ this._onQueryFinished();
return;
}
} catch (e) {
// FIXME: error handling
log('Unable to fetch results from cursor: ' + e.toString());
+ this._onQueryFinished();
return;
}
@@ -288,6 +295,7 @@ TrackerModel.prototype = {
} catch (e) {
// FIXME: error handling
log('Unable to execute query: ' + e.toString());
+ this._onQueryFinished();
}
},
@@ -300,12 +308,15 @@ TrackerModel.prototype = {
this.emit('model-update-done');
},
- _emitModelUpdatePending: function() {
- this.emit('model-update-pending');
- },
-
_refresh: function() {
+ Global.selectionController.freezeSelection(true);
this.model.clear();
+
+ this._docs.forEach(function(doc) {
+ doc.destroy();
+ });
+ this._docs = [];
+
this._performCurrentQuery();
},
diff --git a/src/utils.js b/src/utils.js
index 56e3ae0..0299b20 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -22,6 +22,9 @@
const Gtk = imports.gi.Gtk;
const Global = imports.global;
+const TrackerModel = imports.trackerModel;
+
+const Lang = imports.lang;
const _ICON_VIEW_SIZE = 128;
const _LIST_VIEW_SIZE = 48;
@@ -57,3 +60,13 @@ function pixbufFromRdfType(type) {
return pixbuf;
}
+
+function getURNsFromPaths(paths, model) {
+ return paths.map(Lang.bind(this,
+ function(path) {
+ let iter = model.get_iter(path)[1];
+ let urn = model.get_value(iter, TrackerModel.ModelColumns.URN);
+
+ return urn;
+ }));
+}
diff --git a/src/view.js b/src/view.js
index 0c25dd7..3bf752b 100644
--- a/src/view.js
+++ b/src/view.js
@@ -24,51 +24,64 @@ const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Signals = imports.signals;
+const Global = imports.global;
const TrackerModel = imports.trackerModel;
+const Utils = imports.utils;
-function View(window) {
- this._init(window);
+function View() {
+ this._init();
}
View.prototype = {
- _init: function(window) {
+ _init: function() {
this._selectedURNs = null;
- this.window = window;
- },
-
- destroy: function() {
- this.widget.destroy();
- },
- setModel: function(model) {
- this.model = model;
- this.model.connect('model-update-pending', Lang.bind(this,
- function() {
- this.preUpdate();
- }));
- this.model.connect('model-update-done', Lang.bind(this,
+ this.model = Global.model;
+ this._treeModel = Global.model.model;
+ this.widget.set_model(this._treeModel);
+ this.widget.connect('destroy', Lang.bind(this,
function() {
- this.postUpdate();
+ Global.selectionController.disconnect(this._selectionControllerId);
}));
- this._treeModel = model.model;
- this.widget.set_model(this._treeModel);
-
this.createRenderers();
+
+ this._selectionController = Global.selectionController;
+ this._selectionControllerId =
+ this._selectionController.connect('selection-check',
+ Lang.bind(this, this._updateSelection));
+
+ this._updateSelection();
},
- preUpdate: function(selection) {
- this._selectedURNs = selection.map(Lang.bind(this,
- function(path) {
- let iter = this._treeModel.get_iter(path)[1];
+ _updateSelection: function() {
+ let selectionObject = this.getSelectionObject();
+ let selected = this._selectionController.getSelection().slice(0);
+
+ if (!selected.length)
+ return;
+
+ this._treeModel.foreach(Lang.bind(this,
+ function(model, path, iter) {
let urn = this._treeModel.get_value(iter, TrackerModel.ModelColumns.URN);
+ let urnIndex = selected.indexOf(urn);
- return urn;
+ if (urnIndex != -1) {
+ selectionObject.select_path(path);
+ selected.splice(urnIndex, 1);
+ }
+
+ if (selected.length == 0)
+ return true;
+
+ return false;
}));
},
- postUpdate: function() {
- this._selectedURNs = null;
+ onSelectionChanged: function() {
+ let selectedURNs = Utils.getURNsFromPaths(this.getSelection(),
+ this._treeModel);
+ Global.selectionController.setSelection(selectedURNs);
},
// this must be overridden by all implementations
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]