From 108d0abe4cd3cdb04377baa3603a60b2be7d2f5c Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Tue, 4 Nov 2014 14:44:05 -0800 Subject: [PATCH] Specify "Reply-To:" in composer fields: Bug #714588 This also closes bug #713808, as lesser-used fields (Reply-To, Bcc) are hidden unless the user expands the composer to show them. Right now that is an option in the composer's toolbar menu; that may change in the future if we can find the right place to put the widgetry. --- THANKS | 1 + src/client/composer/composer-widget.vala | 53 +++++++++++++++++++++++- src/engine/api/geary-composed-email.vala | 1 + src/engine/rfc822/rfc822-message.vala | 6 +++ ui/composer.glade | 51 +++++++++++++++++++++-- ui/composer_accelerators.ui | 1 + 6 files changed, 108 insertions(+), 5 deletions(-) diff --git a/THANKS b/THANKS index 4f5279a0..9b974e9a 100644 --- a/THANKS +++ b/THANKS @@ -25,6 +25,7 @@ Avi Levy Simon Lipp Brendan Long Angelo Marchesin +mar-v-in Kai Mast William Jon McCann Thomas Moschny diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index e0b966fd..de0e5fe8 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -48,6 +48,7 @@ public class ComposerWidget : Gtk.EventBox { public const string ACTION_COLOR = "color"; public const string ACTION_INSERT_LINK = "insertlink"; public const string ACTION_COMPOSE_AS_HTML = "compose as html"; + public const string ACTION_SHOW_EXTENDED = "show extended"; public const string ACTION_CLOSE = "close"; public const string ACTION_DETACH = "detach"; public const string ACTION_SEND = "send"; @@ -139,6 +140,11 @@ public class ComposerWidget : Gtk.EventBox { get { return bcc_entry.get_text(); } set { bcc_entry.set_text(value); } } + + public string reply_to { + get { return reply_to_entry.get_text(); } + set { reply_to_entry.set_text(value); } + } public string in_reply_to { get; set; } public string references { get; set; } @@ -160,6 +166,11 @@ public class ComposerWidget : Gtk.EventBox { get { return ((Gtk.ToggleAction) actions.get_action(ACTION_COMPOSE_AS_HTML)).active; } set { ((Gtk.ToggleAction) actions.get_action(ACTION_COMPOSE_AS_HTML)).active = value; } } + + public bool show_extended { + get { return ((Gtk.ToggleAction) actions.get_action(ACTION_SHOW_EXTENDED)).active; } + set { ((Gtk.ToggleAction) actions.get_action(ACTION_SHOW_EXTENDED)).active = value; } + } public ComposerState state { get; set; } @@ -169,7 +180,7 @@ public class ComposerWidget : Gtk.EventBox { public bool blank { get { - return to_entry.empty && cc_entry.empty && bcc_entry.empty && + return to_entry.empty && cc_entry.empty && bcc_entry.empty && reply_to_entry.empty && subject_entry.buffer.length == 0 && !editor.can_undo() && attachment_files.size == 0; } } @@ -190,7 +201,10 @@ public class ComposerWidget : Gtk.EventBox { private Gtk.ComboBoxText from_multiple = new Gtk.ComboBoxText(); private EmailEntry to_entry; private EmailEntry cc_entry; + private Gtk.Label bcc_label; private EmailEntry bcc_entry; + private Gtk.Label reply_to_label; + private EmailEntry reply_to_entry; public Gtk.Entry subject_entry; private Gtk.Label message_overlay_label; private Gtk.Box attachments_box; @@ -209,6 +223,7 @@ public class ComposerWidget : Gtk.EventBox { private Gtk.MenuItem color_item; private Gtk.MenuItem html_item; private Gtk.MenuItem html_item2; + private Gtk.MenuItem extended_item; private Gtk.ActionGroup actions; private string? hover_url = null; @@ -288,13 +303,19 @@ public class ComposerWidget : Gtk.EventBox { (builder.get_object("cc") as Gtk.EventBox).add(cc_entry); bcc_entry = new EmailEntry(this); (builder.get_object("bcc") as Gtk.EventBox).add(bcc_entry); + reply_to_entry = new EmailEntry(this); + (builder.get_object("reply to") as Gtk.EventBox).add(reply_to_entry); Gtk.Label to_label = (Gtk.Label) builder.get_object("to label"); Gtk.Label cc_label = (Gtk.Label) builder.get_object("cc label"); - Gtk.Label bcc_label = (Gtk.Label) builder.get_object("bcc label"); + bcc_label = (Gtk.Label) builder.get_object("bcc label"); + reply_to_label = (Gtk.Label) builder.get_object("reply to label"); to_label.set_mnemonic_widget(to_entry); cc_label.set_mnemonic_widget(cc_entry); bcc_label.set_mnemonic_widget(bcc_entry); + reply_to_label.set_mnemonic_widget(reply_to_entry); + + to_entry.margin_top = cc_entry.margin_top = bcc_entry.margin_top = reply_to_entry.margin_top = 6; // TODO: It would be nicer to set the completions inside the EmailEntry constructor. But in // testing, this can cause non-deterministic segfaults. Investigate why, and fix if possible. @@ -332,6 +353,7 @@ public class ComposerWidget : Gtk.EventBox { to_entry.changed.connect(validate_send_button); cc_entry.changed.connect(validate_send_button); bcc_entry.changed.connect(validate_send_button); + reply_to_entry.changed.connect(validate_send_button); if (get_direction () == Gtk.TextDirection.RTL) { actions.get_action(ACTION_INDENT).icon_name = "format-indent-more-rtl-symbolic"; @@ -362,6 +384,7 @@ public class ComposerWidget : Gtk.EventBox { actions.get_action(ACTION_REMOVE_FORMAT).activate.connect(on_remove_format); actions.get_action(ACTION_COMPOSE_AS_HTML).activate.connect(on_compose_as_html); + actions.get_action(ACTION_SHOW_EXTENDED).activate.connect(on_show_extended); actions.get_action(ACTION_INDENT).activate.connect(on_indent); actions.get_action(ACTION_OUTDENT).activate.connect(on_action); @@ -506,6 +529,8 @@ public class ComposerWidget : Gtk.EventBox { color_item.related_action = ui.get_action("ui/color"); html_item = new Gtk.CheckMenuItem(); html_item.related_action = ui.get_action("ui/htmlcompose"); + extended_item = new Gtk.CheckMenuItem(); + extended_item.related_action = ui.get_action("ui/extended"); html_item2 = new Gtk.CheckMenuItem(); html_item2.related_action = ui.get_action("ui/htmlcompose"); @@ -635,6 +660,7 @@ public class ComposerWidget : Gtk.EventBox { bind_event(editor,"a", "click", (Callback) on_link_clicked, this); update_actions(); + on_show_extended(); } // Glade only allows one accelerator per-action. This method adds extra accelerators not defined @@ -736,6 +762,9 @@ public class ComposerWidget : Gtk.EventBox { if (bcc_entry.addresses != null) email.bcc = bcc_entry.addresses; + + if (reply_to_entry.addresses != null) + email.reply_to = reply_to_entry.addresses; if (!Geary.String.is_empty(in_reply_to)) email.in_reply_to = in_reply_to; @@ -1306,6 +1335,9 @@ public class ComposerWidget : Gtk.EventBox { if (bcc_entry.addresses != null) foreach(Geary.RFC822.MailboxAddress addr in bcc_entry.addresses) tooltip.append(_("Bcc: ") + addr.get_full_address() + "\n"); + if (reply_to_entry.addresses != null) + foreach(Geary.RFC822.MailboxAddress addr in reply_to_entry.addresses) + tooltip.append(_("Reply-To: ") + addr.get_full_address() + "\n"); header.set_recipients(label, tooltip.str.slice(0, -1)); // Remove trailing \n } @@ -1453,6 +1485,16 @@ public class ComposerWidget : Gtk.EventBox { } GearyApplication.instance.config.compose_as_html = compose_as_html; } + + private void on_show_extended() { + if (!show_extended) { + bcc_label.visible = bcc_entry.visible = reply_to_label.visible = reply_to_entry.visible = false; + } else { + if (state == ComposerState.INLINE_COMPACT) + state = ComposerState.INLINE; + bcc_label.visible = bcc_entry.visible = reply_to_label.visible = reply_to_entry.visible = true; + } + } private void toggle_toolbar_buttons(bool show) { actions.get_action(ACTION_BOLD).visible = @@ -1467,6 +1509,9 @@ public class ComposerWidget : Gtk.EventBox { GtkUtil.clear_menu(menu); menu.append(html_item2); + + menu.append(new Gtk.SeparatorMenuItem()); + menu.append(extended_item); menu.show_all(); } @@ -1487,6 +1532,9 @@ public class ComposerWidget : Gtk.EventBox { menu.append(new Gtk.SeparatorMenuItem()); menu.append(html_item); + + menu.append(new Gtk.SeparatorMenuItem()); + menu.append(extended_item); menu.show_all(); // Call this or only menu items associated with actions will be displayed. } @@ -2048,6 +2096,7 @@ public class ComposerWidget : Gtk.EventBox { to_entry.completion = new ContactEntryCompletion(contact_list_store); cc_entry.completion = new ContactEntryCompletion(contact_list_store); bcc_entry.completion = new ContactEntryCompletion(contact_list_store); + reply_to_entry.completion = new ContactEntryCompletion(contact_list_store); } } diff --git a/src/engine/api/geary-composed-email.vala b/src/engine/api/geary-composed-email.vala index 35c1e82a..a9f94034 100644 --- a/src/engine/api/geary-composed-email.vala +++ b/src/engine/api/geary-composed-email.vala @@ -21,6 +21,7 @@ public class Geary.ComposedEmail : BaseObject { public RFC822.MailboxAddresses? to { get; set; default = null; } public RFC822.MailboxAddresses? cc { get; set; default = null; } public RFC822.MailboxAddresses? bcc { get; set; default = null; } + public RFC822.MailboxAddresses? reply_to { get; set; default = null; } public string? in_reply_to { get; set; default = null; } public Geary.Email? reply_to_email { get; set; default = null; } public string? references { get; set; default = null; } diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala index 1d5daecd..4402ffd4 100644 --- a/src/engine/rfc822/rfc822-message.vala +++ b/src/engine/rfc822/rfc822-message.vala @@ -25,6 +25,7 @@ public class Geary.RFC822.Message : BaseObject { public RFC822.MailboxAddresses? to { get; private set; default = null; } public RFC822.MailboxAddresses? cc { get; private set; default = null; } public RFC822.MailboxAddresses? bcc { get; private set; default = null; } + public RFC822.MailboxAddresses? reply_to { get; private set; default = null; } public RFC822.MessageIDList? in_reply_to { get; private set; default = null; } public RFC822.MessageIDList? references { get; private set; default = null; } public RFC822.Subject? subject { get; private set; default = null; } @@ -119,6 +120,11 @@ public class Geary.RFC822.Message : BaseObject { message.add_recipient(GMime.RecipientType.BCC, mailbox.name, mailbox.address); } + if (email.reply_to != null) { + reply_to = email.reply_to; + message.set_reply_to(email.reply_to.to_rfc822_string()); + } + if (email.in_reply_to != null) { in_reply_to = new Geary.RFC822.MessageIDList.from_rfc822_string(email.in_reply_to); message.set_header(HEADER_IN_REPLY_TO, email.in_reply_to); diff --git a/ui/composer.glade b/ui/composer.glade index 3874edac..2119fcae 100644 --- a/ui/composer.glade +++ b/ui/composer.glade @@ -140,6 +140,12 @@ text-html + + + Show Extended Fields + show-more + + window-close-symbolic @@ -251,7 +257,7 @@ 6 6 6 - 6 + 0 6 @@ -262,6 +268,7 @@ True right to + 6 @@ -282,6 +289,7 @@ True right to + 6 @@ -326,10 +334,11 @@ True True + 6 1 - 4 + 5 1 1 @@ -343,13 +352,14 @@ True right subject + 6 0 - 4 + 5 1 1 @@ -363,6 +373,7 @@ True right to + 6 @@ -387,6 +398,40 @@ 1 + + + True + False + 1 + _Reply-To + True + right + to + 6 + + + + 0 + 4 + 1 + 1 + + + + + True + False + True + + + 1 + 4 + 1 + 1 + + True diff --git a/ui/composer_accelerators.ui b/ui/composer_accelerators.ui index 86205871..fa2acfe5 100644 --- a/ui/composer_accelerators.ui +++ b/ui/composer_accelerators.ui @@ -15,6 +15,7 @@ +