Rebase recent documents on top of ShellDocSystem
Rather of calling .exists() synchronously for all documents, use ShellDocSystem's async API to only stat docs we're showing. Use the doc opening functionality in ShellDocSystem. Also, more intelligently do redisplay(); don't recreate actors for recent docs if we already have one for that URI. We may need more intelligent caching here; if e.g. just the associated application changes, we should probably reread the info. https://bugzilla.gnome.org/show_bug.cgi?id=603522
This commit is contained in:
parent
a442dfea14
commit
949f67469c
@ -32,50 +32,7 @@ DocInfo.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
launch : function() {
|
launch : function() {
|
||||||
// While using Gio.app_info_launch_default_for_uri() would be
|
Shell.DocSystem.get_default().open(this.recentInfo);
|
||||||
// shorter in terms of lines of code, we are not doing so
|
|
||||||
// because that would duplicate the work of retrieving the
|
|
||||||
// mime type.
|
|
||||||
let needsUri = Gio.file_new_for_uri(this.uri).get_path() == null;
|
|
||||||
let appInfo = Gio.app_info_get_default_for_type(this.mimeType, needsUri);
|
|
||||||
|
|
||||||
if (appInfo != null) {
|
|
||||||
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
|
|
||||||
} else {
|
|
||||||
log("Failed to get default application info for mime type " + this.mimeType +
|
|
||||||
". Will try to use the last application that registered the document.");
|
|
||||||
let appName = this.recentInfo.last_application();
|
|
||||||
let [success, appExec, count, time] = this.recentInfo.get_application_info(appName);
|
|
||||||
if (success) {
|
|
||||||
log("Will open a document with the following command: " + appExec);
|
|
||||||
// TODO: Change this once better support for creating
|
|
||||||
// GAppInfo is added to GtkRecentInfo, as right now
|
|
||||||
// this relies on the fact that the file uri is
|
|
||||||
// already a part of appExec, so we don't supply any
|
|
||||||
// files to appInfo.launch().
|
|
||||||
|
|
||||||
// The 'command line' passed to
|
|
||||||
// create_from_command_line is allowed to contain
|
|
||||||
// '%<something>' macros that are expanded to file
|
|
||||||
// name / icon name, etc, so we need to escape % as %%
|
|
||||||
appExec = appExec.replace(/%/g, "%%");
|
|
||||||
|
|
||||||
let appInfo = Gio.app_info_create_from_commandline(appExec, null, 0, null);
|
|
||||||
|
|
||||||
// The point of passing an app launch context to
|
|
||||||
// launch() is mostly to get startup notification and
|
|
||||||
// associated benefits like the app appearing on the
|
|
||||||
// right desktop; but it doesn't really work for now
|
|
||||||
// because with the way we create the appInfo we
|
|
||||||
// aren't reading the application's desktop file, and
|
|
||||||
// thus don't find the StartupNotify=true in it. So,
|
|
||||||
// despite passing the app launch context, no startup
|
|
||||||
// notification occurs.
|
|
||||||
appInfo.launch([], Main.createAppLaunchContext());
|
|
||||||
} else {
|
|
||||||
log("Failed to get application info for " + this.uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
exists : function() {
|
exists : function() {
|
||||||
@ -91,50 +48,51 @@ function getDocManager() {
|
|||||||
return docManagerInstance;
|
return docManagerInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DocManager wraps the DocSystem, primarily to expose DocInfo objects
|
||||||
|
* which conform to the GenericDisplay item API.
|
||||||
|
*/
|
||||||
function DocManager() {
|
function DocManager() {
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
DocManager.prototype = {
|
DocManager.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._recentManager = Gtk.RecentManager.get_default();
|
this._docSystem = Shell.DocSystem.get_default();
|
||||||
this._items = {};
|
this._infosByTimestamp = [];
|
||||||
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
|
this._infosByUri = {};
|
||||||
this._reload();
|
this._docSystem.connect('changed', Lang.bind(this, this._reload));
|
||||||
this.emit('changed');
|
|
||||||
}));
|
|
||||||
this._reload();
|
this._reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
_reload: function() {
|
_reload: function() {
|
||||||
let docs = this._recentManager.get_items();
|
let docs = this._docSystem.get_all();
|
||||||
let newItems = {};
|
this._infosByTimestamp = [];
|
||||||
|
this._infosByUri = {};
|
||||||
for (let i = 0; i < docs.length; i++) {
|
for (let i = 0; i < docs.length; i++) {
|
||||||
let recentInfo = docs[i];
|
let recentInfo = docs[i];
|
||||||
if (!recentInfo.exists())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let docInfo = new DocInfo(recentInfo);
|
let docInfo = new DocInfo(recentInfo);
|
||||||
|
this._infosByTimestamp.push(docInfo);
|
||||||
// we use GtkRecentInfo URI as an item Id
|
this._infosByUri[docInfo.uri] = docInfo;
|
||||||
newItems[docInfo.uri] = docInfo;
|
|
||||||
}
|
}
|
||||||
let deleted = {};
|
this.emit('changed');
|
||||||
for (var uri in this._items) {
|
|
||||||
if (!(uri in newItems))
|
|
||||||
deleted[uri] = this._items[uri];
|
|
||||||
}
|
|
||||||
/* If we'd cached any thumbnail references that no longer exist,
|
|
||||||
dump them here */
|
|
||||||
let texCache = Shell.TextureCache.get_default();
|
|
||||||
for (var uri in deleted) {
|
|
||||||
texCache.evict_recent_thumbnail(this._items[uri].recentInfo);
|
|
||||||
}
|
|
||||||
this._items = newItems;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getItems: function() {
|
getTimestampOrderedInfos: function() {
|
||||||
return this._items;
|
return this._infosByTimestamp;
|
||||||
|
},
|
||||||
|
|
||||||
|
getInfosByUri: function() {
|
||||||
|
return this._infosByUri;
|
||||||
|
},
|
||||||
|
|
||||||
|
lookupByUri: function(uri) {
|
||||||
|
return this._infosByUri[uri];
|
||||||
|
},
|
||||||
|
|
||||||
|
queueExistenceCheck: function(count) {
|
||||||
|
return this._docSystem.queue_existence_check(count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,8 @@ DocDisplay.prototype = {
|
|||||||
_refreshCache : function() {
|
_refreshCache : function() {
|
||||||
if (!this._docsStale)
|
if (!this._docsStale)
|
||||||
return true;
|
return true;
|
||||||
this._allItems = this._docManager.getItems();
|
this._allItems = {};
|
||||||
|
Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
|
||||||
this._docsStale = false;
|
this._docsStale = false;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@ -275,16 +276,21 @@ DashDocDisplayItem.prototype = {
|
|||||||
Main.overview.hide();
|
Main.overview.hide();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.actor._delegate = this;
|
||||||
|
|
||||||
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
|
||||||
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
||||||
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
||||||
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
||||||
let name = new St.Label({ style_class: "dash-recent-docs-item",
|
let name = new St.Label({ style_class: 'dash-recent-docs-item',
|
||||||
text: docInfo.name });
|
text: docInfo.name });
|
||||||
this.actor.append(name, Big.BoxPackFlags.EXPAND);
|
this.actor.append(name, Big.BoxPackFlags.EXPAND);
|
||||||
|
|
||||||
let draggable = DND.makeDraggable(this.actor);
|
let draggable = DND.makeDraggable(this.actor);
|
||||||
this.actor._delegate = this;
|
},
|
||||||
|
|
||||||
|
getUri: function() {
|
||||||
|
return this._info.uri;
|
||||||
},
|
},
|
||||||
|
|
||||||
getDragActorSource: function() {
|
getDragActorSource: function() {
|
||||||
@ -316,12 +322,14 @@ DashDocDisplay.prototype = {
|
|||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
|
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||||
|
|
||||||
|
this._actorsByUri = {};
|
||||||
|
|
||||||
this._docManager = DocInfo.getDocManager();
|
this._docManager = DocInfo.getDocManager();
|
||||||
this._docManager.connect('changed', Lang.bind(this, function(mgr) {
|
this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
|
||||||
this._redisplay();
|
this._pendingDocsChange = true;
|
||||||
}));
|
this._checkDocExistence = false;
|
||||||
this._redisplay();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||||
@ -355,15 +363,17 @@ DashDocDisplay.prototype = {
|
|||||||
|
|
||||||
let firstColumnChildren = Math.ceil(children.length / 2);
|
let firstColumnChildren = Math.ceil(children.length / 2);
|
||||||
|
|
||||||
|
let natural = 0;
|
||||||
for (let i = 0; i < firstColumnChildren; i++) {
|
for (let i = 0; i < firstColumnChildren; i++) {
|
||||||
let child = children[i];
|
let child = children[i];
|
||||||
let [minSize, naturalSize] = child.get_preferred_height(forWidth);
|
let [minSize, naturalSize] = child.get_preferred_height(-1);
|
||||||
alloc.natural_size += naturalSize;
|
natural += naturalSize;
|
||||||
|
|
||||||
if (i > 0 && i < children.length - 1) {
|
if (i > 0 && i < children.length - 1) {
|
||||||
alloc.natural_size += DEFAULT_SPACING;
|
natural += DEFAULT_SPACING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
alloc.natural_size = natural;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
_allocate: function(actor, box, flags) {
|
||||||
@ -418,28 +428,49 @@ DashDocDisplay.prototype = {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything else didn't fit, just hide it.
|
if (this._checkDocExistence) {
|
||||||
for (; i < children.length; i++) {
|
// Now we know how many docs we are displaying, queue a check to see if any of them
|
||||||
children[i].hide();
|
// have been deleted. If they are deleted, then we'll get a 'changed' signal; since
|
||||||
|
// we'll now be displaying items we weren't previously, we'll check again to see
|
||||||
|
// if they were deleted, and so forth and so on.
|
||||||
|
// TODO: We should change this to ask for as many as we can fit in the given space:
|
||||||
|
// https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
|
||||||
|
this._docManager.queueExistenceCheck(i);
|
||||||
|
this._checkDocExistence = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let skipPaint = [];
|
||||||
|
for (; i < children.length; i++)
|
||||||
|
this.actor.set_skip_paint(children[i], true);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDocsChanged: function() {
|
||||||
|
this._checkDocExistence = true;
|
||||||
|
Main.queueDeferredWork(this._workId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_redisplay: function() {
|
_redisplay: function() {
|
||||||
|
// Should be kept alive by the _actorsByUri
|
||||||
this.actor.remove_all();
|
this.actor.remove_all();
|
||||||
|
let docs = this._docManager.getTimestampOrderedInfos();
|
||||||
let docs = this._docManager.getItems();
|
for (let i = 0; i < docs.length; i++) {
|
||||||
let docUrls = [];
|
let doc = docs[i];
|
||||||
for (let url in docs) {
|
let display = this._actorsByUri[doc.uri];
|
||||||
docUrls.push(url);
|
if (display) {
|
||||||
|
this.actor.add_actor(display.actor);
|
||||||
|
} else {
|
||||||
|
let display = new DashDocDisplayItem(doc);
|
||||||
|
this.actor.add_actor(display.actor);
|
||||||
|
this._actorsByUri[doc.uri] = display;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
|
// Any unparented actors must have been deleted
|
||||||
let textureCache = Shell.TextureCache.get_default();
|
for (let uri in this._actorsByUri) {
|
||||||
|
let display = this._actorsByUri[uri];
|
||||||
for (let i = 0; i < docUrls.length; i++) {
|
if (display.actor.get_parent() == null) {
|
||||||
let url = docUrls[i];
|
display.actor.destroy();
|
||||||
let docInfo = docs[url];
|
delete this._actorsByUri[uri];
|
||||||
let display = new DashDocDisplayItem(docInfo);
|
}
|
||||||
this.actor.add_actor(display.actor);
|
|
||||||
}
|
}
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
}
|
}
|
||||||
|
@ -354,8 +354,7 @@ RecentDocsWidget.prototype = {
|
|||||||
for (i = 0; i < docs.length; i++) {
|
for (i = 0; i < docs.length; i++) {
|
||||||
let docInfo = new DocInfo.DocInfo (docs[i]);
|
let docInfo = new DocInfo.DocInfo (docs[i]);
|
||||||
|
|
||||||
if (docInfo.exists())
|
items.push(docInfo);
|
||||||
items.push(docInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
|
items.sort(function (a,b) { return b.timestamp - a.timestamp; });
|
||||||
|
Loading…
Reference in New Issue
Block a user