[sushi] fallbackRenderer: load file information from JS



commit 68103b79fa43f6e9063a2661e43784b5e4c7d42a
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Mon Jun 17 23:59:41 2019 -0700

    fallbackRenderer: load file information from JS
    
    Instead of requiring a GObject class for this.

 src/libsushi/meson.build         |   2 -
 src/libsushi/sushi-file-loader.c | 698 ---------------------------------------
 src/libsushi/sushi-file-loader.h |  74 -----
 src/ui/fallbackRenderer.js       | 231 +++++++++++--
 4 files changed, 196 insertions(+), 809 deletions(-)
---
diff --git a/src/libsushi/meson.build b/src/libsushi/meson.build
index 1bd7e65..b9d9d97 100644
--- a/src/libsushi/meson.build
+++ b/src/libsushi/meson.build
@@ -1,6 +1,5 @@
 libsushi_c = [
   'sushi-cover-art.c',
-  'sushi-file-loader.c',
   'sushi-font-loader.c',
   'sushi-font-widget.c',
   'sushi-media-bin.c',
@@ -10,7 +9,6 @@ libsushi_c = [
 
 libsushi_headers = [
   'sushi-cover-art.h',
-  'sushi-file-loader.h',
   'sushi-font-loader.h',
   'sushi-font-widget.h',
   'sushi-media-bin.h',
diff --git a/src/ui/fallbackRenderer.js b/src/ui/fallbackRenderer.js
index 3c342d2..bedca5d 100644
--- a/src/ui/fallbackRenderer.js
+++ b/src/ui/fallbackRenderer.js
@@ -23,10 +23,167 @@
  *
  */
 
-const {Gio, GObject, Gtk, Pango, Sushi} = imports.gi;
+const {Gio, GLib, GObject, Gtk, Pango} = imports.gi;
+const Gettext = imports.gettext;
 
 const Renderer = imports.ui.renderer;
 
+function _getDeepCountAttrs() {
+    return [
+        Gio.FILE_ATTRIBUTE_STANDARD_SIZE,
+        Gio.FILE_ATTRIBUTE_STANDARD_TYPE,
+        Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+        Gio.FILE_ATTRIBUTE_STANDARD_NAME,
+        Gio.FILE_ATTRIBUTE_UNIX_INODE
+    ].join(',');
+}
+
+function _getLoaderAttrs() {
+    return [
+        Gio.FILE_ATTRIBUTE_STANDARD_ICON,
+        Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+        Gio.FILE_ATTRIBUTE_STANDARD_SIZE,
+        Gio.FILE_ATTRIBUTE_STANDARD_TYPE,
+        Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+        Gio.FILE_ATTRIBUTE_TIME_MODIFIED
+    ].join(',');
+}
+
+const loadFile = function(_fileToLoad, _cancellable, _updateCallback) {
+    let _seenInodes = new Set();
+    let _subDirectories = [];
+    let _enumerator = null;
+    let _file = null;
+
+    let _state = { fileInfo: null,
+                   directoryItems: 0,
+                   fileItems: 0,
+                   loading: true,
+                   totalItems: 0,
+                   totalSize: 0,
+                   unreadableItems: 0 }
+    let _timeoutId = 0;
+
+    function _cleanup() {
+        if (_enumerator && !_enumerator.is_closed())
+            _enumerator.close_async(0, null, null);
+    }
+
+    function _deepCountLoad() {
+        _file.enumerate_children_async(
+            _getDeepCountAttrs(), Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
+            GLib.PRIORITY_LOW, _cancellable,
+            (f, res) => {
+                try {
+                    _enumerator = _file.enumerate_children_finish(res);
+                } catch(e) {
+                    _state.unreadableItems++;
+                    _deepCountNext();
+                    return;
+                }
+
+                _enumerator.next_files_async(
+                    100, GLib.PRIORITY_LOW, _cancellable,
+                    _deepCountMoreFiles);
+            });
+    }
+
+    function _deepCountMoreFiles(en, res) {
+        let files = [];
+        try {
+            files = _enumerator.next_files_finish(res);
+        } catch (e) {
+            if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                return;
+        }
+
+        files.forEach(_deepCountOne);
+
+        if (files.length) {
+            _enumerator.next_files_async(
+                100, GLib.PRIORITY_LOW, _cancellable,
+                _deepCountMoreFiles);
+        } else {
+            _cleanup();
+            _deepCountNext();
+        }
+    }
+
+    function _deepCountNext() {
+        _file = _subDirectories.shift();
+        if (_file) {
+            _deepCountLoad();
+        } else {
+            _cleanup();
+            _state.loading = false;
+        }
+
+        _queueUpdate();
+    }
+
+    function _deepCountOne(info) {
+        let inode = info.get_attribute_uint64(Gio.FILE_ATTRIBUTE_UNIX_INODE);
+        let isSeen = false;
+        if (inode) {
+            isSeen = _seenInodes.has(inode);
+            if (!isSeen)
+                _seenInodes.add(inode);
+        }
+
+        let fileType = info.get_file_type();
+        if (fileType == Gio.FileType.DIRECTORY) {
+            _state.directoryItems++;
+            _subDirectories.unshift(_file.get_child(info.get_name()));
+        } else {
+            _state.fileItems++;
+        }
+
+        if (!isSeen && info.has_attribute(Gio.FILE_ATTRIBUTE_STANDARD_SIZE))
+            _state.totalSize += info.get_size();
+    }
+
+    function _queueUpdate() {
+        if (_timeoutId != 0)
+            return;
+
+        _timeoutId = GLib.timeout_add(0, 300, () => {
+            _timeoutId = 0;
+            _sendUpdate();
+            return false;
+        });
+    }
+
+    function _unqueueUpdate() {
+        if (_timeoutId != 0)
+            GLib.source_remove(_timeoutId);
+    }
+
+    function _sendUpdate() {
+        _updateCallback(_state);
+    }
+
+    _cancellable.connect(_unqueueUpdate);
+
+    _file = _fileToLoad;
+    _file.query_info_async(_getLoaderAttrs(), 0, 0, _cancellable, (f, res) => {
+        try {
+            _state.fileInfo = _file.query_info_finish(res);
+        } catch (e) {
+            if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                logError(e, `Unable to query info for file ${_file.get_uri()}`);
+            return;
+        }
+
+        let fileType = _state.fileInfo.get_file_type();
+        if (fileType == Gio.FileType.DIRECTORY)
+            _deepCountLoad();
+        else
+            _state.loading = false;
+
+        _sendUpdate();
+    });
+};
+
 var FallbackRenderer = GObject.registerClass({
     Implements: [Renderer.Renderer],
     Properties: {
@@ -42,9 +199,8 @@ var FallbackRenderer = GObject.registerClass({
         super._init({ orientation: Gtk.Orientation.HORIZONTAL,
                       spacing: 6 });
 
-        this._fileLoader = new Sushi.FileLoader();
-        this._fileLoader.file = file;
-        this._fileLoaderId = this._fileLoader.connect('notify', this._onFileInfoChanged.bind(this));
+        this._cancellable = new Gio.Cancellable();
+        loadFile(file, this._cancellable, this._onFileInfoUpdated.bind(this));
 
         this._image = new Gtk.Image();
         this.pack_start(this._image, false, false, 0);
@@ -69,6 +225,7 @@ var FallbackRenderer = GObject.registerClass({
         this._spinner = new Gtk.Spinner();
         hbox.pack_start(this._spinner, false, false, 0);
         this._spinner.start();
+        this._spinner.show();
 
         this._typeLabel = new Gtk.Label();
         this._typeLabel.set_halign(Gtk.Align.START);
@@ -82,42 +239,54 @@ var FallbackRenderer = GObject.registerClass({
         this._dateLabel.set_halign(Gtk.Align.START);
         vbox.pack_start(this._dateLabel, false, false, 0);
 
-        this._applyLabels();
-
         this.connect('destroy', this._onDestroy.bind(this));
         this.isReady();
     }
 
-    _applyLabels() {
-        let titleStr =
-            '<b><big>' +
-            ((this._fileLoader.name) ? (this._fileLoader.name) : (this._fileLoader.file.get_basename()))
-            + '</big></b>';
+    _applyLabels(state) {
+        let titleStr = `<b><big>${state.fileInfo.get_display_name()}</big></b>`;
         this._titleLabel.set_markup(titleStr);
 
-        if (this._fileLoader.get_file_type() != Gio.FileType.DIRECTORY) {
-            let typeStr =
-                '<small><b>' + _("Type") + '  </b>' +
-                ((this._fileLoader.contentType) ? (this._fileLoader.contentType) : (_("Loading…")))
-                + '</small>';
+        if (state.fileInfo.get_file_type() != Gio.FileType.DIRECTORY) {
+            let contentType = state.fileInfo.get_content_type();
+            let typeDescr = Gio.content_type_get_description(contentType);
+            let typeStr = '<small><b>' + _("Type") + '  </b>' +
+                `${typeDescr}</small>`;
             this._typeLabel.set_markup(typeStr);
         } else {
             this._typeLabel.hide();
         }
 
-        let sizeStr =
-            '<small><b>' + _("Size") + '  </b>' +
-            ((this._fileLoader.size) ? (this._fileLoader.size) : (_("Loading…")))
-             + '</small>';
+        let sizeFormatted;
+        if (state.fileInfo.get_file_type() != Gio.FileType.DIRECTORY) {
+            sizeFormatted = GLib.format_size(state.fileInfo.get_size());
+        } else if (state.totalSize > 0) {
+            let itemsStr = Gettext.ngettext(
+                "%d item", "%d items",
+                state.fileItems + state.directoryItems).
+                format(state.fileItems + state.directoryItems);
+            sizeFormatted = `${GLib.format_size(state.totalSize)}, ${itemsStr}`;
+        } else {
+            sizeFormatted = _("Empty Folder");
+        }
+
+        let sizeStr = '<small><b>' + _("Size") + '  </b>' +
+            `${sizeFormatted}</small>`;
         this._sizeLabel.set_markup(sizeStr);
 
+        let date = GLib.DateTime.new_from_timeval_local(state.fileInfo.get_modification_time());
+        let dateFormatted = date.format('%x %X');
         let dateStr =
             '<small><b>' + _("Modified") + '  </b>' +
-             ((this._fileLoader.time) ? (this._fileLoader.time) : (_("Loading…")))
-             + '</small>';
+            `${dateFormatted}</small>`;
         this._dateLabel.set_markup(dateStr);
     }
 
+    _applyIcon(state) {
+        let icon = state.fileInfo.get_icon();
+        this._updateIcon(icon);
+    }
+
     _updateIcon(icon) {
         let iconTheme = Gtk.IconTheme.get_default();
         let iconInfo = iconTheme.lookup_by_gicon_for_scale(icon, 256,
@@ -133,26 +302,18 @@ var FallbackRenderer = GObject.registerClass({
         }
     }
 
-    _onFileInfoChanged() {
-        if (!this._fileLoader.get_loading()) {
+    _onFileInfoUpdated(state) {
+        if (!state.loading) {
             this._spinner.stop();
             this._spinner.hide();
         }
 
-        if (this._fileLoader.icon)
-            this._updateIcon(this._fileLoader.icon);
-
-        this._applyLabels();
+        this._applyIcon(state);
+        this._applyLabels(state);
     }
 
     _onDestroy() {
-        if (this._fileLoader) {
-            this._fileLoader.disconnect(this._fileLoaderId);
-            this._fileLoaderId = 0;
-
-            this._fileLoader.stop();
-            this._fileLoader = null;
-        }
+        this._cancellable.cancel();
     }
 
     get resizable() {


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