[gnome-documents] collections: first implementation of an "Organize" dialog
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-documents] collections: first implementation of an "Organize" dialog
- Date: Wed, 16 Nov 2011 02:47:22 +0000 (UTC)
commit 2bfc65ed91c744fcde6f3c482688e05cd287a84a
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Thu Nov 10 17:08:22 2011 -0500
collections: first implementation of an "Organize" dialog
src/documents.js | 2 +-
src/lib/gd-utils.c | 28 +++
src/lib/gd-utils.h | 7 +
src/query.js | 43 ++++-
src/selections.js | 577 +++++++++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 649 insertions(+), 8 deletions(-)
---
diff --git a/src/documents.js b/src/documents.js
index be7501c..7a435e1 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -857,7 +857,7 @@ DocumentManager.prototype = {
setActiveItem: function(doc) {
if (Manager.BaseManager.prototype.setActiveItem.call(this, doc)) {
- if (doc) {
+ if (doc && !doc.collection) {
let recentManager = Gtk.RecentManager.get_default();
recentManager.add_item(doc.uri);
}
diff --git a/src/lib/gd-utils.c b/src/lib/gd-utils.c
index 70cc6f9..ecc5bac 100644
--- a/src/lib/gd-utils.c
+++ b/src/lib/gd-utils.c
@@ -93,6 +93,34 @@ gd_item_store_set (GtkListStore *store,
-1);
}
+/**
+ * gd_create_organize_store:
+ *
+ * Returns: (transfer full):
+ */
+GtkListStore *
+gd_create_organize_store (void)
+{
+ return gtk_list_store_new (3,
+ G_TYPE_STRING, // ID
+ G_TYPE_STRING, // NAME
+ G_TYPE_INT); // STATE
+}
+
+void
+gd_organize_store_set (GtkListStore *store,
+ GtkTreeIter *iter,
+ const gchar *id,
+ const gchar *name,
+ gint state)
+{
+ gtk_list_store_set (store, iter,
+ 0, id,
+ 1, name,
+ 2, state,
+ -1);
+}
+
#define ATTRIBUTES_FOR_THUMBNAIL \
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," \
G_FILE_ATTRIBUTE_TIME_MODIFIED
diff --git a/src/lib/gd-utils.h b/src/lib/gd-utils.h
index 049be08..8fea96f 100644
--- a/src/lib/gd-utils.h
+++ b/src/lib/gd-utils.h
@@ -40,6 +40,13 @@ void gd_item_store_set (GtkListStore *store,
const gchar *name,
const gchar *heading_text);
+GtkListStore* gd_create_organize_store (void);
+void gd_organize_store_set (GtkListStore *store,
+ GtkTreeIter *iter,
+ const gchar *id,
+ const gchar *name,
+ gint state);
+
void gd_queue_thumbnail_job_for_file_async (GFile *file,
GAsyncReadyCallback callback,
gpointer user_data);
diff --git a/src/query.js b/src/query.js
index fcf0e4c..2847392 100644
--- a/src/query.js
+++ b/src/query.js
@@ -21,6 +21,7 @@
const Global = imports.global;
+const Gd = imports.gi.Gd;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
@@ -44,6 +45,8 @@ const QueryFlags = {
UNFILTERED: 1 << 0
};
+const LOCAL_COLLECTIONS_IDENTIFIER = 'gd:collection:local:';
+
function Query(sparql) {
this._init(sparql);
}
@@ -90,7 +93,8 @@ QueryBuilder.prototype = {
let filter =
('((fn:starts-with (nie:url(?urn), "%s")) || ' +
' (fn:starts-with (nie:url(?urn), "%s")) || ' +
- ' (fn:starts-with (nie:url(?urn), "%s")))').format(desktopURI, documentsURI, downloadsURI);
+ ' (fn:starts-with (nie:url(?urn), "%s")) || ' +
+ ' (fn:starts-with (nao:identifier(?urn), "gd:collection:local:")))').format(desktopURI, documentsURI, downloadsURI);
return filter;
},
@@ -214,5 +218,42 @@ QueryBuilder.prototype = {
'LIMIT 4').replace('?collUrn', '<' + resource + '>');
return new Query(sparql);
+ },
+
+ // queries for all the collections the given item is part of
+ buildFetchCollectionsQuery: function(resource) {
+ let sparql =
+ ('SELECT ' +
+ '?urn ' +
+ 'WHERE { ?urn a nfo:DataContainer . ?docUrn nie:isPartOf ?urn }'
+ ).replace('?docUrn', '<' + resource + '>');
+
+ return new Query(sparql);
+ },
+
+ // adds or removes the given item to the given collection
+ buildSetCollectionQuery: function(itemUrn, collectionUrn, setting) {
+ let sparql = ('%s { <%s> nie:isPartOf <%s> }'
+ ).format((setting ? 'INSERT' : 'DELETE'), itemUrn, collectionUrn);
+ return new Query(sparql);
+ },
+
+ // bumps the mtime to current time for the given resource
+ buildUpdateMtimeQuery: function(resource) {
+ let time = Gd.iso8601_from_timestamp(GLib.get_real_time() / GLib.USEC_PER_SEC);
+ let sparql = ('INSERT OR REPLACE { <%s> nie:contentLastModified \"%s\" }'
+ ).format(resource, time);
+
+ return new Query(sparql);
+ },
+
+ buildCreateCollectionQuery: function(name) {
+ let time = Gd.iso8601_from_timestamp(GLib.get_real_time() / GLib.USEC_PER_SEC);
+ let sparql = ('INSERT { _:res a nfo:DataContainer ; a nie:DataObject ; ' +
+ 'nie:contentLastModified \"' + time + '\" ; ' +
+ 'nie:title \"' + name + '\" ; ' +
+ 'nao:identifier \"' + LOCAL_COLLECTIONS_IDENTIFIER + name + '\" }');
+
+ return new Query(sparql);
}
};
diff --git a/src/selections.js b/src/selections.js
index 4f59619..c43bc8e 100644
--- a/src/selections.js
+++ b/src/selections.js
@@ -19,16 +19,555 @@
*
*/
+const Gd = imports.gi.Gd;
+const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const GtkClutter = imports.gi.GtkClutter;
+const Pango = imports.gi.Pango;
const _ = imports.gettext.gettext;
+const Documents = imports.documents;
const Global = imports.global;
+const Manager = imports.manager;
+const Query = imports.query;
const Tweener = imports.util.tweener;
+const Utils = imports.utils;
const Lang = imports.lang;
const Signals = imports.signals;
+// fetch all the collections a given item is part of
+function FetchCollectionsJob(urn) {
+ this._init(urn);
+}
+
+FetchCollectionsJob.prototype = {
+ _init: function(urn) {
+ this._urn = urn;
+ this._collections = [];
+ },
+
+ run: function(callback) {
+ this._callback = callback;
+
+ let query = Global.queryBuilder.buildFetchCollectionsQuery(this._urn);
+ Global.connectionQueue.add(query.sparql, null, Lang.bind(this,
+ function(object, res) {
+ let cursor = null;
+
+ try {
+ cursor = object.query_finish(res);
+ cursor.next_async(null, Lang.bind(this, this._onCursorNext));
+ } catch (e) {
+ log(e);
+ this._emitCallback();
+ }
+ }));
+ },
+
+ _onCursorNext: function(cursor, res) {
+ let valid = false;
+
+ try {
+ valid = cursor.next_finish(res);
+ } catch (e) {
+ log(e);
+ }
+
+ if (!valid) {
+ cursor.close();
+ this._emitCallback();
+
+ return;
+ }
+
+ let urn = cursor.get_string(0)[0];
+ this._collections.push(urn);
+
+ cursor.next_async(null, Lang.bind(this, this._onCursorNext));
+ },
+
+ _emitCallback: function() {
+ this._callback(this._collections);
+ }
+};
+
+// fetch the state of every collection applicable to the selected items
+const OrganizeCollectionState = {
+ NORMAL: 0,
+ ACTIVE: 1 << 0,
+ INCONSISTENT: 1 << 1,
+ INSENSITIVE: 1 << 2
+};
+
+function FetchCollectionStateForSelectionJob() {
+ this._init();
+}
+
+FetchCollectionStateForSelectionJob.prototype = {
+ _init: function() {
+ this._collectionsForItems = {};
+ this._runningJobs = 0;
+ },
+
+ run: function(callback) {
+ this._callback = callback;
+
+ let urns = Global.selectionController.getSelection();
+ urns.forEach(Lang.bind(this,
+ function(urn) {
+ let job = new FetchCollectionsJob(urn);
+
+ this._runningJobs++;
+ job.run(Lang.bind(this, this._jobCollector, urn));
+ }));
+ },
+
+ _jobCollector: function(collectionsForItem, urn) {
+ this._collectionsForItems[urn] = collectionsForItem;
+
+ this._runningJobs--;
+ if (!this._runningJobs)
+ this._emitCallback();
+ },
+
+ _emitCallback: function() {
+ let collectionState = {};
+ let collections = Global.collectionManager.getItems();
+
+ // for all the registered collections...
+ for (collIdx in collections) {
+ let collection = collections[collIdx];
+
+ let found = false;
+ let notFound = false;
+ let sameResource = true;
+
+ for (itemIdx in this._collectionsForItems) {
+ let item = Global.documentManager.getItemById(itemIdx);
+ let collectionsForItem = this._collectionsForItems[itemIdx];
+
+ // if one of the selected items is part of this collection...
+ if (collectionsForItem.indexOf(collIdx) != -1)
+ found = true;
+ else
+ notFound = true;
+
+ if ((item.resourceUrn != collection.resourceUrn) &&
+ (collection.identifier.indexOf(Query.LOCAL_COLLECTIONS_IDENTIFIER) == -1)) {
+ sameResource = false;
+ }
+ }
+
+ let state = OrganizeCollectionState.NORMAL;
+
+ if (found && notFound)
+ // if some items are part of this collection and some are not...
+ state |= OrganizeCollectionState.INCONSISTENT;
+ else if (found)
+ // if all items are part of this collection...
+ state |= OrganizeCollectionState.ACTIVE;
+
+ if (!sameResource)
+ state |= OrganizeCollectionState.INSENSITIVE;
+
+ collectionState[collIdx] = state;
+ }
+
+ this._callback(collectionState);
+ }
+};
+
+// updates the mtime for the given resource to the current system time
+function UpdateMtimeJob(urn) {
+ this._init(urn);
+}
+
+UpdateMtimeJob.prototype = {
+ _init: function(urn) {
+ this._urn = urn;
+ },
+
+ run: function(callback) {
+ this._callback = callback;
+
+ let query = Global.queryBuilder.buildUpdateMtimeQuery(this._urn);
+ Global.connectionQueue.update(query.sparql, null, Lang.bind(this,
+ function(object, res) {
+ try {
+ object.update_finish(res);
+ } catch (e) {
+ log(e);
+ }
+
+ this._callback();
+ }));
+ }
+};
+
+// adds or removes the selected items to the given collection
+function SetCollectionForSelectionJob(collectionUrn, setting) {
+ this._init(collectionUrn, setting);
+}
+
+SetCollectionForSelectionJob.prototype = {
+ _init: function(collectionUrn, setting) {
+ this._collectionUrn = collectionUrn;
+ this._setting = setting;
+ this._runningJobs = 0;
+ },
+
+ run: function(callback) {
+ this._callback = callback;
+
+ let urns = Global.selectionController.getSelection();
+ urns.forEach(Lang.bind(this,
+ function(urn) {
+ let query = Global.queryBuilder.buildSetCollectionQuery(urn,
+ this._collectionUrn, this._setting);
+ this._runningJobs++;
+
+ Global.connectionQueue.update(query.sparql, null, Lang.bind(this,
+ function(object, res) {
+ try {
+ object.update_finish(res);
+ } catch (e) {
+ log(e);
+ }
+
+ this._jobCollector();
+ }));
+ }));
+ },
+
+ _jobCollector: function() {
+ this._runningJobs--;
+
+ if (this._runningJobs == 0) {
+ let job = new UpdateMtimeJob(this._collectionUrn);
+ job.run(Lang.bind(this,
+ function() {
+ this._callback();
+ }));
+ }
+ }
+};
+
+// creates an (empty) collection with the given name
+function CreateCollectionJob(name) {
+ this._init(name);
+}
+
+CreateCollectionJob.prototype = {
+ _init: function(name) {
+ this._name = name;
+ this._createdUrn = null;
+ },
+
+ run: function(callback) {
+ this._callback = callback;
+
+ let query = Global.queryBuilder.buildCreateCollectionQuery(this._name);
+ Global.connectionQueue.updateBlank(query.sparql, null, Lang.bind(this,
+ function(object, res) {
+ let variant = null;
+ try {
+ variant = object.update_blank_finish(res); // variant is aaa{ss}
+ } catch (e) {
+ log(e);
+ }
+
+ variant = variant.get_child_value(0); // variant is now aa{ss}
+ variant = variant.get_child_value(0); // variant is now a{ss}
+ variant = variant.get_child_value(0); // variant is now {ss}
+
+ let key = variant.get_child_value(0).get_string()[0];
+ let val = variant.get_child_value(1).get_string()[0];
+
+ if (key == 'res')
+ this._createdUrn = val;
+
+ this._callback(this._createdUrn);
+ }));
+ }
+};
+
+const OrganizeModelColumns = {
+ ID: 0,
+ NAME: 1,
+ STATE: 2
+};
+
+function OrganizeCollectionModel() {
+ this._init();
+}
+
+OrganizeCollectionModel.prototype = {
+ _init: function() {
+ this.model = Gd.create_organize_store();
+ this._placeholderPath = 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));
+
+ // populate the model
+ let job = new FetchCollectionStateForSelectionJob();
+ job.run(Lang.bind(this, this._onFetchCollectionStateForSelection));
+ },
+
+ _clearPlaceholder: function() {
+ // remove the placeholder if it's here
+ if (this._placeholderPath) {
+ let placeholderIter = this.model.get_iter(this._placeholderPath)[1];
+
+ if (placeholderIter) {
+ this.model.remove(placeholderIter);
+ this._placeholderPath = null;
+ }
+ }
+ },
+
+ _findCollectionIter: function(item) {
+ let retval = null;
+
+ this.model.foreach(Lang.bind(this,
+ function(model, path, iter) {
+ let id = model.get_value(iter, OrganizeModelColumns.ID);
+
+ if (item.id == id) {
+ retval = iter;
+ return true;
+ }
+
+ return false;
+ }));
+
+ return retval;
+ },
+
+ _onFetchCollectionStateForSelection: function(collectionState) {
+ this._clearPlaceholder();
+
+ for (idx in collectionState) {
+ let item = Global.collectionManager.getItemById(idx);
+ let iter = null;
+
+ iter = this._findCollectionIter(item);
+ if (!iter)
+ iter = this.model.append();
+
+ if (iter)
+ Gd.organize_store_set(this.model, iter,
+ item.id, item.name, collectionState[item.id]);
+ }
+ },
+
+ _refreshState: function() {
+ let job = new FetchCollectionStateForSelectionJob();
+ job.run(Lang.bind(this, this._onFetchCollectionStateForSelection));
+ },
+
+ _onCollectionAdded: function(manager, itemAdded) {
+ this._refreshState();
+ },
+
+ _onCollectionRemoved: function(manager, itemRemoved) {
+ let iter = this._findCollectionIter(itemRemoved);
+
+ if (iter)
+ this.model.remove(iter);
+ },
+
+ refreshCollectionState: function() {
+ this._refreshState();
+ },
+
+ setPlaceholder: function(path) {
+ this._clearPlaceholder();
+ this._placeholderPath = path;
+ },
+
+ destroy: function() {
+ if (this._collAddedId != 0) {
+ Global.collectionManager.disconnect(this._collAddedId);
+ this._collAddedId = 0;
+ }
+
+ if (this._collRemovedId != 0) {
+ Global.collectionManager.disconnect(this._collRemovedId);
+ this._collRemovedId = 0;
+ }
+ }
+};
+
+function OrganizeCollectionView() {
+ this._init();
+}
+
+OrganizeCollectionView.prototype = {
+ _init: function() {
+ this._addCollectionPath = null;
+
+ this._model = new OrganizeCollectionModel();
+ this.widget = new Gtk.TreeView({ headers_visible: false,
+ vexpand: true,
+ hexpand: true });
+ this.widget.set_model(this._model.model);
+
+ this.widget.connect('destroy', Lang.bind(this,
+ function() {
+ this._model.destroy();
+ }));
+
+ this._viewCol = new Gtk.TreeViewColumn();
+ this.widget.append_column(this._viewCol);
+
+ // checkbox
+ this._rendererCheck = new Gtk.CellRendererToggle();
+ this._viewCol.pack_start(this._rendererCheck, false);
+ this._viewCol.set_cell_data_func(this._rendererCheck,
+ Lang.bind(this, this._checkCellFunc));
+ this._rendererCheck.connect('toggled', Lang.bind(this, this._onCheckToggled));
+
+ // item name
+ this._rendererText = new Gtk.CellRendererText();
+ this._viewCol.pack_start(this._rendererText, true);
+ this._viewCol.add_attribute(this._rendererText,
+ 'text', Manager.BaseModelColumns.NAME);
+ this._viewCol.set_cell_data_func(this._rendererText,
+ Lang.bind(this, this._textCellFunc));
+
+ this._rendererText.connect('edited', Lang.bind(this, this._onTextEdited));
+ this._rendererText.connect('editing-canceled', Lang.bind(this, this._onTextEditCanceled));
+ this._rendererText.connect('editing-started', Lang.bind(this, this._onTextEditStarted));
+
+ this.widget.show();
+ },
+
+ _onCheckToggled: function(renderer, pathStr) {
+ let path = Gtk.TreePath.new_from_string(pathStr);
+ let iter = this._model.model.get_iter(path)[1];
+
+ let collUrn = this._model.model.get_value(iter, OrganizeModelColumns.ID);
+ let state = this._rendererCheck.get_active();
+
+ let job = new SetCollectionForSelectionJob(collUrn, !state);
+ job.run(Lang.bind(this,
+ function() {
+ this._model.refreshCollectionState();
+
+ // FIXME: we shouldn't be this, but tracker doesn't
+ // notify us for collection changes...
+ let coll = Global.collectionManager.getItemById(collUrn);
+ coll.refresh();
+ }));
+ },
+
+ _onTextEdited: function(cell, pathStr, newText) {
+ let path = Gtk.TreePath.new_from_string(pathStr);
+ let iter = this._model.model.get_iter(path)[1];
+
+ // don't insert collections with empty names
+ if (!newText || newText == '') {
+ this._model.model.remove(iter);
+ return;
+ }
+
+ cell.editable = false;
+ let job = new CreateCollectionJob(newText);
+ job.run(Lang.bind(this, this._onCollectionCreated));
+ },
+
+ _onCollectionCreated: function(collUrn) {
+ // FIXME: we shouldn't be doing any of this, but tracker doesn't
+ // notify us for collection changes...
+
+ let job = new Documents.SingleItemJob(collUrn);
+ job.run(Query.QueryFlags.UNFILTERED, Lang.bind(this,
+ function(cursor) {
+ if (cursor)
+ Global.documentManager.addDocumentFromCursor(cursor);
+ }));
+ },
+
+ _onTextEditCanceled: function() {
+ if (this._addCollectionPath) {
+ let path = Gtk.TreePath.new_from_string(this._addCollectionPath);
+ let iter = this._model.model.get_iter(path)[1];
+
+ this._model.model.remove(iter);
+ this._addCollectionPath = null;
+ }
+ },
+
+ _onTextEditStarted: function(cell, editable, pathStr) {
+ this._addCollectionPath = pathStr;
+ },
+
+ _checkCellFunc: function(col, cell, model, iter) {
+ let state = model.get_value(iter, OrganizeModelColumns.STATE);
+
+ cell.active = (state & OrganizeCollectionState.ACTIVE);
+ cell.inconsistent = (state & OrganizeCollectionState.INCONSISTENT);
+ cell.sensitive = !(state & OrganizeCollectionState.INSENSITIVE);
+ },
+
+ _textCellFunc: function(col, cell, model, iter) {
+ let state = model.get_value(iter, OrganizeModelColumns.STATE);
+ cell.sensitive = !(state & OrganizeCollectionState.INSENSITIVE);
+ },
+
+ addCollection: function() {
+ let iter = this._model.model.append();
+ let path = this._model.model.get_path(iter);
+
+ Gd.organize_store_set(this._model.model, iter,
+ 'collection-placeholder', '', OrganizeCollectionState.NORMAL);
+ this._model.setPlaceholder(path);
+
+ this._rendererText.editable = true;
+ this.widget.set_cursor_on_cell(path, this._viewCol, this._rendererText, true);
+ }
+};
+
+const OrganizeCollectionDialogResponse = {
+ ADD: 1
+};
+
+function OrganizeCollectionDialog(toplevel) {
+ this._init(toplevel);
+};
+
+OrganizeCollectionDialog.prototype = {
+ _init: function(toplevel) {
+ this.widget = new Gtk.Dialog({ transient_for: toplevel,
+ modal: true,
+ destroy_with_parent: true,
+ default_width: 400,
+ default_height: 200 });
+ this.widget.add_button('gtk-add', OrganizeCollectionDialogResponse.ADD);
+
+ this.widget.add_button('gtk-ok', Gtk.ResponseType.OK);
+ this.widget.set_default_response(Gtk.ResponseType.OK);
+
+ let contentArea = this.widget.get_content_area();
+ let collView = new OrganizeCollectionView();
+ contentArea.add(collView.widget);
+
+ this.widget.connect('response', Lang.bind(this,
+ function(widget, response) {
+ if (response == OrganizeCollectionDialogResponse.ADD)
+ collView.addCollection();
+ }));
+
+ this.widget.show();
+ }
+};
+
function SelectionController() {
this._init();
};
@@ -95,14 +634,20 @@ SelectionToolbar.prototype = {
actorWidget.get_style_context().add_class('osd');
this._toolbarFavorite = new Gtk.ToggleToolButton({ icon_name: 'emblem-favorite-symbolic' });
- this.widget.insert(this._toolbarFavorite, 0);
+ this.widget.insert(this._toolbarFavorite, -1);
this._toolbarFavorite.connect('clicked', Lang.bind(this, this._onToolbarFavorite));
this._separator = new Gtk.SeparatorToolItem();
- this.widget.insert(this._separator, 1);
+ this.widget.insert(this._separator, -1);
+
+ this._toolbarCollection = new Gtk.ToolButton({ icon_name: 'list-add-symbolic' });
+ this._toolbarCollection.set_tooltip_text(_("Organize"));
+ this.widget.insert(this._toolbarCollection, -1);
+ this._toolbarCollection.connect('clicked', Lang.bind(this, this._onToolbarCollection));
+ this._toolbarCollection.show();
this._toolbarOpen = new Gtk.ToolButton({ icon_name: 'document-open-symbolic' });
- this.widget.insert(this._toolbarOpen, 2);
+ this.widget.insert(this._toolbarOpen, -1);
this._toolbarOpen.connect('clicked', Lang.bind(this, this._onToolbarOpen));
this.widget.show();
@@ -165,7 +710,8 @@ SelectionToolbar.prototype = {
if (doc.favorite)
favCount++;
- if (apps.indexOf(doc.defaultAppName) == -1)
+ if ((doc.defaultAppName) &&
+ (apps.indexOf(doc.defaultAppName) == -1))
apps.push(doc.defaultAppName);
}));
@@ -183,8 +729,10 @@ SelectionToolbar.prototype = {
openLabel = _("Open");
}
- this._toolbarOpen.set_tooltip_text(openLabel);
- this._toolbarOpen.show();
+ if (apps.length > 0) {
+ this._toolbarOpen.set_tooltip_text(openLabel);
+ this._toolbarOpen.show();
+ }
if (showFavorite) {
let isFavorite = (favCount == selection.length);
@@ -210,6 +758,23 @@ SelectionToolbar.prototype = {
this._insideRefresh = false;
},
+ _onToolbarCollection: function() {
+ let toplevel = this.widget.get_toplevel();
+ if (!toplevel.is_toplevel())
+ return;
+
+ let dialog = new OrganizeCollectionDialog(toplevel);
+ this._fadeOut();
+
+ dialog.widget.connect('response', Lang.bind(this,
+ function(widget, response) {
+ if (response == Gtk.ResponseType.OK) {
+ dialog.widget.destroy();
+ this._fadeIn();
+ }
+ }));
+ },
+
_onToolbarOpen: function(widget) {
let selection = Global.selectionController.getSelection();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]