From e18bef9638594e2256bba75cfebb35d19636eff8 Mon Sep 17 00:00:00 2001 From: Eric Gregory Date: Thu, 31 Jan 2013 14:54:03 -0800 Subject: [PATCH] Closes #6248 Account nicknames --- .../account-dialog-account-list-pane.vala | 53 ++++++++++++++----- src/client/accounts/add-edit-page.vala | 36 ++++++++++++- src/engine/api/geary-account-information.vala | 5 ++ src/engine/api/geary-engine.vala | 6 +++ src/engine/util/util-string.vala | 4 ++ ui/login.glade | 50 +++++++++++++++-- 6 files changed, 135 insertions(+), 19 deletions(-) diff --git a/src/client/accounts/account-dialog-account-list-pane.vala b/src/client/accounts/account-dialog-account-list-pane.vala index e1361b36..02c4f01d 100644 --- a/src/client/accounts/account-dialog-account-list-pane.vala +++ b/src/client/accounts/account-dialog-account-list-pane.vala @@ -6,8 +6,13 @@ // List of accounts. Used with AccountDialog. public class AccountDialogAccountListPane : Gtk.Box { + public enum Column { + ACCOUNT_NICKNAME = 0, + ACCOUNT_ADDRESS; + } + private Gtk.TreeView list_view; - private Gtk.ListStore list_model = new Gtk.ListStore(1, typeof (string)); + private Gtk.ListStore list_model = new Gtk.ListStore(2, typeof (string), typeof (string)); private Gtk.Action edit_action; public signal void add_account(); @@ -21,13 +26,19 @@ public class AccountDialogAccountListPane : Gtk.Box { Gtk.Builder builder = GearyApplication.instance.create_builder("account_list.glade"); pack_end((Gtk.Box) builder.get_object("container")); - - list_view = (Gtk.TreeView) builder.get_object("account_list"); - list_view.set_model(list_model); - list_view.insert_column_with_attributes (-1, "Account", new Gtk.CellRendererText (), "text", 0); Gtk.ActionGroup actions = (Gtk.ActionGroup) builder.get_object("account list actions"); edit_action = actions.get_action("edit_account"); + // Set up list. + list_view = (Gtk.TreeView) builder.get_object("account_list"); + list_view.set_model(list_model); + list_view.insert_column_with_attributes(-1, "Nickname", new Gtk.CellRendererText(), "text", + Column.ACCOUNT_NICKNAME); + list_view.get_column(Column.ACCOUNT_NICKNAME).set_expand(true); + list_view.insert_column_with_attributes(-1, "Address", new Gtk.CellRendererText(), "text", + Column.ACCOUNT_ADDRESS); + list_view.get_column(Column.ACCOUNT_ADDRESS).set_expand(true); + // Hook up signals. actions.get_action("close").activate.connect(() => { close(); }); actions.get_action("add_account").activate.connect(() => { add_account(); }); @@ -43,8 +54,12 @@ public class AccountDialogAccountListPane : Gtk.Box { // Add email accounts to the list. try { - foreach (string address in Geary.Engine.instance.get_accounts().keys) - add_account_to_list(address); + Gee.Map all_accounts = + Geary.Engine.instance.get_accounts(); + + foreach (Geary.AccountInformation account in all_accounts.values) { + on_account_added(account); + } } catch (Error e) { debug("Error enumerating accounts: %s", e.message); } @@ -89,7 +104,7 @@ public class AccountDialogAccountListPane : Gtk.Box { return null; string? account = null; - list_model.get(iter, 0, out account); + list_model.get(iter, Column.ACCOUNT_ADDRESS, out account); return account; } @@ -102,19 +117,22 @@ public class AccountDialogAccountListPane : Gtk.Box { if (iter != null) return; // Already listed. - add_account_to_list(account.email); + add_account_to_list(account.nickname, account.email); + account.notify.connect(on_account_changed); } private void on_account_removed(Geary.AccountInformation account) { remove_account_from_list(account.email); + account.notify.disconnect(on_account_changed); } // Adds an account to the list. // Note: does NOT check if the account is already listed. - private void add_account_to_list(string address) { + private void add_account_to_list(string nickname, string address) { Gtk.TreeIter iter; list_model.append(out iter); - list_model.set(iter, 0, address); + list_model.set(iter, Column.ACCOUNT_NICKNAME, nickname); + list_model.set(iter, Column.ACCOUNT_ADDRESS, address); } // Removes an account on the list. @@ -126,6 +144,17 @@ public class AccountDialogAccountListPane : Gtk.Box { list_model.remove(iter); } + private void on_account_changed(Object object, ParamSpec p) { + Geary.AccountInformation account = (Geary.AccountInformation) object; + + Gtk.TreeIter? iter = list_contains(account.email); + if (iter == null) + return; + + // Since nickname is the only column that can change, just set it. + list_model.set_value(iter, Column.ACCOUNT_NICKNAME, account.nickname); + } + // Returns TreeIter of the address in the account list, else null. private Gtk.TreeIter? list_contains(string address) { Gtk.TreeIter iter; @@ -135,7 +164,7 @@ public class AccountDialogAccountListPane : Gtk.Box { do { string list_address = ""; - list_model.get(iter, 0, out list_address); + list_model.get(iter, Column.ACCOUNT_ADDRESS, out list_address); if (list_address == address) return iter; } while (list_model.iter_next(ref iter)); diff --git a/src/client/accounts/add-edit-page.vala b/src/client/accounts/add-edit-page.vala index fe04a341..4fda4095 100644 --- a/src/client/accounts/add-edit-page.vala +++ b/src/client/accounts/add-edit-page.vala @@ -17,6 +17,11 @@ public class AddEditPage : Gtk.Box { set { entry_real_name.text = value; } } + public string nickname { + get { return entry_nickname.text; } + set { entry_nickname.text = value; } + } + public string email_address { get { return entry_email.text; } set { entry_email.text = value; } @@ -120,6 +125,8 @@ public class AddEditPage : Gtk.Box { private Gtk.Label label_password; private Gtk.Entry entry_password; private Gtk.Entry entry_real_name; + private Gtk.Label label_nickname; + private Gtk.Entry entry_nickname; private Gtk.ComboBoxText combo_service; private Gtk.CheckButton check_remember_password; @@ -161,6 +168,8 @@ public class AddEditPage : Gtk.Box { _("Welcome to Geary."), _("Enter your account information to get started."))); entry_real_name = (Gtk.Entry) builder.get_object("entry: real_name"); + label_nickname = (Gtk.Label) builder.get_object("label: nickname"); + entry_nickname = (Gtk.Entry) builder.get_object("entry: nickname"); combo_service = (Gtk.ComboBoxText) builder.get_object("combo: service"); entry_email = (Gtk.Entry) builder.get_object("entry: email"); label_password = (Gtk.Label) builder.get_object("label: password"); @@ -213,6 +222,8 @@ public class AddEditPage : Gtk.Box { entry_imap_port.insert_text.connect(on_port_insert_text); entry_smtp_port.insert_text.connect(on_port_insert_text); + entry_nickname.insert_text.connect(on_nickname_insert_text); + // Shows/hides settings. update_ui(); } @@ -220,6 +231,7 @@ public class AddEditPage : Gtk.Box { // Sets the account information to display on this page. public void set_account_information(Geary.AccountInformation info) { set_all_info(info.real_name, + info.nickname, info.email, info.imap_credentials.user, info.imap_credentials.pass, @@ -240,6 +252,7 @@ public class AddEditPage : Gtk.Box { // Can use this instead of set_account_information(), both do the same thing. public void set_all_info( string? initial_real_name = null, + string? initial_nickname = null, string? initial_email = null, string? initial_imap_username = null, string? initial_imap_password = null, @@ -258,6 +271,7 @@ public class AddEditPage : Gtk.Box { // Set defaults real_name = initial_real_name ?? ""; + nickname = initial_nickname ?? ""; email_address = initial_email ?? ""; password = initial_imap_password != null ? initial_imap_password : ""; remember_password = initial_remember_password; @@ -321,6 +335,17 @@ public class AddEditPage : Gtk.Box { info_changed(); } + // Prevent non-printable characters in nickname field. + private void on_nickname_insert_text(Gtk.Editable e, string text, int length, ref int position) { + for (long i = 0; i < text.char_count(); i++) { + if (!text.get_char(i).isprint()) { + Signal.stop_emission_by_name(e, "insert-text"); + + return; + } + } + } + private void on_port_insert_text(Gtk.Editable e, string text, int length, ref int position) { // Prevent non-numerical characters and ensure port is <= uint16.MAX if (!uint64.try_parse(text) || uint64.parse(((Gtk.Entry) e).text) > uint16.MAX) { @@ -378,7 +403,8 @@ public class AddEditPage : Gtk.Box { public bool is_complete() { switch (get_service_provider()) { case Geary.ServiceProvider.OTHER: - if (Geary.String.is_empty_or_whitespace(email_address) || + if (Geary.String.is_empty_or_whitespace(nickname) || + Geary.String.is_empty_or_whitespace(email_address) || Geary.String.is_empty_or_whitespace(imap_host) || Geary.String.is_empty_or_whitespace(imap_port.to_string()) || Geary.String.is_empty_or_whitespace(imap_username) || @@ -392,7 +418,8 @@ public class AddEditPage : Gtk.Box { // GMAIL and YAHOO default: - if (Geary.String.is_empty_or_whitespace(email_address) || + if (Geary.String.is_empty_or_whitespace(nickname) || + Geary.String.is_empty_or_whitespace(email_address) || Geary.String.is_empty_or_whitespace(password)) return false; break; @@ -419,6 +446,7 @@ public class AddEditPage : Gtk.Box { } account_information.real_name = real_name.strip(); + account_information.nickname = nickname.strip(); account_information.imap_credentials = imap_credentials; account_information.smtp_credentials = smtp_credentials; account_information.imap_remember_password = remember_password; @@ -452,6 +480,10 @@ public class AddEditPage : Gtk.Box { private void update_ui() { base.show_all(); welcome_box.visible = mode == PageMode.WELCOME; + entry_nickname.visible = label_nickname.visible = mode != PageMode.WELCOME; + + if (mode == PageMode.WELCOME) + nickname = Geary.AccountInformation.DEFAULT_NICKNAME; if (get_service_provider() == Geary.ServiceProvider.OTHER) { // Display all options for custom providers. diff --git a/src/engine/api/geary-account-information.vala b/src/engine/api/geary-account-information.vala index a64d6183..775fc968 100644 --- a/src/engine/api/geary-account-information.vala +++ b/src/engine/api/geary-account-information.vala @@ -7,6 +7,7 @@ public class Geary.AccountInformation : Object { private const string GROUP = "AccountInformation"; private const string REAL_NAME_KEY = "real_name"; + private const string NICKNAME_KEY = "nickname"; private const string SERVICE_PROVIDER_KEY = "service_provider"; private const string IMAP_USERNAME_KEY = "imap_username"; private const string IMAP_REMEMBER_PASSWORD_KEY = "imap_remember_password"; @@ -23,11 +24,13 @@ public class Geary.AccountInformation : Object { private const string SMTP_STARTTLS = "smtp_starttls"; public const string SETTINGS_FILENAME = "geary.ini"; + public const string DEFAULT_NICKNAME = _("Default"); internal File settings_dir; internal File file; public string real_name { get; set; } + public string nickname { get; set; } public string email { get; set; } public Geary.ServiceProvider service_provider { get; set; } public bool imap_server_pipeline { get; set; default = true; } @@ -61,6 +64,7 @@ public class Geary.AccountInformation : Object { // It's no big deal if we couldn't load the key file -- just means we give you the defaults. } finally { real_name = get_string_value(key_file, GROUP, REAL_NAME_KEY); + nickname = get_string_value(key_file, GROUP, NICKNAME_KEY, DEFAULT_NICKNAME); 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); @@ -337,6 +341,7 @@ public class Geary.AccountInformation : Object { KeyFile key_file = new KeyFile(); key_file.set_value(GROUP, REAL_NAME_KEY, real_name); + key_file.set_value(GROUP, NICKNAME_KEY, nickname); key_file.set_value(GROUP, SERVICE_PROVIDER_KEY, service_provider.to_string()); key_file.set_value(GROUP, IMAP_USERNAME_KEY, imap_credentials.user); key_file.set_boolean(GROUP, IMAP_REMEMBER_PASSWORD_KEY, imap_remember_password); diff --git a/src/engine/api/geary-engine.vala b/src/engine/api/geary-engine.vala index 8a79c684..fe94e252 100644 --- a/src/engine/api/geary-engine.vala +++ b/src/engine/api/geary-engine.vala @@ -173,6 +173,12 @@ public class Geary.Engine { Cancellable? cancellable = null) throws Error { check_opened(); + // Make sure the account nickname is not in use. + foreach (AccountInformation a in get_accounts().values) { + if (account != a && Geary.String.equals_ci(account.nickname, a.nickname)) + return false; + } + // validate IMAP, which requires logging in and establishing an AUTHORIZED cx state bool imap_valid = false; Geary.Imap.ClientSession? imap_session = new Imap.ClientSession(account.get_imap_endpoint(), true); diff --git a/src/engine/util/util-string.vala b/src/engine/util/util-string.vala index c5c23a9a..52a64fe9 100644 --- a/src/engine/util/util-string.vala +++ b/src/engine/util/util-string.vala @@ -60,6 +60,10 @@ public bool stri_equal(void *a, void *b) { return str_equal(((string *) a)->down(), ((string *) b)->down()); } +public bool equals_ci(string a, string b) { + return a.down() == b.down(); +} + public bool nullable_stri_equal(void *a, void *b) { if (a == null) return (b == null); diff --git a/ui/login.glade b/ui/login.glade index 71197195..31588049 100644 --- a/ui/login.glade +++ b/ui/login.glade @@ -64,10 +64,11 @@ False False email@example.com + email 1 - 2 + 3 1 1 @@ -84,10 +85,11 @@ False False Password + password 1 - 3 + 4 1 1 @@ -103,7 +105,7 @@ 0 - 2 + 3 1 1 @@ -119,7 +121,7 @@ 0 - 3 + 4 1 1 @@ -180,6 +182,7 @@ False False First Last + name 1 @@ -201,7 +204,44 @@ 1 - 4 + 5 + 1 + 1 + + + + + True + False + 0 + 1 + N_ickname: + True + entry: nickname + + + 0 + 2 + 1 + 1 + + + + + True + True + True + 255 + + True + True + False + False + Work, Home, etc + + + 1 + 2 1 1