[geary/wip/714104-refine-account-dialog: 143/180] Implement editing for the account editor pane.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/714104-refine-account-dialog: 143/180] Implement editing for the account editor pane.
- Date: Mon, 19 Nov 2018 10:16:17 +0000 (UTC)
commit bbedb24599e1c3f0a5cf5f5a63b052ce0de6edf2
Author: Michael James Gratton <mike vee net>
Date: Wed Jun 13 17:44:17 2018 +1000
Implement editing for the account editor pane.
src/client/accounts/accounts-editor-edit-pane.vala | 707 ++++++++++++++++++---
src/client/accounts/accounts-editor-row.vala | 203 +++++-
.../accounts/accounts-editor-servers-pane.vala | 139 +---
src/client/accounts/accounts-editor.vala | 122 +++-
src/client/application/geary-controller.vala | 2 +
src/engine/api/geary-account-information.vala | 75 +++
ui/accounts_editor.ui | 33 +-
ui/accounts_editor_edit_pane.ui | 75 ++-
ui/client-web-view.js | 2 +
ui/geary.css | 13 +
10 files changed, 1103 insertions(+), 268 deletions(-)
---
diff --git a/src/client/accounts/accounts-editor-edit-pane.vala
b/src/client/accounts/accounts-editor-edit-pane.vala
index b46c61d4..260e9aa5 100644
--- a/src/client/accounts/accounts-editor-edit-pane.vala
+++ b/src/client/accounts/accounts-editor-edit-pane.vala
@@ -12,19 +12,23 @@
public class Accounts.EditorEditPane : Gtk.Grid {
- private weak Editor editor; // circular ref
- private Geary.AccountInformation account;
+ /** The editor this pane belongs to. */
+ internal weak Editor editor; // circular ref
+
+ /** The account being displayed by this pane. */
+ internal Geary.AccountInformation account;
[GtkChild]
private Gtk.ListBox details_list;
[GtkChild]
- private Gtk.ListBox addresses_list;
+ private Gtk.ListBox senders_list;
[GtkChild]
- private Gtk.ScrolledWindow signature_scrolled;
+ private Gtk.Frame signature_frame;
private ClientWebView signature_preview;
+ private bool signature_changed = false;
[GtkChild]
private Gtk.ListBox settings_list;
@@ -35,58 +39,85 @@ public class Accounts.EditorEditPane : Gtk.Grid {
this.editor = editor;
this.account = account;
- PropertyRow nickname_row = new PropertyRow(
- account,
- "nickname",
- // Translators: This label in the account editor is for
- // the user's name for an account.
- _("Account name")
- );
- nickname_row.set_dim_label(true);
-
this.details_list.set_header_func(Editor.seperator_headers);
- this.details_list.add(nickname_row);
-
- this.addresses_list.set_header_func(Editor.seperator_headers);
- this.addresses_list.add(
- new AddressRow(account.primary_mailbox, get_login_session_name())
- );
+ this.details_list.add(new NicknameRow(account));
- string? default_name = account.primary_mailbox.name;
- if (Geary.String.is_empty_or_whitespace(default_name)) {
- default_name = null;
- }
- if (account.alternate_mailboxes != null) {
- foreach (Geary.RFC822.MailboxAddress alt
- in account.alternate_mailboxes) {
- this.addresses_list.add(new AddressRow(alt, default_name));
- }
+ this.senders_list.set_header_func(Editor.seperator_headers);
+ foreach (Geary.RFC822.MailboxAddress sender
+ in account.get_sender_mailboxes()) {
+ this.senders_list.add(new MailboxRow(account, sender));
}
-
- this.addresses_list.add(new AddRow());
+ this.senders_list.add(new AddMailboxRow(this));
this.signature_preview = new ClientWebView(
((GearyApplication) editor.application).config
);
- this.signature_preview.load_html(account.email_signature);
+ this.signature_preview.events = (
+ this.signature_preview.events | Gdk.EventType.FOCUS_CHANGE
+ );
+ this.signature_preview.content_loaded.connect(() => {
+ debug("Signature loaded");
+ // Only enable editability after the content has fully
+ // loaded to avoid the WebProcess crashing.
+ this.signature_preview.set_editable.begin(true, null);
+ });
+ this.signature_preview.document_modified.connect(() => {
+ debug("Signature changed");
+ this.signature_changed = true;
+ });
+ this.signature_preview.focus_in_event.connect(() => {
+ debug("Sig focus in");
+ return Gdk.EVENT_PROPAGATE;
+ });
+ this.signature_preview.focus_out_event.connect(() => {
+ debug("Sig focus out");
+ // This event will also be fired if the top-level
+ // window loses focus, e.g. if the user alt-tabs away,
+ // so don't execute the command if the signature web
+ // view no longer the focus widget
+ if (!this.signature_preview.is_focus &&
+ this.signature_changed) {
+ editor.commands.execute.begin(
+ new SignatureChangedCommand(
+ this.signature_preview, account
+ ),
+ null
+ );
+ }
+ return Gdk.EVENT_PROPAGATE;
+ });
+
this.signature_preview.show();
+ this.signature_preview.load_html(
+ Geary.HTML.smart_escape(account.email_signature)
+ );
- this.signature_scrolled.add(this.signature_preview);
+ this.signature_frame.add(this.signature_preview);
this.settings_list.set_header_func(Editor.seperator_headers);
- // No settings to show at the moment, so hide the list and its
- // frame until we do.
- this.settings_list.get_parent().hide();
+ this.settings_list.add(new EmailPrefetchRow(editor, this.account));
}
- private string? get_login_session_name() {
- string? name = Environment.get_real_name();
- if (Geary.String.is_empty(name) || name == "Unknown") {
- name = null;
+ internal string? get_default_name() {
+ string? name = account.primary_mailbox.name;
+
+ if (Geary.String.is_empty_or_whitespace(name)) {
+ name = Environment.get_real_name();
+ if (Geary.String.is_empty(name) || name == "Unknown") {
+ name = null;
+ }
}
+
return name;
}
+ [GtkCallback]
+ private void on_setting_activated(Gtk.ListBoxRow row) {
+ EditorRow? setting = row as EditorRow;
+ if (setting != null) {
+ setting.activated(this.editor);
+ }
+ }
[GtkCallback]
private void on_server_settings_clicked() {
@@ -97,79 +128,601 @@ public class Accounts.EditorEditPane : Gtk.Grid {
private void on_remove_account_clicked() {
this.editor.push(new EditorRemovePane(this.editor, this.account));
}
+
}
-private class Accounts.PropertyRow : LabelledEditorRow {
+private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
- private GLib.Object object;
- private string property_name;
+ public NicknameRow(Geary.AccountInformation account) {
+ base(
+ account,
+ // Translators: Label in the account editor for the user's
+ // custom name for an account.
+ _("Account name"),
+ new Gtk.Label("")
+ );
+ update();
+ }
+
+ public override void activated(Accounts.Editor editor) {
+ EditorPopover popover = new EditorPopover();
+
+ string? value = this.account.nickname;
+ Gtk.Entry entry = new Gtk.Entry();
+ entry.set_text(value ?? "");
+ entry.set_placeholder_text(value ?? "");
+ entry.set_width_chars(20);
+ entry.activate.connect(() => {
+ editor.commands.execute.begin(
+ new PropertyCommand<string>(
+ this.account,
+ this.account,
+ "nickname",
+ entry.get_text(),
+ // Translators: Tooltip used to undo changing
+ // the name of an account. The string
+ // substitution is the old name of the
+ // account.
+ _("Change account name back to “%s”")
+ ),
+ null
+ );
+ popover.popdown();
+ });
+ entry.show();
+
+ popover.add_labelled_row(
+ // Translators: Label used when editing the account's
+ // name.
+ _("Account name:"),
+ entry
+ );
+
+ popover.set_relative_to(this);
+ popover.layout.add(entry);
+ popover.popup();
+ }
+
+ public override void update() {
+ this.value.set_text(this.account.nickname);
+ }
+
+}
+
+
+private class Accounts.AddMailboxRow : AddRow {
+
+
+ private EditorEditPane edit_pane;
+
+
+ public AddMailboxRow(EditorEditPane edit_pane) {
+ this.edit_pane = edit_pane;
+
+ // Translators: Tooltip for adding a new email sender/from
+ // address's address to an account
+ this.set_tooltip_text(_("Add a new sender email address"));
+ }
+
+ public override void activated(Accounts.Editor editor) {
+ MailboxEditorPopover popover = new MailboxEditorPopover(
+ this.edit_pane.get_default_name() ?? "", "", false
+ );
+ popover.activated.connect(() => {
+ editor.commands.execute.begin(
+ new AppendMailboxCommand(
+ (Gtk.ListBox) get_parent(),
+ new MailboxRow(
+ this.edit_pane.account,
+ new Geary.RFC822.MailboxAddress(
+ popover.display_name,
+ popover.address
+ )
+ )
+ ),
+ null
+ );
+ popover.popdown();
+ });
+
+ popover.set_relative_to(this);
+ popover.popup();
+ }
+}
+
- private Gtk.Label value = new Gtk.Label("");
+private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
- public PropertyRow(Object object,
- string property_name,
- string label) {
- base(label);
+ internal Geary.RFC822.MailboxAddress mailbox;
- this.object = object;
- this.property_name = property_name;
- this.value.show();
- this.layout.add(this.value);
+ public MailboxRow(Geary.AccountInformation account,
+ Geary.RFC822.MailboxAddress mailbox) {
+ base(account, "", new Gtk.Label(""));
+ this.mailbox = mailbox;
update();
}
- public void update() {
- string? value = null;
- this.object.get(this.property_name, ref value);
+ public override void activated(Accounts.Editor editor) {
+ MailboxEditorPopover popover = new MailboxEditorPopover(
+ this.mailbox.name ?? "",
+ this.mailbox.address,
+ this.account.get_sender_mailboxes().size > 1
+ );
+ popover.activated.connect(() => {
+ editor.commands.execute.begin(
+ new UpdateMailboxCommand(
+ this,
+ new Geary.RFC822.MailboxAddress(
+ popover.display_name,
+ popover.address
+ )
+ ),
+ null
+ );
+ popover.popdown();
+ });
+ popover.remove_clicked.connect(() => {
+ editor.commands.execute.begin(
+ new RemoveMailboxCommand(this), null
+ );
+ popover.popdown();
+ });
+
+ popover.set_relative_to(this);
+ popover.popup();
+ }
- if (value != null) {
- this.value.set_text(value);
+ public override void update() {
+ string? name = this.mailbox.name;
+ if (Geary.String.is_empty_or_whitespace(name)) {
+ // Translators: Label used to indicate the user has
+ // provided no display name for one of their sender
+ // email addresses in their account settings.
+ name = _("Name not set");
+ set_dim_label(true);
+ } else {
+ set_dim_label(false);
}
+
+ this.label.set_text(name);
+ this.value.set_text(mailbox.address.strip());
}
}
+internal class Accounts.MailboxEditorPopover : EditorPopover {
-private class Accounts.AddressRow : LabelledEditorRow {
+ public string display_name { get; private set; }
+ public string address { get; private set; }
- private Geary.RFC822.MailboxAddress address;
- private string? fallback_name;
- private Gtk.Label value = new Gtk.Label("");
+ private Gtk.Entry name_entry = new Gtk.Entry();
+ private Gtk.Entry address_entry = new Gtk.Entry();
+ private Gtk.Button remove_button;
+ private bool is_valid = true;
+ private Geary.TimeoutManager validation_timeout;
- public AddressRow(Geary.RFC822.MailboxAddress address,
- string? fallback_name) {
- base("");
+ public signal void activated();
+ public signal void remove_clicked();
+
+
+ public MailboxEditorPopover(string? display_name,
+ string? address,
+ bool can_remove) {
+ this.display_name = display_name;
this.address = address;
- this.fallback_name = fallback_name;
- this.value.show();
- this.layout.add(this.value);
+ this.validation_timeout = new Geary.TimeoutManager.milliseconds(
+ 150, () => { validate(); }
+ );
- update();
+ this.name_entry.set_text(display_name ?? "");
+ this.name_entry.set_placeholder_text(
+ // Translators: This is used as a placeholder for the
+ // display name for an email address when editing a user's
+ // sender address preferences for an account.
+ _("Sender Name")
+ );
+ this.name_entry.set_width_chars(20);
+ this.name_entry.changed.connect(on_name_changed);
+ this.name_entry.activate.connect(on_activate);
+ this.name_entry.show();
+
+ this.address_entry.input_purpose = Gtk.InputPurpose.EMAIL;
+ this.address_entry.set_text(address ?? "");
+ this.address_entry.set_placeholder_text(
+ // Translators: This is used as a placeholder for the
+ // address part of an email address when editing a user's
+ // sender address preferences for an account.
+ _("person example com")
+ );
+ this.address_entry.set_width_chars(20);
+ this.address_entry.changed.connect(on_address_changed);
+ this.address_entry.activate.connect(on_activate);
+ this.address_entry.show();
+
+ this.remove_button = new Gtk.Button.with_label(_("Remove"));
+ this.remove_button.halign = Gtk.Align.END;
+ this.remove_button.get_style_context().add_class(
+ "geary-setting-remove"
+ );
+ this.remove_button.get_style_context().add_class(
+ Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION
+ );
+ this.remove_button.clicked.connect(on_remove_clicked);
+ this.remove_button.show();
+
+ add_labelled_row(
+ // Translators: Label used for the display name part of an
+ // email address when editing a user's sender address
+ // preferences for an account.
+ _("Sender name:"),
+ this.name_entry
+ );
+ add_labelled_row(
+ // Translators: Label used for the address part of an
+ // email address when editing a user's sender address
+ // preferences for an account.
+ _("Email address:"),
+ this.address_entry
+ );
+
+ if (can_remove) {
+ this.layout.attach(this.remove_button, 0, 2, 2, 1);
+ }
+
+ this.popup_focus = this.name_entry;
}
- public void update() {
- string? name = Geary.String.is_empty_or_whitespace(this.address.name)
- ? this.fallback_name
- : this.address.name;
+ ~MailboxEditorPopover() {
+ this.validation_timeout.reset();
- if (Geary.String.is_empty_or_whitespace(name)) {
- name = _("No name set");
- set_dim_label(true);
+ this.name_entry.changed.disconnect(on_name_changed);
+ this.name_entry.activate.disconnect(on_activate);
+
+ this.address_entry.changed.disconnect(on_address_changed);
+ this.address_entry.activate.disconnect(on_activate);
+
+ this.remove_button.clicked.disconnect(on_remove_clicked);
+ }
+
+ private void validate() {
+ Gtk.Entry entry = this.address_entry;
+ this.is_valid = Geary.RFC822.MailboxAddress.is_valid_address(
+ this.address
+ );
+ Gtk.StyleContext style = entry.get_style_context();
+ Gtk.EntryIconPosition pos = Gtk.EntryIconPosition.SECONDARY;
+ if (!this.is_valid) {
+ style.add_class(Gtk.STYLE_CLASS_ERROR);
+ entry.set_icon_from_icon_name(
+ pos, "dialog-error-symbolic"
+ );
+ entry.set_tooltip_text(
+ _("Email address is not valid, e.g. person example com")
+ );
} else {
- set_dim_label(false);
+ style.remove_class(Gtk.STYLE_CLASS_ERROR);
+ entry.set_icon_from_icon_name(pos, null);
+ entry.set_tooltip_text("");
}
+ }
- this.label.set_text(name);
- this.value.set_text(this.address.address.strip());
+ private void on_name_changed() {
+ this.display_name = this.name_entry.get_text().strip();
+ }
+
+ private void on_address_changed() {
+ this.address = this.address_entry.get_text().strip();
+ this.validation_timeout.start();
+ }
+
+ private void on_remove_clicked() {
+ remove_clicked();
+ }
+
+ private void on_activate() {
+ if (this.address != "" && this.is_valid) {
+ activated();
+ }
+ }
+
+}
+
+
+internal class Accounts.AppendMailboxCommand : Application.Command {
+
+
+ private Gtk.ListBox senders_list;
+ private MailboxRow new_row = null;
+
+ private int mailbox_index;
+
+
+ public AppendMailboxCommand(Gtk.ListBox senders_list, MailboxRow new_row) {
+ this.senders_list = senders_list;
+ this.new_row = new_row;
+
+ this.mailbox_index = new_row.account.get_sender_mailboxes().size;
+
+ // Translators: Label used as the undo tooltip after adding an
+ // new sender email address to an account. The string
+ // substitution is the email address added.
+ this.undo_label = _("Remove “%s”").printf(new_row.mailbox.address);
+ }
+
+ public async override void execute(GLib.Cancellable? cancellable) {
+ this.senders_list.insert(this.new_row, this.mailbox_index);
+ this.new_row.account.append_sender_mailbox(this.new_row.mailbox);
+ this.new_row.account.information_changed();
+ }
+
+ public async override void undo(GLib.Cancellable? cancellable) {
+ this.senders_list.remove(this.new_row);
+ this.new_row.account.remove_sender_mailbox(this.new_row.mailbox);
+ this.new_row.account.information_changed();
+ }
+
+}
+
+
+internal class Accounts.UpdateMailboxCommand : Application.Command {
+
+
+ private MailboxRow row;
+ private Geary.RFC822.MailboxAddress new_mailbox;
+
+ private Geary.RFC822.MailboxAddress old_mailbox;
+ private int mailbox_index;
+
+
+ public UpdateMailboxCommand(MailboxRow row,
+ Geary.RFC822.MailboxAddress new_mailbox) {
+ this.row = row;
+ this.new_mailbox = new_mailbox;
+
+ this.old_mailbox = row.mailbox;
+ this.mailbox_index =
+ row.account.get_sender_mailboxes().index_of(this.old_mailbox);
+
+ // Translators: Label used as the undo tooltip after editing a
+ // sender address for an account. The string substitution is
+ // the email address edited.
+ this.undo_label = _("Undo changes to “%s”").printf(
+ this.old_mailbox.address
+ );
+ }
+
+ public async override void execute(GLib.Cancellable? cancellable) {
+ this.row.mailbox = this.new_mailbox;
+ this.row.account.remove_sender_mailbox(this.old_mailbox);
+ this.row.account.insert_sender_mailbox(this.mailbox_index, this.new_mailbox);
+ this.row.account.information_changed();
+ }
+
+ public async override void undo(GLib.Cancellable? cancellable) {
+ this.row.mailbox = this.old_mailbox;
+ this.row.account.remove_sender_mailbox(this.new_mailbox);
+ this.row.account.insert_sender_mailbox(this.mailbox_index, this.old_mailbox);
+ this.row.account.information_changed();
+ }
+
+}
+
+
+internal class Accounts.RemoveMailboxCommand : Application.Command {
+
+
+ private MailboxRow row;
+
+ private Geary.RFC822.MailboxAddress mailbox;
+ private int mailbox_index;
+ private Gtk.ListBox list;
+
+
+ public RemoveMailboxCommand(MailboxRow row) {
+ this.row = row;
+
+ this.mailbox = row.mailbox;
+ this.mailbox_index =
+ row.account.get_sender_mailboxes().index_of(mailbox);
+ this.list = (Gtk.ListBox) row.get_parent();
+
+ // Translators: Label used as the undo tooltip after removing
+ // a sender address from an account. The string substitution
+ // is the email address edited.
+ this.undo_label = _("Add “%s” back").printf(this.mailbox.address);
+ }
+
+ public async override void execute(GLib.Cancellable? cancellable) {
+ this.list.remove(this.row);
+ this.row.account.remove_sender_mailbox(this.mailbox);
+ this.row.account.information_changed();
+ }
+
+ public async override void undo(GLib.Cancellable? cancellable) {
+ this.list.insert(this.row, this.mailbox_index);
+ this.row.account.insert_sender_mailbox(this.mailbox_index, this.mailbox);
+ this.row.account.information_changed();
+ }
+
+}
+
+
+internal class Accounts.SignatureChangedCommand : Application.Command {
+
+
+ private ClientWebView signature_view;
+ private Geary.AccountInformation account;
+
+ private string old_value;
+ private string? new_value = null;
+
+
+ public SignatureChangedCommand(ClientWebView signature_view,
+ Geary.AccountInformation account) {
+ this.signature_view = signature_view;
+ this.account = account;
+
+ this.old_value = Geary.HTML.smart_escape(account.email_signature);
+
+ // Translators: Label used as the undo tooltip after removing
+ // a sender address from an account. The string substitution
+ // is the email address edited.
+ this.undo_label = _("Undo signature changes");
+ }
+
+ public async override void execute(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ this.new_value = yield this.signature_view.get_html();
+ update_account_signature(this.new_value);
+ }
+
+ public async override void undo(GLib.Cancellable? cancellable) {
+ this.signature_view.load_html(this.old_value);
+ update_account_signature(this.old_value);
+ }
+
+ public async override void redo(GLib.Cancellable? cancellable) {
+ this.signature_view.load_html(this.new_value);
+ update_account_signature(this.new_value);
+ }
+
+ private inline void update_account_signature(string value) {
+ this.account.email_signature = value;
+ this.account.information_changed();
+ }
+
+}
+
+
+private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
+
+
+ private static bool row_separator(Gtk.TreeModel model, Gtk.TreeIter iter) {
+ GLib.Value v;
+ model.get_value(iter, 0, out v);
+ return v.get_string() == ".";
+ }
+
+
+ public EmailPrefetchRow(Accounts.Editor editor,
+ Geary.AccountInformation account) {
+ base(
+ account,
+ // Translators: This label describes the account
+ // preference for the length of time (weeks, months or
+ // years) that past email should be downloaded.
+ _("Download mail"),
+ new Gtk.ComboBoxText()
+ );
+ set_activatable(false);
+
+ this.value.set_row_separator_func(row_separator);
+
+ // Populate the model
+ get_label(14, true);
+ get_label(30, true);
+ get_label(90, true);
+ get_label(180, true);
+ get_label(365, true);
+ get_label(720, true);
+ get_label(1461, true);
+ get_label(-1, true);
+
+ // Update before connecting to the changed signal to avoid
+ // getting a spurious command.
+ update();
+
+ this.value.changed.connect(() => {
+ editor.commands.execute.begin(
+ new PropertyCommand<int>(
+ this.account,
+ this.account,
+ "prefetch-period-days",
+ int.parse(this.value.get_active_id()),
+ // Translators: Tooltip for undoing a change
+ // to the length of time that past email
+ // should be downloaded for an account. The
+ // string substitution is the duration,
+ // e.g. "1 month back".
+ _("Change download period back to: %s").printf(
+ get_label(this.account.prefetch_period_days)
+ )
+ ),
+ null
+ );
+ });
+ }
+
+ public override void update() {
+ string id = this.account.prefetch_period_days.to_string();
+ if (this.value.get_active_id() != id) {
+ this.value.set_active_id(id);
+ }
+ }
+
+ private string get_label(int duration, bool append = false) {
+ string label = "";
+ bool is_custom = false;
+ switch (duration) {
+ case -1:
+ label = _("Everything");
+ break;
+
+ case 14:
+ label = _("2 weeks back");
+ break;
+
+ case 30:
+ label = _("1 month back");
+ break;
+
+ case 90:
+ label = _("3 months back");
+ break;
+
+ case 180:
+ label = _("6 months back");
+ break;
+
+ case 365:
+ label = _("1 year back");
+ break;
+
+ case 720:
+ label = _("2 years back");
+ break;
+
+ case 1461:
+ label = _("4 years back");
+ break;
+
+ default:
+ is_custom = true;
+ label = GLib.ngettext(
+ "%d day back",
+ "%d days back",
+ duration
+ ).printf(duration);
+ break;
+ }
+
+ if (append) {
+ if (duration == -1 || is_custom) {
+ this.value.append(".", "."); // Separator
+ }
+ this.value.append(duration.to_string(), label);
+ }
+
+ return label;
}
}
diff --git a/src/client/accounts/accounts-editor-row.vala b/src/client/accounts/accounts-editor-row.vala
index 0b9159eb..38718b29 100644
--- a/src/client/accounts/accounts-editor-row.vala
+++ b/src/client/accounts/accounts-editor-row.vala
@@ -22,22 +22,35 @@ internal class Accounts.EditorRow : Gtk.ListBoxRow {
this.show();
}
+ public virtual void activated(Accounts.Editor editor) {
+ // No-op by default
+ }
+
}
-internal class Accounts.LabelledEditorRow : EditorRow {
+internal class Accounts.LabelledEditorRow<V> : EditorRow {
protected Gtk.Label label { get; private set; default = new Gtk.Label(""); }
+ protected V value;
- public LabelledEditorRow(string label) {
- this.label.set_text(label);
- this.label.set_hexpand(true);
+ public LabelledEditorRow(string label, V value) {
+ this.label.hexpand = true;
this.label.halign = Gtk.Align.START;
+ this.label.valign = Gtk.Align.CENTER;
+ this.label.set_text(label);
this.label.show();
-
this.layout.add(this.label);
+
+ this.value = value;
+ Gtk.Widget? widget = value as Gtk.Widget;
+ if (widget != null) {
+ widget.valign = Gtk.Align.CENTER;
+ widget.show();
+ this.layout.add(widget);
+ }
}
public void set_dim_label(bool is_dim) {
@@ -65,5 +78,185 @@ internal class Accounts.AddRow : EditorRow {
this.layout.add(add_icon);
}
+}
+
+
+internal abstract class Accounts.AccountRow<V> : LabelledEditorRow<V> {
+
+
+ internal Geary.AccountInformation account { get; private set; }
+
+
+ public AccountRow(Geary.AccountInformation account, string label, V value) {
+ base(label, value);
+ this.account = account;
+ this.account.information_changed.connect(on_account_changed);
+
+ set_dim_label(true);
+ }
+
+ ~AccountRow() {
+ this.account.information_changed.disconnect(on_account_changed);
+ }
+
+ public abstract void update();
+
+ private void on_account_changed() {
+ update();
+ }
+
+}
+
+
+private abstract class Accounts.ServiceRow<V> : AccountRow<V> {
+
+
+ internal Geary.ServiceInformation service { get; private set; }
+
+ protected virtual bool is_value_editable {
+ get {
+ return (
+ this.account.service_provider == Geary.ServiceProvider.OTHER &&
+ !this.is_goa_account
+ );
+ }
+ }
+
+ // XXX convenience method until we get a better way of doing this.
+ protected bool is_goa_account {
+ get { return (this.service.mediator is GoaMediator); }
+ }
+
+
+ public ServiceRow(Geary.AccountInformation account,
+ Geary.ServiceInformation service,
+ string label,
+ V value) {
+ base(account, label, value);
+ this.service = service;
+
+ bool is_editable = this.is_value_editable;
+ set_activatable(is_editable);
+
+ Gtk.Widget? widget = value as Gtk.Widget;
+ if (widget != null && !is_editable) {
+ if (widget is Gtk.Label) {
+ widget.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
+ } else {
+ widget.set_sensitive(false);
+ }
+ }
+ }
+
+}
+
+
+internal class Accounts.EditorPopover : Gtk.Popover {
+
+
+ internal Gtk.Grid layout { get; private set; default = new Gtk.Grid(); }
+
+ protected Gtk.Widget popup_focus = null;
+
+
+ public EditorPopover() {
+ get_style_context().add_class("geary-editor");
+
+ this.layout.orientation = Gtk.Orientation.VERTICAL;
+ this.layout.set_row_spacing(6);
+ this.layout.set_column_spacing(12);
+ this.layout.show();
+ add(this.layout);
+
+ this.closed.connect_after(on_closed);
+ }
+
+ ~EditorPopover() {
+ this.closed.disconnect(on_closed);
+ }
+
+ /** {@inheritdoc} */
+ public new void popup() {
+ // Work-around GTK+ issue #1138
+ Gtk.Widget target = get_relative_to();
+
+ Gtk.Allocation content_area;
+ target.get_allocation(out content_area);
+
+ Gtk.StyleContext style = target.get_style_context();
+ Gtk.StateFlags flags = style.get_state();
+ Gtk.Border margin = style.get_margin(flags);
+
+ content_area.x = margin.left;
+ content_area.y = margin.bottom;
+ content_area.width -= (content_area.x + margin.right);
+ content_area.height -= (content_area.y + margin.top);
+
+ set_pointing_to(content_area);
+
+ base.popup();
+
+ if (this.popup_focus != null) {
+ this.popup_focus.grab_focus();
+ }
+ }
+
+ public void add_labelled_row(string label, Gtk.Widget value) {
+ Gtk.Label label_widget = new Gtk.Label(label);
+ label_widget.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
+ label_widget.halign = Gtk.Align.END;
+ label_widget.show();
+
+ this.layout.add(label_widget);
+ this.layout.attach_next_to(value, label_widget, Gtk.PositionType.RIGHT);
+ }
+
+ private void on_closed() {
+ destroy();
+ }
+
+}
+
+
+internal class PropertyCommand<T> : Application.Command {
+
+
+ private Geary.AccountInformation account;
+ private GLib.Object object;
+ private string property_name;
+ private T? new_value;
+ private T? old_value;
+
+
+ public PropertyCommand(Geary.AccountInformation account,
+ GLib.Object object,
+ string property_name,
+ T? new_value,
+ string? undo_label = null,
+ string? redo_label = null,
+ string? executed_label = null,
+ string? undone_label = null) {
+ this.account = account;
+ this.object = object;
+ this.property_name = property_name;
+ this.new_value = new_value;
+
+ this.object.get(this.property_name, ref this.old_value);
+
+ this.undo_label = undo_label.printf(this.old_value);
+ this.redo_label = redo_label.printf(this.new_value);
+ this.executed_label = executed_label.printf(this.new_value);
+ this.undone_label = undone_label.printf(this.old_value);
+ }
+
+ public async override void execute(GLib.Cancellable? cancellable) {
+ this.object.set(this.property_name, this.new_value);
+ this.account.information_changed();
+ }
+
+ public async override void undo(GLib.Cancellable? cancellable) {
+ this.object.set(this.property_name, this.old_value);
+ this.account.information_changed();
+ }
}
diff --git a/src/client/accounts/accounts-editor-servers-pane.vala
b/src/client/accounts/accounts-editor-servers-pane.vala
index ebb4ec15..7a25d695 100644
--- a/src/client/accounts/accounts-editor-servers-pane.vala
+++ b/src/client/accounts/accounts-editor-servers-pane.vala
@@ -36,7 +36,6 @@ public class Accounts.EditorServersPane : Gtk.Grid {
if (this.account.imap.mediator is GoaMediator) {
this.details_list.add(new AccountProviderRow(this.account));
}
- this.details_list.add(new EmailPrefetchRow(this.account));
this.details_list.add(new SaveDraftsRow(this.account));
this.receiving_list.set_header_func(Editor.seperator_headers);
@@ -56,38 +55,7 @@ public class Accounts.EditorServersPane : Gtk.Grid {
}
-private abstract class Accounts.ServerAccountRow<V> : LabelledEditorRow {
-
-
- protected Geary.AccountInformation account;
-
- protected V value;
-
-
- public ServerAccountRow(Geary.AccountInformation account,
- string label,
- V value) {
- base(label);
- this.account = account;
-
- set_dim_label(true);
-
- this.value = value;
-
- Gtk.Widget? widget = value as Gtk.Widget;
- if (widget != null) {
- widget.valign = Gtk.Align.CENTER;
- widget.show();
- this.layout.add(widget);
- }
- }
-
- public abstract void update();
-
-}
-
-
-private class Accounts.ServiceProviderRow : ServerAccountRow<Gtk.Label> {
+private class Accounts.ServiceProviderRow : AccountRow<Gtk.Label> {
public ServiceProviderRow(Geary.AccountInformation account) {
@@ -100,6 +68,10 @@ private class Accounts.ServiceProviderRow : ServerAccountRow<Gtk.Label> {
new Gtk.Label("")
);
+ // Can't change this, so dim it out
+ this.value.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
+ set_activatable(false);
+
update();
}
@@ -119,14 +91,12 @@ private class Accounts.ServiceProviderRow : ServerAccountRow<Gtk.Label> {
break;
}
this.value.set_text(details);
- // Can't change this, so dim it out
- this.value.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
}
}
-private class Accounts.AccountProviderRow : ServerAccountRow<Gtk.Label> {
+private class Accounts.AccountProviderRow : AccountRow<Gtk.Label> {
public AccountProviderRow(Geary.AccountInformation account) {
@@ -139,6 +109,10 @@ private class Accounts.AccountProviderRow : ServerAccountRow<Gtk.Label> {
new Gtk.Label("")
);
+ // Can't change this, so dim it out
+ this.value.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
+ this.set_activatable(false);
+
update();
}
@@ -150,14 +124,12 @@ private class Accounts.AccountProviderRow : ServerAccountRow<Gtk.Label> {
source = _("Geary");
}
this.value.set_text(source);
- // Can't change this, so dim it out
- this.value.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
}
}
-private class Accounts.SaveDraftsRow : ServerAccountRow<Gtk.Switch> {
+private class Accounts.SaveDraftsRow : AccountRow<Gtk.Switch> {
public SaveDraftsRow(Geary.AccountInformation account) {
@@ -179,89 +151,7 @@ private class Accounts.SaveDraftsRow : ServerAccountRow<Gtk.Switch> {
}
-private class Accounts.EmailPrefetchRow : ServerAccountRow<Gtk.ComboBoxText> {
-
-
- private static bool row_separator(Gtk.TreeModel model, Gtk.TreeIter iter) {
- GLib.Value v;
- model.get_value(iter, 0, out v);
- return v.get_string() == ".";
- }
-
-
- public EmailPrefetchRow(Geary.AccountInformation account) {
- Gtk.ComboBoxText combo = new Gtk.ComboBoxText();
- combo.set_row_separator_func(row_separator);
- combo.append("14", _("2 weeks back")); // IDs are # of days
- combo.append("30", _("1 month back"));
- combo.append("90", _("3 months back"));
- combo.append("180", _("6 months back"));
- combo.append("365", _("1 year back"));
- combo.append("730", _("2 years back"));
- combo.append("1461", _("4 years back"));
- combo.append(".", "."); // Separator
- combo.append("-1", _("Everything"));
-
- base(
- account,
- // Translators: This label describes the account
- // preference for the length of time (weeks, months or
- // years) that past email should be downloaded.
- _("Download mail"),
- combo
- );
-
- update();
- }
-
- public override void update() {
- this.value.set_active_id(this.account.prefetch_period_days.to_string());
- }
-
-}
-
-
-private abstract class Accounts.ServerServiceRow<V> : ServerAccountRow<V> {
-
-
- protected Geary.ServiceInformation service;
-
- public virtual bool is_value_editable {
- get {
- return (
- this.account.service_provider == Geary.ServiceProvider.OTHER &&
- !this.is_goa_account
- );
- }
- }
-
- // XXX convenience method until we get a better way of doing this.
- protected bool is_goa_account {
- get { return (this.service.mediator is GoaMediator); }
- }
-
-
- public ServerServiceRow(Geary.AccountInformation account,
- Geary.ServiceInformation service,
- string label,
- V value) {
- base(account, label, value);
- this.service = service;
-
- Gtk.Widget? widget = value as Gtk.Widget;
- if (widget != null && !this.is_value_editable) {
- if (widget is Gtk.Label) {
- widget.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL);
- } else {
- widget.set_sensitive(false);
- }
- }
- }
-
-}
-
-
-private class Accounts.ServiceHostRow : ServerServiceRow<Gtk.Label> {
+private class Accounts.ServiceHostRow : ServiceRow<Gtk.Label> {
public ServiceHostRow(Geary.AccountInformation account,
Geary.ServiceInformation service) {
@@ -301,8 +191,7 @@ private class Accounts.ServiceHostRow : ServerServiceRow<Gtk.Label> {
}
-private class Accounts.ServiceSecurityRow :
- ServerServiceRow<Gtk.ComboBoxText> {
+private class Accounts.ServiceSecurityRow : ServiceRow<Gtk.ComboBoxText> {
private const string INSECURE_ICON = "channel-insecure-symbolic";
private const string SECURE_ICON = "channel-secure-symbolic";
@@ -357,7 +246,7 @@ private class Accounts.ServiceSecurityRow :
}
-private class Accounts.ServiceAuthRow : ServerServiceRow<Gtk.Label> {
+private class Accounts.ServiceAuthRow : ServiceRow<Gtk.Label> {
public ServiceAuthRow(Geary.AccountInformation account,
Geary.ServiceInformation service) {
diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala
index b4a5efe4..48102c6b 100644
--- a/src/client/accounts/accounts-editor.vala
+++ b/src/client/accounts/accounts-editor.vala
@@ -12,6 +12,12 @@
public class Accounts.Editor : Gtk.Dialog {
+ private const ActionEntry[] ACTION_ENTRIES = {
+ { GearyController.ACTION_REDO, on_redo },
+ { GearyController.ACTION_UNDO, on_undo },
+ };
+
+
internal static void seperator_headers(Gtk.ListBoxRow row,
Gtk.ListBoxRow? first) {
if (first == null) {
@@ -22,8 +28,8 @@ public class Accounts.Editor : Gtk.Dialog {
}
private static int ordinal_sort(Gtk.ListBoxRow a, Gtk.ListBoxRow b) {
- AccountRow? account_a = a as AccountRow;
- AccountRow? account_b = b as AccountRow;
+ AccountListRow? account_a = a as AccountListRow;
+ AccountListRow? account_b = b as AccountListRow;
if (account_a == null) {
return (account_b == null) ? 0 : 1;
@@ -37,17 +43,20 @@ public class Accounts.Editor : Gtk.Dialog {
}
- /**
- * The current application instance.
- *
- * Note this hides the {@link GtkWindow.application} property
- * since we don't want the application to know about this dialog -
- * it should not prevent the app from closing.
- */
- internal new GearyApplication application { get; private set; }
+ /** The command stack for this pane. */
+ internal Application.CommandStack commands {
+ get; private set; default = new Application.CommandStack();
+ }
+
+ /** The current account being edited, if any. */
+ private Geary.AccountInformation selected_account {
+ get; private set; default = null;
+ }
private AccountManager accounts;
+ private SimpleActionGroup actions = new SimpleActionGroup();
+
[GtkChild]
private Gtk.HeaderBar default_header;
@@ -57,6 +66,9 @@ public class Accounts.Editor : Gtk.Dialog {
[GtkChild]
private Gtk.Button back_button;
+ [GtkChild]
+ private Gtk.Button undo_button;
+
[GtkChild]
private Gtk.Grid list_pane;
@@ -67,14 +79,17 @@ public class Accounts.Editor : Gtk.Dialog {
new Gee.LinkedList<Gtk.Widget>();
-
public Editor(GearyApplication application, Gtk.Window parent) {
this.application = application;
this.accounts = application.controller.account_manager;
+ this.actions.add_action_entries(ACTION_ENTRIES, this);
+ insert_action_group("win", this.actions);
+
set_titlebar(this.default_header);
set_transient_for(parent);
- set_modal(true);
+ //set_modal(true);
+ set_modal(false);
// XXX Glade 3.22 won't let us set this
get_content_area().border_width = 2;
@@ -90,12 +105,23 @@ public class Accounts.Editor : Gtk.Dialog {
this.accounts_list.add(new AddRow());
- accounts.account_added.connect(on_account_added);
- accounts.account_status_changed.connect(on_account_status_changed);
- accounts.account_removed.connect(on_account_removed);
+ this.accounts.account_added.connect(on_account_added);
+ this.accounts.account_status_changed.connect(on_account_status_changed);
+ this.accounts.account_removed.connect(on_account_removed);
+
+ this.commands.executed.connect(on_command);
+ this.commands.undone.connect(on_command);
+ this.commands.redone.connect(on_command);
+
+ get_action(GearyController.ACTION_UNDO).set_enabled(false);
+ get_action(GearyController.ACTION_REDO).set_enabled(false);
}
~Editor() {
+ this.commands.executed.disconnect(on_command);
+ this.commands.undone.disconnect(on_command);
+ this.commands.redone.disconnect(on_command);
+
this.accounts.account_added.disconnect(on_account_added);
this.accounts.account_status_changed.disconnect(on_account_status_changed);
this.accounts.account_removed.disconnect(on_account_removed);
@@ -117,10 +143,11 @@ public class Accounts.Editor : Gtk.Dialog {
this.editor_panes.add(child);
this.editor_panes.set_visible_child(child);
this.back_button.show();
+ this.undo_button.show();
}
internal void pop() {
- // We can't simply remove old panes fro the GTK stack since
+ // One can't simply remove old panes fro the GTK stack since
// there won't be any transition between them - the old one
// will simply disappear. So we need to keep old, popped panes
// around until a new one is pushed on.
@@ -129,27 +156,35 @@ public class Accounts.Editor : Gtk.Dialog {
// them?
Gtk.Widget current = this.editor_panes.get_visible_child();
int next = this.editor_pane_stack.index_of(current) - 1;
-
+
this.editor_panes.set_visible_child(this.editor_pane_stack.get(next));
+ // Don't carry commands over from one pane to another
+ this.commands.clear();
+ get_action(GearyController.ACTION_UNDO).set_enabled(false);
+ get_action(GearyController.ACTION_REDO).set_enabled(false);
+
if (next == 0) {
+ this.selected_account = null;
this.back_button.hide();
+ this.undo_button.hide();
}
}
private void add_account(Geary.AccountInformation account,
AccountManager.Status status) {
- this.accounts_list.add(new AccountRow(account, status));
+ this.accounts_list.add(new AccountListRow(account, status));
}
private void show_account(Geary.AccountInformation account) {
+ this.selected_account = account;
push(new EditorEditPane(this, account));
}
- private AccountRow? get_account_row(Geary.AccountInformation account) {
- AccountRow? row = null;
+ private AccountListRow? get_account_row(Geary.AccountInformation account) {
+ AccountListRow? row = null;
this.accounts_list.foreach((child) => {
- AccountRow? account_row = child as AccountRow;
+ AccountListRow? account_row = child as AccountListRow;
if (account_row != null && account_row.account == account) {
row = account_row;
}
@@ -157,6 +192,10 @@ public class Accounts.Editor : Gtk.Dialog {
return row;
}
+ private inline GLib.SimpleAction get_action(string name) {
+ return (GLib.SimpleAction) this.actions.lookup_action(name);
+ }
+
private void on_account_added(Geary.AccountInformation account,
AccountManager.Status status) {
add_account(account, status);
@@ -164,22 +203,51 @@ public class Accounts.Editor : Gtk.Dialog {
private void on_account_status_changed(Geary.AccountInformation account,
AccountManager.Status status) {
- AccountRow? row = get_account_row(account);
+ AccountListRow? row = get_account_row(account);
if (row != null) {
row.update(status);
}
}
private void on_account_removed(Geary.AccountInformation account) {
- AccountRow? row = get_account_row(account);
+ AccountListRow? row = get_account_row(account);
if (row != null) {
this.accounts_list.remove(row);
}
+
+ if (this.selected_account == account) {
+ while (this.editor_panes.get_visible_child() != this.list_pane) {
+ pop();
+ }
+ }
+ }
+
+ private void on_undo() {
+ this.commands.undo.begin(null);
+ }
+
+ private void on_redo() {
+ this.commands.redo.begin(null);
+ }
+
+ private void on_command() {
+ get_action(GearyController.ACTION_UNDO).set_enabled(
+ this.commands.can_undo
+ );
+ get_action(GearyController.ACTION_REDO).set_enabled(
+ this.commands.can_redo
+ );
+
+ Application.Command next_undo = this.commands.peek_undo();
+ this.undo_button.set_tooltip_text(
+ (next_undo != null && next_undo.undo_label != null)
+ ? next_undo.undo_label : ""
+ );
}
[GtkCallback]
private void on_accounts_list_row_activated(Gtk.ListBoxRow activated) {
- AccountRow? row = activated as AccountRow;
+ AccountListRow? row = activated as AccountListRow;
if (row != null) {
show_account(row.account);
}
@@ -192,7 +260,7 @@ public class Accounts.Editor : Gtk.Dialog {
}
-private class Accounts.AccountRow : EditorRow {
+private class Accounts.AccountListRow : EditorRow {
internal Geary.AccountInformation account;
@@ -204,8 +272,8 @@ private class Accounts.AccountRow : EditorRow {
private Gtk.Label account_details = new Gtk.Label("");
- public AccountRow(Geary.AccountInformation account,
- AccountManager.Status status) {
+ public AccountListRow(Geary.AccountInformation account,
+ AccountManager.Status status) {
this.account = account;
this.account_name.show();
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 3e6c8fcb..61fdad7b 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -32,6 +32,7 @@ public class GearyController : Geary.BaseObject {
public const string ACTION_EMPTY_SPAM = "empty-spam";
public const string ACTION_EMPTY_TRASH = "empty-trash";
public const string ACTION_UNDO = "undo";
+ public const string ACTION_REDO = "redo";
public const string ACTION_FIND_IN_CONVERSATION = "conv-find";
public const string ACTION_ZOOM = "zoom";
public const string ACTION_SHOW_MARK_MENU = "mark-message-menu";
@@ -566,6 +567,7 @@ public class GearyController : Geary.BaseObject {
add_window_accelerators(ACTION_TRASH_CONVERSATION, { "Delete", "BackSpace" });
add_window_accelerators(ACTION_DELETE_CONVERSATION, { "<Shift>Delete", "<Shift>BackSpace" });
add_window_accelerators(ACTION_UNDO, { "<Ctrl>Z" });
+ add_window_accelerators(ACTION_REDO, { "<Ctrl><Shift>Z" });
add_window_accelerators(ACTION_ZOOM+("('in')"), { "<Ctrl>equal", "equal" });
add_window_accelerators(ACTION_ZOOM+("('out')"), { "<Ctrl>minus", "minus" });
add_window_accelerators(ACTION_ZOOM+("('normal')"), { "<Ctrl>0", "0" });
diff --git a/src/engine/api/geary-account-information.vala b/src/engine/api/geary-account-information.vala
index 7efab6d7..50e6a9ff 100644
--- a/src/engine/api/geary-account-information.vala
+++ b/src/engine/api/geary-account-information.vala
@@ -267,6 +267,81 @@ public class Geary.AccountInformation : BaseObject {
this.data_dir = data;
}
+ /**
+ * Return a read only, ordered list of the account's sender mailboxes.
+ */
+ public Gee.List<Geary.RFC822.MailboxAddress> get_sender_mailboxes() {
+ Gee.List<RFC822.MailboxAddress> all =
+ new Gee.LinkedList<RFC822.MailboxAddress>();
+
+ all.add(this.primary_mailbox);
+ if (alternate_mailboxes != null) {
+ all.add_all(alternate_mailboxes);
+ }
+
+ return all.read_only_view;
+ }
+
+ /**
+ * Appends a sender mailbox to the account.
+ */
+ public void append_sender_mailbox(Geary.RFC822.MailboxAddress mailbox) {
+ if (this.primary_mailbox == null) {
+ this.primary_mailbox = mailbox;
+ } else {
+ if (this.alternate_mailboxes == null) {
+ this.alternate_mailboxes =
+ new Gee.LinkedList<Geary.RFC822.MailboxAddress>();
+ }
+ this.alternate_mailboxes.add(mailbox);
+ }
+ }
+
+ /**
+ * Appends a sender mailbox to the account.
+ */
+ public void insert_sender_mailbox(int index,
+ Geary.RFC822.MailboxAddress mailbox) {
+ Geary.RFC822.MailboxAddress? alt_insertion = null;
+ int actual_index = index;
+ if (actual_index == 0) {
+ if (this.primary_mailbox == null) {
+ this.primary_mailbox = mailbox;
+ } else {
+ this.primary_mailbox = mailbox;
+ alt_insertion = this.primary_mailbox;
+ actual_index = 0;
+ }
+ } else {
+ alt_insertion = mailbox;
+ actual_index--;
+ }
+
+ if (alt_insertion != null) {
+ if (this.alternate_mailboxes == null) {
+ this.alternate_mailboxes =
+ new Gee.LinkedList<Geary.RFC822.MailboxAddress>();
+ }
+ this.alternate_mailboxes.insert(actual_index, alt_insertion);
+ }
+ }
+
+ /**
+ * Removes a sender mailbox for the account.
+ */
+ public void remove_sender_mailbox(Geary.RFC822.MailboxAddress mailbox) {
+ if (this.primary_mailbox == mailbox) {
+ this.primary_mailbox = (
+ this.alternate_mailboxes != null &&
+ !this.alternate_mailboxes.is_empty
+ ) ? this.alternate_mailboxes.remove_at(0) : null;
+ } else if (this.alternate_mailboxes != null) {
+ this.alternate_mailboxes.remove_at(
+ this.alternate_mailboxes.index_of(mailbox)
+ );
+ }
+ }
+
/**
* Return a list of the primary and all alternate email addresses.
*/
diff --git a/ui/accounts_editor.ui b/ui/accounts_editor.ui
index e15b5fd9..f957839e 100644
--- a/ui/accounts_editor.ui
+++ b/ui/accounts_editor.ui
@@ -101,14 +101,14 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
+ <property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
- <child>
+ <child type="titlebar">
<placeholder/>
</child>
<style>
@@ -146,5 +146,34 @@
</child>
</object>
</child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="undo_button">
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.undo</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="icon_name">edit-undo-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
</interface>
diff --git a/ui/accounts_editor_edit_pane.ui b/ui/accounts_editor_edit_pane.ui
index 0b2e817d..e38c0fa0 100644
--- a/ui/accounts_editor_edit_pane.ui
+++ b/ui/accounts_editor_edit_pane.ui
@@ -3,7 +3,6 @@
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="AccountsEditorEditPane" parent="GtkGrid">
- <property name="name">1</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
@@ -34,6 +33,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
+ <signal name="row-activated" handler="on_setting_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
@@ -68,10 +68,11 @@
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
- <object class="GtkListBox" id="addresses_list">
+ <object class="GtkListBox" id="senders_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
+ <signal name="row-activated" handler="on_setting_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
@@ -98,6 +99,43 @@
<property name="top_attach">3</property>
</packing>
</child>
+ <child>
+ <object class="GtkFrame" id="signature_frame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ <style>
+ <class name="geary-settings"/>
+ <class name="geary-signature"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Settings</property>
+ <style>
+ <class name="geary-settings-heading"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
@@ -110,6 +148,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
+ <signal name="row-activated" handler="on_setting_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
@@ -118,7 +157,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">5</property>
+ <property name="top_attach">6</property>
</packing>
</child>
<child>
@@ -167,35 +206,7 @@
</object>
<packing>
<property name="left_attach">0</property>
- <property name="top_attach">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkScrolledWindow" id="signature_scrolled">
- <property name="height_request">80</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="vexpand">True</property>
- <property name="vscrollbar_policy">never</property>
- <property name="min_content_height">40</property>
- <child>
- <placeholder/>
- </child>
- </object>
- </child>
- <child type="label_item">
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">4</property>
+ <property name="top_attach">7</property>
</packing>
</child>
<style>
diff --git a/ui/client-web-view.js b/ui/client-web-view.js
index c2a9d24a..76d4456e 100644
--- a/ui/client-web-view.js
+++ b/ui/client-web-view.js
@@ -172,3 +172,5 @@ PageState.prototype = {
}
}
};
+
+var geary = new PageState();
diff --git a/ui/geary.css b/ui/geary.css
index 359be70b..2055b4e4 100644
--- a/ui/geary.css
+++ b/ui/geary.css
@@ -230,6 +230,11 @@ row.geary-settings > grid > image:dir(rtl) {
margin-left: 6px;
}
+frame.geary-settings.geary-signature {
+ min-height: 5em;
+}
+
+
row.geary-settings > grid > combobox,
row.geary-settings:not(.geary-add-row) > grid > image,
row.geary-settings > grid > switch {
@@ -242,3 +247,11 @@ row.geary-settings > grid > switch {
buttonbox.geary-settings {
margin-top: 36px;
}
+
+popover.geary-editor > grid {
+ margin: 6px;
+}
+
+popover.geary-editor > grid > button.geary-setting-remove {
+ margin-top: 6px;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]