5d067ec718
Move thumbnail creation into ShellTextureCache. It's now asynchronous, and we cache the result. Create a DocManager class which keeps around the DocInfo objects between invocations. This is also where we ensure we remove thumbnails for recent items not known anymore.
157 lines
5.3 KiB
JavaScript
157 lines
5.3 KiB
JavaScript
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
const Gio = imports.gi.Gio;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Shell = imports.gi.Shell;
|
|
|
|
const Lang = imports.lang;
|
|
const Signals = imports.signals;
|
|
const Main = imports.ui.main;
|
|
|
|
const THUMBNAIL_ICON_MARGIN = 2;
|
|
|
|
function DocInfo(recentInfo) {
|
|
this._init(recentInfo);
|
|
}
|
|
|
|
DocInfo.prototype = {
|
|
_init : function(recentInfo) {
|
|
this._recentInfo = recentInfo;
|
|
this.name = recentInfo.get_display_name();
|
|
this.uri = recentInfo.get_uri();
|
|
this.mimeType = recentInfo.get_mime_type();
|
|
},
|
|
|
|
createIcon : function(size) {
|
|
let icon = null;
|
|
let defaultPixbuf = this._recentInfo.get_icon(size);
|
|
|
|
if (this.uri.match("^file://")) {
|
|
icon = Shell.TextureCache.get_default().load_thumbnail(size, this.uri, this.mimeType,
|
|
defaultPixbuf);
|
|
}
|
|
|
|
if (!icon) {
|
|
icon = new Clutter.Texture({});
|
|
Shell.clutter_texture_set_from_pixbuf(icon, defaultPixbuf);
|
|
}
|
|
return icon;
|
|
},
|
|
|
|
launch : function() {
|
|
// While using Gio.app_info_launch_default_for_uri() would be
|
|
// shorter in terms of lines of code, we are not doing so
|
|
// because that would duplicate the work of retrieving the
|
|
// mime type.
|
|
|
|
let appInfo = Gio.app_info_get_default_for_type(this.mimeType, true);
|
|
|
|
if (appInfo != null) {
|
|
appInfo.launch_uris([this.uri], Main.createAppLaunchContext());
|
|
} else {
|
|
log("Failed to get default application info for mime type " + 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() {
|
|
return this._recentInfo.exists();
|
|
},
|
|
|
|
lastVisited : function() {
|
|
// We actually used get_modified() instead of get_visited()
|
|
// here, as GtkRecentInfo doesn't updated get_visited()
|
|
// correctly. See
|
|
// http://bugzilla.gnome.org/show_bug.cgi?id=567094
|
|
|
|
return this._recentInfo.get_modified();
|
|
}
|
|
};
|
|
|
|
var docManagerInstance = null;
|
|
|
|
function getDocManager(size) {
|
|
if (docManagerInstance == null)
|
|
docManagerInstance = new DocManager(size);
|
|
return docManagerInstance;
|
|
}
|
|
|
|
function DocManager(size) {
|
|
this._init(size);
|
|
}
|
|
|
|
DocManager.prototype = {
|
|
_init: function(iconSize) {
|
|
this._iconSize = iconSize;
|
|
this._recentManager = Gtk.RecentManager.get_default();
|
|
this._items = {};
|
|
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
|
|
this._reload();
|
|
this.emit('changed');
|
|
}));
|
|
this._reload();
|
|
},
|
|
|
|
_reload: function() {
|
|
let docs = this._recentManager.get_items();
|
|
let newItems = {};
|
|
for (let i = 0; i < docs.length; i++) {
|
|
let recentInfo = docs[i];
|
|
let docInfo = new DocInfo(recentInfo);
|
|
|
|
// we use GtkRecentInfo URI as an item Id
|
|
newItems[docInfo.uri] = docInfo;
|
|
}
|
|
let deleted = {};
|
|
for (var uri in this._items) {
|
|
if (!(uri in newItems))
|
|
deleted[uri] = 1;
|
|
}
|
|
/* 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.unref_thumbnail(this._iconSize, uri);
|
|
}
|
|
this._items = newItems;
|
|
},
|
|
|
|
getItems: function() {
|
|
return this._items;
|
|
}
|
|
}
|
|
|
|
Signals.addSignalMethods(DocManager.prototype);
|