[gnome-shell] Rebase recent documents on top of ShellDocSystem



commit 949f67469c1d0f680326d16f9ceed8897f33b349
Author: Colin Walters <walters verbum org>
Date:   Sun Nov 29 17:43:25 2009 -0500

    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

 js/misc/docInfo.js  |  100 +++++++++++++++------------------------------------
 js/ui/docDisplay.js |   83 +++++++++++++++++++++++++++++-------------
 js/ui/widget.js     |    3 +-
 3 files changed, 87 insertions(+), 99 deletions(-)
---
diff --git a/js/misc/docInfo.js b/js/misc/docInfo.js
index 8123e28..e60d7ca 100644
--- a/js/misc/docInfo.js
+++ b/js/misc/docInfo.js
@@ -32,50 +32,7 @@ DocInfo.prototype = {
     },
 
     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 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);
-            }
-        }
+        Shell.DocSystem.get_default().open(this.recentInfo);
     },
 
     exists : function() {
@@ -91,50 +48,51 @@ function getDocManager() {
     return docManagerInstance;
 }
 
+/**
+ * DocManager wraps the DocSystem, primarily to expose DocInfo objects
+ * which conform to the GenericDisplay item API.
+ */
 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._docSystem = Shell.DocSystem.get_default();
+        this._infosByTimestamp = [];
+        this._infosByUri = {};
+        this._docSystem.connect('changed', Lang.bind(this, this._reload));
         this._reload();
     },
 
     _reload: function() {
-        let docs = this._recentManager.get_items();
-        let newItems = {};
+        let docs = this._docSystem.get_all();
+        this._infosByTimestamp = [];
+        this._infosByUri = {};
         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];
+            this._infosByTimestamp.push(docInfo);
+            this._infosByUri[docInfo.uri] = docInfo;
         }
-        /* 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;
+        this.emit('changed');
+    },
+
+    getTimestampOrderedInfos: function() {
+        return this._infosByTimestamp;
+    },
+
+    getInfosByUri: function() {
+        return this._infosByUri;
+    },
+
+    lookupByUri: function(uri) {
+        return this._infosByUri[uri];
     },
 
-    getItems: function() {
-        return this._items;
+    queueExistenceCheck: function(count) {
+        return this._docSystem.queue_existence_check(count);
     }
 }
 
diff --git a/js/ui/docDisplay.js b/js/ui/docDisplay.js
index 2b67f50..6afcf4f 100644
--- a/js/ui/docDisplay.js
+++ b/js/ui/docDisplay.js
@@ -152,7 +152,8 @@ DocDisplay.prototype = {
     _refreshCache : function() {
         if (!this._docsStale)
             return true;
-        this._allItems = this._docManager.getItems();
+        this._allItems = {};
+        Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
         this._docsStale = false;
         return false;
     },
@@ -275,16 +276,21 @@ DashDocDisplayItem.prototype = {
             Main.overview.hide();
         }));
 
+        this.actor._delegate = this;
+
         this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
         let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
         iconBox.append(this._icon, 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 });
         this.actor.append(name, Big.BoxPackFlags.EXPAND);
 
         let draggable = DND.makeDraggable(this.actor);
-        this.actor._delegate = this;
+    },
+
+    getUri: function() {
+        return this._info.uri;
     },
 
     getDragActorSource: function() {
@@ -316,12 +322,14 @@ DashDocDisplay.prototype = {
         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('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.connect('changed', Lang.bind(this, function(mgr) {
-            this._redisplay();
-        }));
-        this._redisplay();
+        this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
+        this._pendingDocsChange = true;
+        this._checkDocExistence = false;
     },
 
     _getPreferredWidth: function(actor, forHeight, alloc) {
@@ -355,15 +363,17 @@ DashDocDisplay.prototype = {
 
         let firstColumnChildren = Math.ceil(children.length / 2);
 
+        let natural = 0;
         for (let i = 0; i < firstColumnChildren; i++) {
             let child = children[i];
-            let [minSize, naturalSize] = child.get_preferred_height(forWidth);
-            alloc.natural_size += naturalSize;
+            let [minSize, naturalSize] = child.get_preferred_height(-1);
+            natural += naturalSize;
 
             if (i > 0 && i < children.length - 1) {
-                alloc.natural_size += DEFAULT_SPACING;
+                natural += DEFAULT_SPACING;
             }
         }
+        alloc.natural_size = natural;
     },
 
     _allocate: function(actor, box, flags) {
@@ -418,28 +428,49 @@ DashDocDisplay.prototype = {
             i++;
         }
 
-        // Everything else didn't fit, just hide it.
-        for (; i < children.length; i++) {
-            children[i].hide();
+        if (this._checkDocExistence) {
+            // Now we know how many docs we are displaying, queue a check to see if any of them
+            // 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() {
+        // Should be kept alive by the _actorsByUri
         this.actor.remove_all();
-
-        let docs = this._docManager.getItems();
-        let docUrls = [];
-        for (let url in docs) {
-            docUrls.push(url);
+        let docs = this._docManager.getTimestampOrderedInfos();
+        for (let i = 0; i < docs.length; i++) {
+            let doc = docs[i];
+            let display = this._actorsByUri[doc.uri];
+            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; });
-        let textureCache = Shell.TextureCache.get_default();
-
-        for (let i = 0; i < docUrls.length; i++) {
-            let url = docUrls[i];
-            let docInfo = docs[url];
-            let display = new DashDocDisplayItem(docInfo);
-            this.actor.add_actor(display.actor);
+        // Any unparented actors must have been deleted
+        for (let uri in this._actorsByUri) {
+            let display = this._actorsByUri[uri];
+            if (display.actor.get_parent() == null) {
+                display.actor.destroy();
+                delete this._actorsByUri[uri];
+            }
         }
         this.emit('changed');
     }
diff --git a/js/ui/widget.js b/js/ui/widget.js
index 341833d..b7e8ca3 100644
--- a/js/ui/widget.js
+++ b/js/ui/widget.js
@@ -354,8 +354,7 @@ RecentDocsWidget.prototype = {
         for (i = 0; i < docs.length; 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; });



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]