[geary/wip/714922-multiple-addresses-2: 2/5] Rebase against master from mar-v-in



commit a0bdbc2b69eb0fac8cba634aec7beb113bb98b29
Author: Jim Nelson <jim yorba org>
Date:   Fri Feb 6 12:30:25 2015 -0800

    Rebase against master from mar-v-in

 src/client/composer/composer-widget.vala           |  147 ++++++++++++++------
 .../conversation-list/conversation-list-store.vala |    4 +-
 .../formatted-conversation-data.vala               |   30 ++--
 src/engine/api/geary-account-information.vala      |   28 ++++-
 src/engine/imap-db/outbox/smtp-outbox-folder.vala  |    2 +-
 src/engine/rfc822/rfc822-utils.vala                |   33 +++--
 6 files changed, 172 insertions(+), 72 deletions(-)
---
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 9626b2f..541e30a 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -26,6 +26,15 @@ public class ComposerWidget : Gtk.EventBox {
         INLINE,
         INLINE_COMPACT
     }
+
+    private class FromAddressMap {
+        public Geary.Account account;
+        public string email;
+        public FromAddressMap(Geary.Account a, string e) {
+            account = a;
+            email = e;
+        }
+    }
     
     public const string ACTION_UNDO = "undo";
     public const string ACTION_REDO = "redo";
@@ -205,6 +214,7 @@ public class ComposerWidget : Gtk.EventBox {
     private Gtk.Label from_label;
     private Gtk.Label from_single;
     private Gtk.ComboBoxText from_multiple = new Gtk.ComboBoxText();
+    private Gee.ArrayList<FromAddressMap> from_list = new Gee.ArrayList<FromAddressMap>();
     private EmailEntry to_entry;
     private EmailEntry cc_entry;
     private Gtk.Label bcc_label;
@@ -236,6 +246,7 @@ public class ComposerWidget : Gtk.EventBox {
     private bool action_flag = false;
     private bool is_attachment_overlay_visible = false;
     private Gee.List<Geary.Attachment>? pending_attachments = null;
+    private string? preferred_from_address = null;
     private Geary.RFC822.MailboxAddresses reply_to_addresses;
     private Geary.RFC822.MailboxAddresses reply_cc_addresses;
     private string reply_subject = "";
@@ -427,7 +438,7 @@ public class ComposerWidget : Gtk.EventBox {
         
         add_extra_accelerators();
         
-        from = account.information.get_from().to_rfc822_string();
+        from = account.information.get_primary_from().to_rfc822_string();
         update_from_field();
         from_multiple.changed.connect(on_from_changed);
         
@@ -571,7 +582,7 @@ public class ComposerWidget : Gtk.EventBox {
         chain.append(attachments_box);
         box.set_focus_chain(chain);
         
-        // If there's only one account, open the drafts manager.  If there's more than one account,
+        // If there's only one From option, open the drafts manager.  If there's more than one,
         // the drafts manager will be opened by on_from_changed().
         if (!from_multiple.visible)
             open_draft_manager_async.begin(null);
@@ -702,6 +713,32 @@ public class ComposerWidget : Gtk.EventBox {
         }
     }
     
+    private bool check_preferred_from_address(Gee.List<string> account_addresses,
+        Geary.RFC822.MailboxAddresses? referred_addresses) {
+        if (referred_addresses != null) {
+            foreach (string address in account_addresses) {
+                if (referred_addresses.contains(address)) {
+                    preferred_from_address = address;
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    private void set_preferred_from_address(Geary.Email referred, ComposeType compose_type) {
+        if (compose_type == ComposeType.NEW_MESSAGE) {
+            if (referred.from != null)
+                preferred_from_address = referred.from.to_rfc822_string();
+        } else {
+            Gee.List<string> account_addresses = account.information.get_all_email_addresses();
+            if (!check_preferred_from_address(account_addresses, referred.to)) {
+                if (!check_preferred_from_address(account_addresses, referred.cc))
+                    check_preferred_from_address(account_addresses, referred.bcc);
+            }
+        }
+    }
+
     private void on_load_finished(WebKit.WebFrame frame) {
         if (get_realized())
             on_load_finished_and_realized();
@@ -943,15 +980,16 @@ public class ComposerWidget : Gtk.EventBox {
     
     private void add_recipients_and_ids(ComposeType type, Geary.Email referred,
         bool modify_headers = true) {
-        string? sender_address = account.information.get_mailbox_address().address;
+        Gee.List<string> sender_addresses = account.information.get_all_email_addresses();
         Geary.RFC822.MailboxAddresses to_addresses =
-            Geary.RFC822.Utils.create_to_addresses_for_reply(referred, sender_address);
+            Geary.RFC822.Utils.create_to_addresses_for_reply(referred, sender_addresses);
         Geary.RFC822.MailboxAddresses cc_addresses =
-            Geary.RFC822.Utils.create_cc_addresses_for_reply_all(referred, sender_address);
+            Geary.RFC822.Utils.create_cc_addresses_for_reply_all(referred, sender_addresses);
         reply_to_addresses = Geary.RFC822.Utils.merge_addresses(reply_to_addresses, to_addresses);
         reply_cc_addresses = Geary.RFC822.Utils.remove_addresses(
             Geary.RFC822.Utils.merge_addresses(reply_cc_addresses, cc_addresses),
             reply_to_addresses);
+        set_preferred_from_address(referred, type);
         
         if (!modify_headers)
             return;
@@ -2218,6 +2256,36 @@ public class ComposerWidget : Gtk.EventBox {
             action_flag = false;
         }
     }
+
+    private void add_account_emails_to_from_list(Geary.Account account) {
+        from_multiple.append_text(account.information.get_primary_mailbox_address().to_rfc822_string());
+        from_list.add(new FromAddressMap(account, account.information.email));
+
+        bool set_active = false;
+        int index = 1; // We already inserted the main address.
+        if (account.information.alternate_emails != null) {
+            foreach (string alternate_email in account.information.alternate_emails) {
+                string rfc822_address = new Geary.RFC822.MailboxAddress(
+                    account.information.real_name, alternate_email).to_rfc822_string();
+                // Displayed in the From dropdown to indicate an "alternate email address"
+                // for an account.  The first printf argument will be the alternate email
+                // address, and the second will be the account's primary email address.
+                string display = _("%1$s via %2$s").printf(rfc822_address, account.information.email);
+                from_multiple.append_text(display);
+                from_list.add(new FromAddressMap(account, alternate_email));
+                
+                if (!set_active && preferred_from_address == alternate_email) {
+                    from_multiple.set_active(index);
+                    set_active = true;
+                }
+                
+                index++;
+            }
+        }
+        
+        if (!set_active)
+            from_multiple.set_active(0);
+    }
     
     private void update_from_field() {
         from_single.visible = from_multiple.visible = from_label.visible = false;
@@ -2237,44 +2305,39 @@ public class ComposerWidget : Gtk.EventBox {
             return;
         
         // If there's only one account, show nothing. (From fields are hidden above.)
-        if (accounts.size <= 1)
+        if (accounts.size < 1 || (accounts.size == 1 && Geary.traverse<Geary.AccountInformation>(
+            accounts.values).first().alternate_emails == null))
             return;
         
         from_label.visible = true;
+
+        from_label.set_use_underline(true);
+        from_label.set_mnemonic_widget(from_multiple);
+        // Composer label (with mnemonic underscore) for the account selector
+        // when choosing what address to send a message from.
+        from_label.set_text_with_mnemonic(_("_From:"));
+
+        from_multiple.visible = true;
+        from_multiple.remove_all();
+        from_list = new Gee.ArrayList<FromAddressMap>();
         
         if (compose_type == ComposeType.NEW_MESSAGE) {
-            // For new messages, show the account combo-box.
-            from_label.set_use_underline(true);
-            from_label.set_mnemonic_widget(from_multiple);
-            // Composer label (with mnemonic underscore) for the account selector
-            // when choosing what address to send a message from.
-            from_label.set_text_with_mnemonic(_("_From:"));
-            
-            from_multiple.visible = true;
-            from_multiple.remove_all();
-            foreach (Geary.AccountInformation a in accounts.values)
-                from_multiple.append(a.email, a.get_mailbox_address().get_full_address());
-            
-            // Set the active account to the currently selected account, or failing that, set it
-            // to the first account in the list.
-            if (!from_multiple.set_active_id(account.information.email))
-                from_multiple.set_active(0);
+            add_account_emails_to_from_list(account);
+            foreach (Geary.AccountInformation info in accounts.values) {
+                try {
+                    Geary.Account a = Geary.Engine.instance.get_account_instance(info);
+                    if (a != account)
+                        add_account_emails_to_from_list(a);
+                } catch (Error e) {
+                    debug("Error getting account in composer: %s", e.message);
+                }
+            }
         } else {
-            // For other types of messages, just show the from account.
-            from_label.set_use_underline(false);
-            // Composer label (without mnemonic underscore) for the account selector
-            // when choosing what address to send a message from.
-            from_label.set_text(_("From:"));
-            
-            from_single.label = account.information.get_mailbox_address().get_full_address();
-            from_single.visible = true;
+            add_account_emails_to_from_list(account);
         }
     }
     
     private void on_from_changed() {
-        if (compose_type != ComposeType.NEW_MESSAGE)
-            return;
-        
         bool changed = false;
         try {
             changed = update_from_account();
@@ -2293,24 +2356,20 @@ public class ComposerWidget : Gtk.EventBox {
     }
     
     private bool update_from_account() throws Error {
-        // Since we've set the combo box ID to the email addresses, we can
-        // fetch that and use it to grab the account from the engine.
-        string? id = from_multiple.get_active_id();
-        if (id == null)
+        int index = from_multiple.get_active();
+        if (index < 0)
             return false;
         
-        // it's possible for changed signals to fire even though nothing has changed; catch that
-        // here when possible to avoid a lot of extra work
-        Geary.AccountInformation? new_account_info = Geary.Engine.instance.get_accounts().get(id);
-        if (new_account_info == null)
-            return false;
+        assert(from_list.size > index);
         
-        Geary.Account new_account = Geary.Engine.instance.get_account_instance(new_account_info);
+        Geary.Account new_account = from_list.get(index).account;
+        // TODO: Allow using other real name (not only mail address)
+        from = new Geary.RFC822.MailboxAddresses.single(new Geary.RFC822.MailboxAddress(
+            new_account.information.real_name, from_list.get(index).email)).to_rfc822_string();
         if (new_account == account)
             return false;
         
         account = new_account;
-        from = new_account_info.get_from().to_rfc822_string();
         set_entry_completions();
         
         return true;
diff --git a/src/client/conversation-list/conversation-list-store.vala 
b/src/client/conversation-list/conversation-list-store.vala
index 12859cd..f359af1 100644
--- a/src/client/conversation-list/conversation-list-store.vala
+++ b/src/client/conversation-list/conversation-list-store.vala
@@ -72,6 +72,7 @@ public class ConversationListStore : Gtk.ListStore {
         }
     }
     
+    public Gee.List<string>? account_owner_emails { get; set; default = null; }
     public Geary.ProgressMonitor preview_monitor { get; private set; default = 
         new Geary.SimpleProgressMonitor(Geary.ProgressType.ACTIVITY); }
     public bool is_clearing { get; private set; default = false; }
@@ -289,7 +290,8 @@ public class ConversationListStore : Gtk.ListStore {
     
     private void set_row(Gtk.TreeIter iter, Geary.App.Conversation conversation, Geary.Email preview) {
         FormattedConversationData conversation_data = new FormattedConversationData(conversation,
-            preview, conversation_monitor.folder, conversation_monitor.folder.account.information.email);
+            preview, conversation_monitor.folder, 
+            conversation_monitor.folder.account.information.get_all_email_addresses());
         
         Gtk.TreePath? path = get_path(iter);
         assert(path != null);
diff --git a/src/client/conversation-list/formatted-conversation-data.vala 
b/src/client/conversation-list/formatted-conversation-data.vala
index 211361a..be0d05c 100644
--- a/src/client/conversation-list/formatted-conversation-data.vala
+++ b/src/client/conversation-list/formatted-conversation-data.vala
@@ -30,12 +30,12 @@ public class FormattedConversationData : Geary.BaseObject {
             this.is_unread = is_unread;
         }
         
-        public string get_full_markup(string normalized_account_key) {
-            return get_as_markup((key == normalized_account_key) ? ME : address.get_short_address());
+        public string get_full_markup(Gee.List<string> normalized_account_emails) {
+            return get_as_markup((key in normalized_account_emails) ? ME : address.get_short_address());
         }
         
-        public string get_short_markup(string normalized_account_key) {
-            if (key == normalized_account_key)
+        public string get_short_markup(Gee.List<string> normalized_account_emails) {
+            if (key in normalized_account_emails)
                 return get_as_markup(ME);
             
             string short_address = address.get_short_address().strip();
@@ -45,17 +45,17 @@ public class FormattedConversationData : Geary.BaseObject {
                 string[] tokens = short_address.split(", ", 2);
                 short_address = tokens[1].strip();
                 if (Geary.String.is_empty(short_address))
-                    return get_full_markup(normalized_account_key);
+                    return get_full_markup(normalized_account_emails);
             }
             
             // use first name as delimited by a space
             string[] tokens = short_address.split(" ", 2);
             if (tokens.length < 1)
-                return get_full_markup(normalized_account_key);
+                return get_full_markup(normalized_account_emails);
             
             string first_name = tokens[0].strip();
             if (Geary.String.is_empty_or_whitespace(first_name))
-                return get_full_markup(normalized_account_key);
+                return get_full_markup(normalized_account_emails);
             
             return get_as_markup(first_name);
         }
@@ -89,17 +89,17 @@ public class FormattedConversationData : Geary.BaseObject {
     public Geary.Email? preview { get; private set; default = null; }
     
     private Geary.App.Conversation? conversation = null;
-    private string? account_owner_email = null;
+    private Gee.List<string>? account_owner_emails = null;
     private bool use_to = true;
     private CountBadge count_badge = new CountBadge(2);
     
     // Creates a formatted message data from an e-mail.
     public FormattedConversationData(Geary.App.Conversation conversation, Geary.Email preview,
-        Geary.Folder folder, string account_owner_email) {
+        Geary.Folder folder, Gee.List<string> account_owner_emails) {
         assert(preview.fields.fulfills(ConversationListStore.REQUIRED_FIELDS));
         
         this.conversation = conversation;
-        this.account_owner_email = account_owner_email;
+        this.account_owner_emails = account_owner_emails;
         use_to = (folder != null) && folder.special_folder_type.is_outgoing();
         
         // Load preview-related data.
@@ -173,10 +173,12 @@ public class FormattedConversationData : Geary.BaseObject {
     }
     
     private string get_participants_markup(Gtk.Widget widget, bool selected) {
-        if (conversation == null || account_owner_email == null)
+        if (conversation == null || account_owner_emails == null || account_owner_emails.size == 0)
             return "";
         
-        string normalized_account_owner_email = account_owner_email.normalize().casefold();
+        Gee.ArrayList<string> normalized_account_owner_emails = Geary.traverse<string>(account_owner_emails)
+            .map<string>(e => e.normalize().casefold())
+            .to_array_list();
         
         // Build chronological list of AuthorDisplay records, setting to unread if any message by
         // that author is unread
@@ -210,14 +212,14 @@ public class FormattedConversationData : Geary.BaseObject {
             rgba_to_markup(get_foreground_rgba(widget, selected))));
         if (list.size == 1) {
             // if only one participant, use full name
-            builder.append(list[0].get_full_markup(normalized_account_owner_email));
+            builder.append(list[0].get_full_markup(normalized_account_owner_emails));
         } else {
             bool first = true;
             foreach (ParticipantDisplay participant in list) {
                 if (!first)
                     builder.append(", ");
                 
-                builder.append(participant.get_short_markup(normalized_account_owner_email));
+                builder.append(participant.get_short_markup(normalized_account_owner_emails));
                 first = false;
             }
         }
diff --git a/src/engine/api/geary-account-information.vala b/src/engine/api/geary-account-information.vala
index 926dc27..af25735 100644
--- a/src/engine/api/geary-account-information.vala
+++ b/src/engine/api/geary-account-information.vala
@@ -10,6 +10,7 @@ public class Geary.AccountInformation : BaseObject {
     private const string GROUP = "AccountInformation";
     private const string REAL_NAME_KEY = "real_name";
     private const string NICKNAME_KEY = "nickname";
+    private const string ALTERNATE_EMAILS_KEY = "alternate_emails";
     private const string SERVICE_PROVIDER_KEY = "service_provider";
     private const string ORDINAL_KEY = "ordinal";
     private const string PREFETCH_PERIOD_DAYS_KEY = "prefetch_period_days";
@@ -60,6 +61,7 @@ public class Geary.AccountInformation : BaseObject {
     public string real_name { get; set; }
     public string nickname { get; set; }
     public string email { get; set; }
+    public Gee.List<string>? alternate_emails { get; set; }
     public Geary.ServiceProvider service_provider { get; set; }
     public int prefetch_period_days { get; set; }
     
@@ -143,6 +145,9 @@ public class Geary.AccountInformation : BaseObject {
         } finally {
             real_name = get_string_value(key_file, GROUP, REAL_NAME_KEY);
             nickname = get_string_value(key_file, GROUP, NICKNAME_KEY);
+            alternate_emails = get_string_list_value(key_file, GROUP, ALTERNATE_EMAILS_KEY);
+            if (alternate_emails.size == 0)
+                alternate_emails = null;
             imap_credentials.user = get_string_value(key_file, GROUP, IMAP_USERNAME_KEY, email);
             imap_remember_password = get_bool_value(key_file, GROUP, IMAP_REMEMBER_PASSWORD_KEY, true);
             smtp_credentials.user = get_string_value(key_file, GROUP, SMTP_USERNAME_KEY, email);
@@ -226,6 +231,12 @@ public class Geary.AccountInformation : BaseObject {
         real_name = from.real_name;
         nickname = from.nickname;
         email = from.email;
+        alternate_emails = null;
+        if (from.alternate_emails != null) {
+            alternate_emails = new Gee.ArrayList<string>();
+            foreach (string alternate_email in from.alternate_emails)
+                alternate_emails.add(alternate_email);
+        }
         service_provider = from.service_provider;
         prefetch_period_days = from.prefetch_period_days;
         save_sent_mail = from.save_sent_mail;
@@ -254,6 +265,17 @@ public class Geary.AccountInformation : BaseObject {
     }
     
     /**
+     * Return a list of the primary and all alternate email addresses.
+     */
+    public Gee.List<string> get_all_email_addresses() {
+        Gee.ArrayList<string> all = new Gee.ArrayList<string>();
+        all.add(email);
+        if (alternate_emails != null)
+            all.add_all(alternate_emails);
+        return all;
+    }
+    
+    /**
      * Return whether this account allows setting the save_sent_mail option.
      * If not, save_sent_mail will always be true and setting it will be
      * ignored.
@@ -792,15 +814,15 @@ public class Geary.AccountInformation : BaseObject {
     /**
      * Returns a MailboxAddress object for this account.
      */
-    public RFC822.MailboxAddress get_mailbox_address() {
+    public RFC822.MailboxAddress get_primary_mailbox_address() {
         return new RFC822.MailboxAddress(real_name, email);
     }
     
     /**
      * Returns a MailboxAddresses object with this mailbox address.
      */
-    public RFC822.MailboxAddresses get_from() {
-        return new RFC822.MailboxAddresses.single(get_mailbox_address());
+    public RFC822.MailboxAddresses get_primary_from() {
+        return new RFC822.MailboxAddresses.single(get_primary_mailbox_address());
     }
     
     public static int compare_ascending(AccountInformation a, AccountInformation b) {
diff --git a/src/engine/imap-db/outbox/smtp-outbox-folder.vala 
b/src/engine/imap-db/outbox/smtp-outbox-folder.vala
index 745967b..a09c6bc 100644
--- a/src/engine/imap-db/outbox/smtp-outbox-folder.vala
+++ b/src/engine/imap-db/outbox/smtp-outbox-folder.vala
@@ -634,7 +634,7 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractLocalFolder, Geary.FolderSu
         
         if (smtp_err == null) {
             try {
-                yield smtp.send_email_async(_account.information.get_mailbox_address(),
+                yield smtp.send_email_async(_account.information.get_primary_mailbox_address(),
                     rfc822, cancellable);
             } catch (Error send_err) {
                 debug("SMTP send mail error: %s", send_err.message);
diff --git a/src/engine/rfc822/rfc822-utils.vala b/src/engine/rfc822/rfc822-utils.vala
index a76373b..e9a8da9 100644
--- a/src/engine/rfc822/rfc822-utils.vala
+++ b/src/engine/rfc822/rfc822-utils.vala
@@ -69,14 +69,22 @@ private void remove_address(Gee.List<Geary.RFC822.MailboxAddress> addresses,
     }
 }
 
+private bool email_is_from_sender(Geary.Email email, Gee.List<string>? sender_addresses) {
+    if (sender_addresses == null)
+        return false;
+    
+    return Geary.traverse<string>(sender_addresses).any(a =>
+        !String.is_empty(a) && email.from.contains(a));
+}
+
 public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email email,
-    string? sender_address = null) {
+    Gee.List<string>? sender_addresses = null) {
     Gee.List<Geary.RFC822.MailboxAddress> new_to =
         new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
     
     // If we're replying to something we sent, send it to the same people we originally did.
     // Otherwise, we'll send to the reply-to address or the from address.
-    if (email.to != null && !String.is_empty(sender_address) && email.from.contains(sender_address))
+    if (email.to != null && email_is_from_sender(email, sender_addresses))
         new_to.add_all(email.to.get_all());
     else if (email.reply_to != null)
         new_to.add_all(email.reply_to.get_all());
@@ -84,29 +92,36 @@ public Geary.RFC822.MailboxAddresses create_to_addresses_for_reply(Geary.Email e
         new_to.add_all(email.from.get_all());
     
     // Exclude the current sender.  No need to receive the mail they're sending.
-    if (!String.is_empty(sender_address))
-        remove_address(new_to, sender_address);
+    if (sender_addresses != null) {
+        foreach (string address in sender_addresses) {
+            if (!String.is_empty(address))
+                remove_address(new_to, address);
+        }
+    }
     
     return new Geary.RFC822.MailboxAddresses(new_to);
 }
 
 public Geary.RFC822.MailboxAddresses create_cc_addresses_for_reply_all(Geary.Email email,
-    string? sender_address = null) {
+    Gee.List<string>? sender_addresses = null) {
     Gee.List<Geary.RFC822.MailboxAddress> new_cc = new Gee.ArrayList<Geary.RFC822.MailboxAddress>();
     
     // If we're replying to something we received, also add other recipients.  Don't do this for
     // emails we sent, since everyone we sent it to is already covered in
     // create_to_addresses_for_reply().
-    if (email.to != null && (String.is_empty(sender_address) ||
-        !email.from.contains(sender_address)))
+    if (email.to != null && !email_is_from_sender(email, sender_addresses))
         new_cc.add_all(email.to.get_all());
     
     if (email.cc != null)
         new_cc.add_all(email.cc.get_all());
     
     // Again, exclude the current sender.
-    if (!String.is_empty(sender_address))
-        remove_address(new_cc, sender_address, true);
+    if (sender_addresses != null) {
+        foreach (string address in sender_addresses) {
+            if (!String.is_empty(address))
+                remove_address(new_cc, address, true);
+        }
+    }
     
     return new Geary.RFC822.MailboxAddresses(new_cc);
 }


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