[geary/bug/728002-webkit2: 84/140] Re-enable inserting inline images in the composer.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/bug/728002-webkit2: 84/140] Re-enable inserting inline images in the composer.
- Date: Tue, 31 Jan 2017 23:05:36 +0000 (UTC)
commit 58588aa580845a6e93bedf967eee549609f534ee
Author: Michael James Gratton <mike vee net>
Date: Sat Jan 14 22:34:48 2017 +1100
Re-enable inserting inline images in the composer.
* src/client/components/client-web-view.vala (ClientWebView): Remove
now-unsed ;;allow_prefix property. Add ::insert_image method.
* src/client/composer/composer-widget.vala (ComposerWidget): Split up
"attachment" handling into inline and attached parts so we can require
a Content ID be passed in for inline parts. Convert ::inline_files set
into a map, so we can be explict about the IMG SRC URL that was used to
refer to the inline part, either cid or geary, and update call sites.
(ComposerWidget::on_insert_image): Simply add the image as a new inline
part with a geary URL, and insert it via the editor.
* src/engine/api/geary-composed-email.vala (ComposedEmail): Also use a
map of inline parts, for the same reason as ComposerWidget. Update call
sites.
* src/engine/rfc822/rfc822-message.vala (Message.from_composed_email):
Use the given URL for inline parts when assigning Content IDs and
replacing the internal URL in the source.
src/client/components/client-web-view.vala | 4 +-
src/client/composer/composer-web-view.vala | 13 +++
src/client/composer/composer-widget.vala | 114 ++++++++++++++++------------
src/engine/api/geary-composed-email.vala | 7 +-
src/engine/rfc822/rfc822-message.vala | 14 ++--
5 files changed, 89 insertions(+), 63 deletions(-)
---
diff --git a/src/client/components/client-web-view.vala b/src/client/components/client-web-view.vala
index 08c7e5e..583ccfe 100644
--- a/src/client/components/client-web-view.vala
+++ b/src/client/components/client-web-view.vala
@@ -146,9 +146,9 @@ public class ClientWebView : WebKit.WebView {
}
- public bool has_valid_height { get; set; default = false; }
- public string allow_prefix { get; private set; default = ""; }
+ /** Determines if the view has started rendering the HTML */
+ public bool has_valid_height { get; set; default = false; }
private string _document_font;
public string document_font {
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index 08a0dc0..75fa47c 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -282,6 +282,19 @@ public class ComposerWebView : ClientWebView {
}
/**
+ * Inserts an IMG with the given `src` at the current cursor location.
+ */
+ public void insert_image(string src) {
+ // Use insertHTML instead of insertImage here so
+ // we can specify a max width inline, preventing
+ // large images from overflowing the view port.
+ execute_editing_command_with_argument(
+ "insertHTML",
+ @"<img style=\"max-width: 100%\" src=\"$src\">"
+ );
+ }
+
+ /**
* Returns the editor content as an HTML string.
*/
public async string? get_html() throws Error {
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 4b8c627..fc36c3f 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -339,8 +339,7 @@ public class ComposerWidget : Gtk.EventBox {
private AttachPending pending_include = AttachPending.INLINE_ONLY;
private Gee.Set<File> attached_files = new Gee.HashSet<File>(Geary.Files.nullable_hash,
Geary.Files.nullable_equal);
- private Gee.Set<File> inline_files = new Gee.HashSet<File>(Geary.Files.nullable_hash,
- Geary.Files.nullable_equal);
+ private Gee.Map<string,File> inline_files = new Gee.HashMap<string,File>();
private Gee.Map<string,File> cid_files = new Gee.HashMap<string,File>();
private Geary.App.DraftManager? draft_manager = null;
@@ -560,7 +559,7 @@ public class ComposerWidget : Gtk.EventBox {
attachments.add_all(headers.get("attachment"));
foreach (string attachment in attachments) {
try {
- add_attachment(File.new_for_commandline_arg(attachment));
+ add_attachment_part(File.new_for_commandline_arg(attachment));
} catch (Error err) {
attachment_failed(err.message);
}
@@ -874,7 +873,7 @@ public class ComposerWidget : Gtk.EventBox {
continue;
try {
- add_attachment(File.new_for_uri(uri.strip()));
+ add_attachment_part(File.new_for_uri(uri.strip()));
} catch (Error err) {
attachment_failed(err.message);
}
@@ -926,10 +925,10 @@ public class ComposerWidget : Gtk.EventBox {
email.subject = this.subject;
email.attached_files.add_all(this.attached_files);
- email.inline_files.add_all(this.inline_files);
+ email.inline_files.set_all(this.inline_files);
email.cid_files.set_all(this.cid_files);
- email.img_src_prefix = this.editor.allow_prefix;
+ email.img_src_prefix = ClientWebView.INTERNAL_URL_PREFIX;
try {
if (this.editor.is_rich_text || only_html)
@@ -1456,8 +1455,12 @@ public class ComposerWidget : Gtk.EventBox {
// hasn't already been added
if (do_add &&
!(file in this.attached_files) &&
- !(file in this.inline_files)) {
- add_attachment(file, type);
+ !(content_id in this.inline_files)) {
+ if (type == Geary.Mime.DispositionType.INLINE) {
+ add_inline_part(file, content_id);
+ } else {
+ add_attachment_part(file);
+ }
}
} else {
// The pending attachment should only be added
@@ -1472,8 +1475,50 @@ public class ComposerWidget : Gtk.EventBox {
this.header.show_pending_attachments = manual_enabled;
}
- private void add_attachment(File target,
- Geary.Mime.DispositionType? disposition = null)
+ private void add_attachment_part(File target)
+ throws AttachmentError {
+ FileInfo target_info = check_attachment_file(target);
+
+ if (!this.attached_files.add(target)) {
+ throw new AttachmentError.DUPLICATE(
+ _("“%s” already attached for delivery.").printf(target.get_path())
+ );
+ }
+
+ Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
+ this.attachments_box.pack_start(box);
+
+ /// In the composer, the filename followed by its filesize, i.e. "notes.txt (1.12KB)"
+ string label_text = _("%s (%s)").printf(target.get_basename(),
+ Files.get_filesize_as_string(target_info.get_size()));
+ Gtk.Label label = new Gtk.Label(label_text);
+ box.pack_start(label);
+ label.halign = Gtk.Align.START;
+ label.margin_start = 4;
+ label.margin_end = 4;
+
+ Gtk.Button remove_button = new Gtk.Button.with_mnemonic(Stock._REMOVE);
+ box.pack_start(remove_button, false, false);
+ remove_button.clicked.connect(() => remove_attachment(target, box));
+
+ show_attachments();
+ }
+
+ private void add_inline_part(File target, string content_id)
+ throws AttachmentError {
+ check_attachment_file(target);
+ this.inline_files[content_id] = target;
+ try {
+ this.editor.add_internal_resource(
+ content_id, new Geary.Memory.FileBuffer(target, true)
+ );
+ } catch (Error err) {
+ // unlikely
+ debug("Failed to re-open file for attachment: %s", err.message);
+ }
+ }
+
+ private FileInfo check_attachment_file(File target)
throws AttachmentError {
FileInfo target_info;
try {
@@ -1484,7 +1529,7 @@ public class ComposerWidget : Gtk.EventBox {
_("“%s” could not be found.").printf(target.get_path())
);
}
-
+
if (target_info.get_file_type() == FileType.DIRECTORY) {
throw new AttachmentError.FILE(
_("“%s” is a folder.").printf(target.get_path())
@@ -1496,7 +1541,7 @@ public class ComposerWidget : Gtk.EventBox {
_("“%s” is an empty file.").printf(target.get_path())
);
}
-
+
try {
FileInputStream? stream = target.read();
if (stream != null)
@@ -1504,39 +1549,13 @@ public class ComposerWidget : Gtk.EventBox {
} catch(Error e) {
debug("File '%s' could not be opened for reading. Error: %s", target.get_path(),
e.message);
-
+
throw new AttachmentError.FILE(
_("“%s” could not be opened for reading.").printf(target.get_path())
);
}
- if (disposition != Geary.Mime.DispositionType.INLINE) {
- if (!this.attached_files.add(target)) {
- throw new AttachmentError.DUPLICATE(
- _("“%s” already attached for delivery.").printf(target.get_path())
- );
- }
-
- Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
- this.attachments_box.pack_start(box);
-
- /// In the composer, the filename followed by its filesize, i.e. "notes.txt (1.12KB)"
- string label_text = _("%s (%s)").printf(target.get_basename(),
- Files.get_filesize_as_string(target_info.get_size()));
- Gtk.Label label = new Gtk.Label(label_text);
- box.pack_start(label);
- label.halign = Gtk.Align.START;
- label.margin_start = 4;
- label.margin_end = 4;
-
- Gtk.Button remove_button = new Gtk.Button.with_mnemonic(Stock._REMOVE);
- box.pack_start(remove_button, false, false);
- remove_button.clicked.connect(() => remove_attachment(target, box));
-
- show_attachments();
- } else {
- this.inline_files.add(target);
- }
+ return target_info;
}
private void attachment_failed(string msg) {
@@ -2224,7 +2243,7 @@ public class ComposerWidget : Gtk.EventBox {
dialog.hide();
foreach (File file in dialog.get_files()) {
try {
- add_attachment(file, Geary.Mime.DispositionType.ATTACHMENT);
+ add_attachment_part(file);
} catch (Error err) {
attachment_failed(err.message);
break;
@@ -2250,15 +2269,10 @@ public class ComposerWidget : Gtk.EventBox {
dialog.hide();
foreach (File file in dialog.get_files()) {
try {
- add_attachment(file, Geary.Mime.DispositionType.INLINE);
- // Use insertHTML instead of insertImage here so
- // we can specify a max width inline, preventing
- // large images from overflowing the view port.
- this.editor.execute_editing_command_with_argument(
- "insertHTML",
- "<img style=\"max-width: 100%\" src=\"%s\">".printf(
- this.editor.allow_prefix + file.get_uri()
- )
+ string path = file.get_path();
+ add_inline_part(file, path);
+ this.editor.insert_image(
+ ClientWebView.INTERNAL_URL_PREFIX + path
);
} catch (Error err) {
attachment_failed(err.message);
diff --git a/src/engine/api/geary-composed-email.vala b/src/engine/api/geary-composed-email.vala
index de49c33..034934e 100644
--- a/src/engine/api/geary-composed-email.vala
+++ b/src/engine/api/geary-composed-email.vala
@@ -40,9 +40,10 @@ public class Geary.ComposedEmail : BaseObject {
public Gee.Set<File> attached_files { get; private set;
default = new Gee.HashSet<File>(Geary.Files.nullable_hash, Geary.Files.nullable_equal); }
- public Gee.Set<File> inline_files { get; private set;
- default = new Gee.HashSet<File>(Geary.Files.nullable_hash, Geary.Files.nullable_equal); }
- public Gee.Map<string,File> cid_files = new Gee.HashMap<string,File>();
+ public Gee.Map<string,File> inline_files { get; private set;
+ default = new Gee.HashMap<string,File>(); }
+ public Gee.Map<string,File> cid_files { get; private set;
+ default = new Gee.HashMap<string,File>(); }
public string img_src_prefix { get; set; default = ""; }
diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index 4a18b6a..584b199 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -176,10 +176,8 @@ public class Geary.RFC822.Message : BaseObject {
new Gee.LinkedList<GMime.Object>();
// The files that need to have Content IDs assigned
- Gee.Set<File> inline_files = new Gee.HashSet<File>(
- Geary.Files.nullable_hash, Geary.Files.nullable_equal
- );
- inline_files.add_all(email.inline_files);
+ Gee.Map<string,File> inline_files = new Gee.HashMap<string,File>();
+ inline_files.set_all(email.inline_files);
// Create parts for inline images, if any, and updating
// the IMG SRC attributes as we go. An inline file is only
@@ -200,7 +198,7 @@ public class Geary.RFC822.Message : BaseObject {
}
// Don't need to assign a CID to this file, so
// don't process it below any further.
- inline_files.remove(file);
+ inline_files.remove(cid);
}
}
@@ -209,16 +207,16 @@ public class Geary.RFC822.Message : BaseObject {
if (!inline_files.is_empty) {
const string CID_TEMPLATE = "inline_%02u@geary";
uint cid_index = 0;
- foreach (File file in inline_files) {
+ foreach (string name in inline_files.keys) {
string cid = "";
do {
cid = CID_TEMPLATE.printf(cid_index++);
} while (cid in email.cid_files);
- if (email.replace_inline_img_src(file.get_uri(),
+ if (email.replace_inline_img_src(name,
CID_URL_PREFIX + cid)) {
GMime.Object? inline_part = get_file_part(
- file, Geary.Mime.DispositionType.INLINE
+ inline_files[name], Geary.Mime.DispositionType.INLINE
);
if (inline_part != null) {
inline_part.set_content_id(cid);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]