/* -*- 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; // 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 this.timestamp = recentInfo.get_modified().getTime() / 1000; this.name = recentInfo.get_display_name(); this.uri = recentInfo.get_uri(); this.mimeType = recentInfo.get_mime_type(); }, createIcon : function(size) { return Shell.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo); }, 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 " + 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 // '%' 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(); } }; var docManagerInstance = null; function getDocManager() { if (docManagerInstance == null) docManagerInstance = new DocManager(); return docManagerInstance; } function DocManager() { this._init(); } DocManager.prototype = { _init: function() { 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]; if (!recentInfo.exists()) continue; 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] = 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() { return this._items; } } Signals.addSignalMethods(DocManager.prototype);