[geary/wip/765516-gtk-widget-conversation-viewer: 87/187] Re-enable replying to and quoting the selected message, if any.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/765516-gtk-widget-conversation-viewer: 87/187] Re-enable replying to and quoting the selected message, if any.
- Date: Sat, 24 Sep 2016 16:13:23 +0000 (UTC)
commit 13420f792029cc823d9a9d8bfe34de6f7aa3844f
Author: Michael James Gratton <mike vee net>
Date: Mon Jul 4 22:31:47 2016 +1000
Re-enable replying to and quoting the selected message, if any.
src/client/application/geary-controller.vala | 16 ++++----
.../conversation-viewer/conversation-email.vala | 38 ++++++++++++++++-
.../conversation-viewer/conversation-message.vala | 42 ++++++++++++++++++++
.../conversation-viewer/conversation-viewer.vala | 34 +++++++++-------
4 files changed, 105 insertions(+), 25 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index fba0b16..6e1fb72 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -2143,15 +2143,15 @@ public class GearyController : Geary.BaseObject {
// was triggered. If null, this was triggered from the headerbar
// or shortcut.
private void create_reply_forward_widget(ComposerWidget.ComposeType compose_type,
- string? quote;
- Geary.Email? quote_message = main_window.conversation_viewer.get_selected_email(out quote);
- if (message == null)
- message = quote_message;
- if (quote_message != message)
- quote = null;
- create_compose_widget(compose_type, message, quote);
owned ConversationEmail? view) {
- Geary.Email message = (view != null) ? view.email : null;
+ if (view == null) {
+ view = main_window.conversation_viewer.get_reply_email_view();
+ }
+ string? quote = null;
+ if (view != null) {
+ quote = view.get_body_selection();
+ }
+ create_compose_widget(compose_type, view.email, quote);
}
private void create_compose_widget(ComposerWidget.ComposeType compose_type,
diff --git a/src/client/conversation-viewer/conversation-email.vala
b/src/client/conversation-viewer/conversation-email.vala
index 4a3446b..5115a2c 100644
--- a/src/client/conversation-viewer/conversation-email.vala
+++ b/src/client/conversation-viewer/conversation-email.vala
@@ -75,6 +75,9 @@ public class ConversationEmail : Gtk.Box {
// Contacts for the email's account
private Geary.ContactStore contact_store;
+ // Message view with selected text, if any
+ private ConversationMessage? body_selection_message = null;
+
// Attachment ids that have been displayed inline
private Gee.HashSet<string> inlined_content_ids = new Gee.HashSet<string>();
@@ -162,6 +165,8 @@ public class ConversationEmail : Gtk.Box {
/** Fired when the view source action is activated. */
public signal void view_source();
+ /** Fired when the user selects text in a message. */
+ internal signal void body_selection_changed(bool has_selection);
/**
* Constructs a new view to display an email.
@@ -238,6 +243,9 @@ public class ConversationEmail : Gtk.Box {
primary_message.web_view.link_selected.connect((link) => {
link_activated(link);
});
+ primary_message.web_view.selection_changed.connect(() => {
+ on_message_selection_changed(primary_message);
+ });
primary_message.save_image.connect((filename, buffer) => {
save_image(filename, buffer);
});
@@ -275,10 +283,13 @@ public class ConversationEmail : Gtk.Box {
primary_message.body_box.pack_start(sub_messages_box, false, false, 0);
}
foreach (Geary.RFC822.Message sub_message in sub_messages) {
- ConversationMessage conversation_message =
+ ConversationMessage attached_message =
new ConversationMessage(sub_message, contact_store, false);
- sub_messages_box.pack_start(conversation_message, false, false, 0);
- this._attached_messages.add(conversation_message);
+ attached_message.web_view.selection_changed.connect(() => {
+ on_message_selection_changed(attached_message);
+ });
+ sub_messages_box.pack_start(attached_message, false, false, 0);
+ this._attached_messages.add(attached_message);
}
pack_start(primary_message, true, true, 0);
@@ -356,6 +367,15 @@ public class ConversationEmail : Gtk.Box {
get_style_context().add_class("geary_manual_read");
}
+ /**
+ * Returns user-selected body HTML from a message, if any.
+ */
+ public string? get_body_selection() {
+ return (this.body_selection_message != null)
+ ? this.body_selection_message.get_selection_for_quoting()
+ : null;
+ }
+
private SimpleAction add_action(string name) {
SimpleAction action = new SimpleAction(name, null);
message_actions.add_action(action);
@@ -433,6 +453,18 @@ public class ConversationEmail : Gtk.Box {
contact_store.mark_contacts_async.begin(contact_list, flags, null);
}
+ private void on_message_selection_changed(ConversationMessage view) {
+ bool has_selection = false;
+ if (view.web_view.has_selection()) {
+ WebKit.DOM.Document document = view.web_view.get_dom_document();
+ has_selection = !document.default_view.get_selection().is_collapsed;
+ this.body_selection_message = view;
+ } else {
+ this.body_selection_message = null;
+ }
+ body_selection_changed(has_selection);
+ }
+
[GtkCallback]
private void on_attachments_view_activated(Gtk.IconView view, Gtk.TreePath path) {
AttachmentInfo attachment_info = attachment_info_for_view_path(path);
diff --git a/src/client/conversation-viewer/conversation-message.vala
b/src/client/conversation-viewer/conversation-message.vala
index 4f923dd..efae476 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -413,6 +413,48 @@ public class ConversationMessage : Gtk.Box {
web_view.unmark_text_matches();
}
+ internal string? get_selection_for_quoting() {
+ string? quote = null;
+ WebKit.DOM.Document document = this.web_view.get_dom_document();
+ WebKit.DOM.DOMSelection selection = document.default_view.get_selection();
+ if (!selection.is_collapsed) {
+ try {
+ WebKit.DOM.Range range = selection.get_range_at(0);
+ WebKit.DOM.HTMLElement dummy =
+ (WebKit.DOM.HTMLElement) document.create_element("div");
+ bool include_dummy = false;
+ WebKit.DOM.Node ancestor_node = range.get_common_ancestor_container();
+ WebKit.DOM.Element? ancestor = ancestor_node as WebKit.DOM.Element;
+ if (ancestor == null)
+ ancestor = ancestor_node.get_parent_element();
+ // If the selection is part of a plain text message,
+ // we have to stick it in an appropriately styled div,
+ // so that new lines are preserved.
+ if (Util.DOM.is_descendant_of(ancestor, ".plaintext")) {
+ dummy.get_class_list().add("plaintext");
+ dummy.set_attribute("style", "white-space: pre-wrap;");
+ include_dummy = true;
+ }
+ dummy.append_child(range.clone_contents());
+
+ // Remove the chrome we put around quotes, leaving
+ // only the blockquote element.
+ WebKit.DOM.NodeList quotes =
+ dummy.query_selector_all(".quote_container");
+ for (int i = 0; i < quotes.length; i++) {
+ WebKit.DOM.Element div = (WebKit.DOM.Element) quotes.item(i);
+ WebKit.DOM.Element blockquote = div.query_selector("blockquote");
+ div.get_parent_element().replace_child(blockquote, div);
+ }
+
+ quote = include_dummy ? dummy.get_outer_html() : dummy.get_inner_html();
+ } catch (Error error) {
+ debug("Problem getting selected text: %s", error.message);
+ }
+ }
+ return quote;
+ }
+
private SimpleAction add_action(string name, bool enabled) {
SimpleAction action = new SimpleAction(name, null);
action.set_enabled(enabled);
diff --git a/src/client/conversation-viewer/conversation-viewer.vala
b/src/client/conversation-viewer/conversation-viewer.vala
index 84be0ce..0a0cc47 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -94,7 +94,10 @@ public class ConversationViewer : Gtk.Stack {
// Conversation emails list
[GtkChild]
private Gtk.ListBox conversation_listbox;
- private Gtk.Widget? last_list_row;
+ private Gtk.Widget? last_list_row = null;
+
+ // Email view with selected text, if any
+ private ConversationEmail? body_selected_view = null;
// Label for displaying messages in the main pane.
[GtkChild]
@@ -210,20 +213,19 @@ public class ConversationViewer : Gtk.Stack {
}
/**
- * Returns the last email in the list by sort order, if any.
+ * Returns the email view to be replied to, if any.
+ *
+ * If an email view has selected body text that view will be
+ * returned. Else the last message by sort order will be returned,
+ * if any.
*/
- public Geary.Email? get_last_email() {
- return emails.is_empty ? null : emails.last();
- }
-
- /**
- * Returns the email with text currently selected, if any.
- */
- public Geary.Email? get_selected_email(out string? quote) {
- // XXX check to see if there is a email with selected text,
- // if so return that
- quote = null;
- return emails.is_empty ? null : emails.last();
+ public ConversationEmail get_reply_email_view() {
+ ConversationEmail view = this.body_selected_view;
+ if (view == null) {
+ Geary.Email last_email = emails.is_empty ? null : emails.last();
+ view = conversation_email_for_id(last_email.id);
+ }
+ return view;
}
/**
@@ -405,6 +407,7 @@ public class ConversationViewer : Gtk.Stack {
email_to_row.clear();
emails.clear();
current_conversation = null;
+ body_selected_view = null;
cleared();
}
@@ -696,6 +699,9 @@ public class ConversationViewer : Gtk.Stack {
);
conversation_email.mark_email.connect(on_mark_email);
conversation_email.mark_email_from_here.connect(on_mark_email_from_here);
+ conversation_email.body_selection_changed.connect((email, has_selection) => {
+ this.body_selected_view = has_selection ? email : null;
+ });
ConversationMessage conversation_message = conversation_email.primary_message;
conversation_message.body_box.button_release_event.connect_after((event) => {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]