[polari/wip/fmuellner/drop-target: 7/12] entryArea: Support pasting text/image content from files



commit e69402d961d6b60d673159c4b294f0f0b93f7456
Author: Florian Müllner <fmuellner gnome org>
Date:   Fri Feb 12 05:30:45 2016 +0100

    entryArea: Support pasting text/image content from files
    
    For users, it is not immediately obvious why they can copy file contents
    in a text/image editor and paste it into Polari for uploading, but trying
    to paste the file itself only inserts a (most likely useless) URI.
    Instead, accept file pastes and upload the contents to the appropriate
    service if possible.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=766066

 src/entryArea.js    |   64 ++++++++++++++++++++++++++++++++++++++------------
 src/pasteManager.js |   40 +++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+), 16 deletions(-)
---
diff --git a/src/entryArea.js b/src/entryArea.js
index 3fc9810..dd2d853 100644
--- a/src/entryArea.js
+++ b/src/entryArea.js
@@ -23,7 +23,8 @@ const ChatEntry = new Lang.Class({
     Extends: Gtk.Entry,
     Signals: { 'text-pasted': { param_types: [GObject.TYPE_STRING,
                                               GObject.TYPE_INT] },
-               'image-pasted': { param_types: [GdkPixbuf.Pixbuf.$gtype] } },
+               'image-pasted': { param_types: [GdkPixbuf.Pixbuf.$gtype] },
+               'file-pasted': { param_types: [Gio.File.$gtype] } },
 
     _init: function(params) {
         this.parent(params);
@@ -38,21 +39,12 @@ const ChatEntry = new Lang.Class({
         }
 
         let clipboard = Gtk.Clipboard.get_default(this.get_display());
-        clipboard.request_text(Lang.bind(this,
-            function(clipboard, text) {
-                if (text == null)
-                    return;
-               text = text.trim();
-
-                let nLines = text.split('\n').length;
-                if (nLines >= MAX_LINES) {
-                    this.emit('text-pasted', text, nLines);
-                    return;
-                }
-
-                this._useDefaultHandler = true;
-                this.emit('paste-clipboard');
-                this._useDefaultHandler = false;
+        clipboard.request_uris(Lang.bind(this,
+            function(clipboard, uris) {
+                if (uris && uris.length)
+                    this.emit('file-pasted', Gio.File.new_for_uri(uris[0]));
+                else
+                    clipboard.request_text(Lang.bind(this, this._onTextReceived));
             }));
 
         clipboard.request_image(Lang.bind(this,
@@ -62,6 +54,22 @@ const ChatEntry = new Lang.Class({
                 this.emit('image-pasted', pixbuf);
             }));
     },
+
+    _onTextReceived: function(clipboard, text) {
+        if (text == null)
+            return;
+        text = text.trim();
+
+        let nLines = text.split('\n').length;
+        if (nLines >= MAX_LINES) {
+            this.emit('text-pasted', text, nLines);
+            return;
+        }
+
+        this._useDefaultHandler = true;
+        this.emit('paste-clipboard');
+        this._useDefaultHandler = false;
+    }
 });
 
 const EntryArea = new Lang.Class({
@@ -118,6 +126,7 @@ const EntryArea = new Lang.Class({
 
         this._chatEntry.connect('text-pasted', Lang.bind(this, this._onTextPasted));
         this._chatEntry.connect('image-pasted', Lang.bind(this, this._onImagePasted));
+        this._chatEntry.connect('file-pasted', Lang.bind(this, this._onFilePasted));
         this._chatEntry.connect('changed', Lang.bind(this, this._onEntryChanged));
 
         this._chatEntry.connect('activate', Lang.bind(this,
@@ -249,6 +258,29 @@ const EntryArea = new Lang.Class({
         this._setPasteContent(pixbuf);
     },
 
+    _onFilePasted: function(entry, file) {
+        file.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                              Gio.FileQueryInfoFlags.NONE,
+                              GLib.PRIORITY_DEFAULT, null,
+                              Lang.bind(this, this._onFileInfoReady));
+    },
+
+    _onFileInfoReady: function(file, res) {
+        let fileInfo = null;
+        try {
+            fileInfo = file.query_info_finish(res);
+        } catch(e) {
+            return;
+        }
+
+        let name = fileInfo.get_display_name();
+        /* Translators: %s is a filename */
+        this._confirmLabel.label = _("Upload “%s” to public paste service?").format(name);
+        /* Translators: %s is a filename */
+        this._uploadLabel.label = _("Uploading “%s” to public paste service …").format(name);
+        this._setPasteContent(file);
+    },
+
     _onPasteClicked: function() {
         let title;
         let nick = this._room.channel.connection.self_contact.alias;
diff --git a/src/pasteManager.js b/src/pasteManager.js
index 848d0a7..2c11301 100644
--- a/src/pasteManager.js
+++ b/src/pasteManager.js
@@ -73,11 +73,51 @@ const PasteManager = new Lang.Class({
             Utils.gpaste(content, title, callback);
         } else if (content instanceof GdkPixbuf.Pixbuf) {
             Utils.imgurPaste(content, title, callback);
+        } else if (content.query_info_async) {
+            this._pasteFile(content, title, callback);
         } else {
             throw new Error('Unhandled content type');
         }
     },
 
+    _pasteFile: function(file, title, callback) {
+        file.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+                              Gio.FileQueryInfoFlags.NONE,
+                              GLib.PRIORITY_DEFAULT, null,
+                              Lang.bind(this, this._onFileQueryFinish, title, callback));
+    },
+
+    _onFileQueryFinish: function(file, res, title, callback) {
+        let fileInfo = null;
+        try {
+            fileInfo = file.query_info_finish(res);
+        } catch(e) {
+            callback(null);
+        }
+
+        let contentType = fileInfo.get_content_type();
+        let targetType = this._getTargetForContentType(contentType);
+
+        if (targetType == DndTargetType.TEXT)
+            file.load_contents_async(null, Lang.bind(this,
+                function(f, res) {
+                    let [, contents, ,] = f.load_contents_finish(res);
+                    Utils.gpaste(contents.toString(), title, callback);
+                }));
+        else if (targetType == DndTargetType.IMAGE)
+            file.read_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this,
+                function(f, res) {
+                    let stream = f.read_finish(res);
+                    GdkPixbuf.Pixbuf.new_from_stream_async(stream, null,
+                        Lang.bind(this, function(stream, res) {
+                            let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(res);
+                            Utils.imgurPaste(pixbuf, title, callback);
+                        }));
+                }));
+        else
+            callback(null);
+    },
+
     _onDragDrop: function(widget, context, x, y, time) {
         if (!Polari.drag_dest_supports_target(widget, context, null))
             return Gdk.EVENT_PROPAGATE;


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