Closes #6248 Account nicknames
This commit is contained in:
parent
a9dc52b658
commit
e18bef9638
6 changed files with 135 additions and 19 deletions
|
|
@ -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<string, Geary.AccountInformation> 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));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -64,10 +64,11 @@
|
|||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="placeholder_text">email@example.com</property>
|
||||
<property name="input_purpose">email</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
|
|
@ -84,10 +85,11 @@
|
|||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="placeholder_text">Password</property>
|
||||
<property name="input_purpose">password</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
|
|
@ -103,7 +105,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
|
|
@ -119,7 +121,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
|
|
@ -180,6 +182,7 @@
|
|||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="placeholder_text">First Last</property>
|
||||
<property name="input_purpose">name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
|
|
@ -201,7 +204,44 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label: nickname">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="xpad">1</property>
|
||||
<property name="label" translatable="yes">N_ickname:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">entry: nickname</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry: nickname">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="max_length">255</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="placeholder_text">Work, Home, etc</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="width">1</property>
|
||||
<property name="height">1</property>
|
||||
</packing>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue