Introduce some common interfaces for account editor panes.

This allows the panes themselves to manage their own headers, command
stacks, and so on. Break out the account list pane out so it's handled in
the same way as the others.
This commit is contained in:
Michael James Gratton 2018-06-14 22:27:45 +10:00
parent d9b94c1c61
commit f754d3df82
14 changed files with 713 additions and 482 deletions

View file

@ -19,6 +19,7 @@ src/client/accounts/account-manager.vala
src/client/accounts/account-spinner-page.vala
src/client/accounts/accounts-editor.vala
src/client/accounts/accounts-editor-edit-pane.vala
src/client/accounts/accounts-editor-list-pane.vala
src/client/accounts/accounts-editor-remove-pane.vala
src/client/accounts/accounts-editor-row.vala
src/client/accounts/accounts-editor-servers-pane.vala
@ -412,8 +413,8 @@ src/mailer/main.vala
ui/account_cannot_remove.glade
ui/account_list.glade
ui/account_spinner.glade
ui/accounts_editor.ui
ui/accounts_editor_edit_pane.ui
ui/accounts_editor_list_pane.ui
ui/accounts_editor_remove_pane.ui
ui/accounts_editor_servers_pane.ui
ui/certificate_warning_dialog.glade

View file

@ -6,17 +6,23 @@
*/
/**
* The main account editor window.
* An account editor pane for editing a specific account's preferences.
*/
[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_edit_pane.ui")]
public class Accounts.EditorEditPane : Gtk.Grid {
internal class Accounts.EditorEditPane : Gtk.Grid, EditorPane, AccountPane {
/** The editor this pane belongs to. */
internal weak Editor editor; // circular ref
internal Geary.AccountInformation account { get ; protected set; }
/** The account being displayed by this pane. */
internal Geary.AccountInformation account;
/** Command stack for edit pane user commands. */
internal Application.CommandStack commands {
get; private set; default = new Application.CommandStack();
}
protected weak Accounts.Editor editor { get; set; }
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.ListBox details_list;
@ -33,9 +39,11 @@ public class Accounts.EditorEditPane : Gtk.Grid {
[GtkChild]
private Gtk.ListBox settings_list;
[GtkChild]
private Gtk.Button undo_button;
public EditorEditPane(Editor editor,
Geary.AccountInformation account) {
public EditorEditPane(Editor editor, Geary.AccountInformation account) {
this.editor = editor;
this.account = account;
@ -47,7 +55,7 @@ public class Accounts.EditorEditPane : Gtk.Grid {
in account.get_sender_mailboxes()) {
this.senders_list.add(new MailboxRow(account, sender));
}
this.senders_list.add(new AddMailboxRow(this));
this.senders_list.add(new AddMailboxRow());
this.signature_preview = new ClientWebView(
((GearyApplication) editor.application).config
@ -77,7 +85,7 @@ public class Accounts.EditorEditPane : Gtk.Grid {
// view no longer the focus widget
if (!this.signature_preview.is_focus &&
this.signature_changed) {
editor.commands.execute.begin(
this.commands.execute.begin(
new SignatureChangedCommand(
this.signature_preview, account
),
@ -95,7 +103,22 @@ public class Accounts.EditorEditPane : Gtk.Grid {
this.signature_frame.add(this.signature_preview);
this.settings_list.set_header_func(Editor.seperator_headers);
this.settings_list.add(new EmailPrefetchRow(editor, this.account));
this.settings_list.add(new EmailPrefetchRow(this));
this.account.information_changed.connect(on_account_changed);
update_header();
this.commands.executed.connect(on_command);
this.commands.undone.connect(on_command);
this.commands.redone.connect(on_command);
}
~EditorEditPane() {
this.account.information_changed.disconnect(on_account_changed);
this.commands.executed.disconnect(on_command);
this.commands.undone.disconnect(on_command);
this.commands.redone.disconnect(on_command);
}
internal string? get_default_name() {
@ -111,11 +134,50 @@ public class Accounts.EditorEditPane : Gtk.Grid {
return name;
}
internal Gtk.HeaderBar get_header() {
return this.header;
}
internal void pane_shown() {
update_actions();
}
internal void undo() {
this.commands.undo.begin(null);
}
internal void redo() {
this.commands.redo.begin(null);
}
private void update_actions() {
this.editor.get_action(GearyController.ACTION_UNDO).set_enabled(
this.commands.can_undo
);
this.editor.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 : ""
);
}
private void on_account_changed() {
update_header();
}
private void on_command() {
update_actions();
}
[GtkCallback]
private void on_setting_activated(Gtk.ListBoxRow row) {
EditorRow? setting = row as EditorRow;
EditorRow<EditorEditPane>? setting = row as EditorRow<EditorEditPane>;
if (setting != null) {
setting.activated(this.editor);
setting.activated(this);
}
}
@ -129,10 +191,15 @@ public class Accounts.EditorEditPane : Gtk.Grid {
this.editor.push(new EditorRemovePane(this.editor, this.account));
}
[GtkCallback]
private void on_back_button_clicked() {
this.editor.pop();
}
}
private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
private class Accounts.NicknameRow : AccountRow<EditorEditPane,Gtk.Label> {
public NicknameRow(Geary.AccountInformation account) {
@ -146,7 +213,7 @@ private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
update();
}
public override void activated(Accounts.Editor editor) {
public override void activated(EditorEditPane pane) {
EditorPopover popover = new EditorPopover();
string? value = this.account.nickname;
@ -155,11 +222,11 @@ private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
entry.set_placeholder_text(value ?? "");
entry.set_width_chars(20);
entry.activate.connect(() => {
editor.commands.execute.begin(
pane.commands.execute.begin(
new PropertyCommand<string>(
this.account,
this.account,
"nickname",
Geary.AccountInformation.PROP_NICKNAME,
entry.get_text(),
// Translators: Tooltip used to undo changing
// the name of an account. The string
@ -192,30 +259,25 @@ private class Accounts.NicknameRow : AccountRow<Gtk.Label> {
}
private class Accounts.AddMailboxRow : AddRow {
private class Accounts.AddMailboxRow : AddRow<EditorEditPane> {
private EditorEditPane edit_pane;
public AddMailboxRow(EditorEditPane edit_pane) {
this.edit_pane = edit_pane;
public AddMailboxRow() {
// 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) {
public override void activated(EditorEditPane pane) {
MailboxEditorPopover popover = new MailboxEditorPopover(
this.edit_pane.get_default_name() ?? "", "", false
pane.get_default_name() ?? "", "", false
);
popover.activated.connect(() => {
editor.commands.execute.begin(
pane.commands.execute.begin(
new AppendMailboxCommand(
(Gtk.ListBox) get_parent(),
new MailboxRow(
this.edit_pane.account,
pane.account,
new Geary.RFC822.MailboxAddress(
popover.display_name,
popover.address
@ -233,7 +295,7 @@ private class Accounts.AddMailboxRow : AddRow {
}
private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
private class Accounts.MailboxRow : AccountRow<EditorEditPane,Gtk.Label> {
internal Geary.RFC822.MailboxAddress mailbox;
@ -247,14 +309,14 @@ private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
update();
}
public override void activated(Accounts.Editor editor) {
public override void activated(EditorEditPane pane) {
MailboxEditorPopover popover = new MailboxEditorPopover(
this.mailbox.name ?? "",
this.mailbox.address,
this.account.get_sender_mailboxes().size > 1
);
popover.activated.connect(() => {
editor.commands.execute.begin(
pane.commands.execute.begin(
new UpdateMailboxCommand(
this,
new Geary.RFC822.MailboxAddress(
@ -267,7 +329,7 @@ private class Accounts.MailboxRow : AccountRow<Gtk.Label> {
popover.popdown();
});
popover.remove_clicked.connect(() => {
editor.commands.execute.begin(
pane.commands.execute.begin(
new RemoveMailboxCommand(this), null
);
popover.popdown();
@ -603,7 +665,8 @@ internal class Accounts.SignatureChangedCommand : Application.Command {
}
private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
private class Accounts.EmailPrefetchRow :
AccountRow<EditorEditPane,Gtk.ComboBoxText> {
private static bool row_separator(Gtk.TreeModel model, Gtk.TreeIter iter) {
@ -613,10 +676,9 @@ private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
}
public EmailPrefetchRow(Accounts.Editor editor,
Geary.AccountInformation account) {
public EmailPrefetchRow(EditorEditPane pane) {
base(
account,
pane.account,
// Translators: This label describes the account
// preference for the length of time (weeks, months or
// years) that past email should be downloaded.
@ -642,7 +704,7 @@ private class Accounts.EmailPrefetchRow : AccountRow<Gtk.ComboBoxText> {
update();
this.value.changed.connect(() => {
editor.commands.execute.begin(
pane.commands.execute.begin(
new PropertyCommand<int>(
this.account,
this.account,

View file

@ -0,0 +1,215 @@
/*
* Copyright 2018 Michael Gratton <mike@vee.net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* An account editor pane for listing all known accounts.
*/
[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_list_pane.ui")]
internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
private static int ordinal_sort(Gtk.ListBoxRow a, Gtk.ListBoxRow b) {
AccountListRow? account_a = a as AccountListRow;
AccountListRow? account_b = b as AccountListRow;
if (account_a == null) {
return (account_b == null) ? 0 : 1;
} else if (account_b == null) {
return -1;
}
return Geary.AccountInformation.compare_ascending(
account_a.account, account_b.account
);
}
protected weak Accounts.Editor editor { get; set; }
private AccountManager accounts { get; private set; }
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.ListBox accounts_list;
private Gee.Map<Geary.AccountInformation,EditorEditPane> edit_pane_cache =
new Gee.HashMap<Geary.AccountInformation,EditorEditPane>();
public EditorListPane(Editor editor) {
this.editor = editor;
this.accounts =
((GearyApplication) editor.application).controller.account_manager;
this.accounts_list.set_header_func(Editor.seperator_headers);
this.accounts_list.set_sort_func(ordinal_sort);
foreach (Geary.AccountInformation account in this.accounts.iterable()) {
add_account(account, this.accounts.get_status(account));
}
this.accounts_list.add(new AddRow<EditorServersPane>());
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);
}
public override void destroy() {
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);
this.edit_pane_cache.clear();
base.destroy();
}
/** Adds a new account to the list. */
internal void add_account(Geary.AccountInformation account,
AccountManager.Status status) {
this.accounts_list.add(new AccountListRow(account, status));
}
/** Removes an account from the list. */
internal void remove_account(Geary.AccountInformation account) {
AccountListRow? row = get_account_row(account);
if (row != null) {
this.accounts_list.remove(row);
}
}
internal Gtk.HeaderBar get_header() {
return this.header;
}
private AccountListRow? get_account_row(Geary.AccountInformation account) {
AccountListRow? row = null;
this.accounts_list.foreach((child) => {
AccountListRow? account_row = child as AccountListRow;
if (account_row != null && account_row.account == account) {
row = account_row;
}
});
return row;
}
private void on_account_added(Geary.AccountInformation account,
AccountManager.Status status) {
add_account(account, status);
}
private void on_account_status_changed(Geary.AccountInformation account,
AccountManager.Status status) {
AccountListRow? row = get_account_row(account);
if (row != null) {
row.update(status);
}
}
private void on_account_removed(Geary.AccountInformation account) {
remove_account(account);
}
[GtkCallback]
private void on_accounts_list_row_activated(Gtk.ListBoxRow activated) {
AccountListRow? row = activated as AccountListRow;
if (row != null) {
Geary.AccountInformation account = row.account;
EditorEditPane? edit_pane = this.edit_pane_cache.get(account);
if (edit_pane == null) {
edit_pane = new EditorEditPane(this.editor, account);
this.edit_pane_cache.set(account, edit_pane);
}
this.editor.push(edit_pane);
}
}
}
private class Accounts.AccountListRow : EditorRow<EditorListPane> {
internal Geary.AccountInformation account;
private Gtk.Image unavailable_icon = new Gtk.Image.from_icon_name(
"dialog-warning-symbolic", Gtk.IconSize.BUTTON
);
private Gtk.Label account_name = new Gtk.Label("");
private Gtk.Label account_details = new Gtk.Label("");
public AccountListRow(Geary.AccountInformation account,
AccountManager.Status status) {
this.account = account;
this.account_name.show();
this.account_name.set_hexpand(true);
this.account_name.halign = Gtk.Align.START;
this.account_details.show();
this.layout.add(this.unavailable_icon);
this.layout.add(this.account_name);
this.layout.add(this.account_details);
update(status);
}
public void update(AccountManager.Status status) {
if (status != AccountManager.Status.UNAVAILABLE) {
this.unavailable_icon.hide();
this.set_tooltip_text("");
} else {
this.unavailable_icon.show();
this.set_tooltip_text(
_("This account has encountered a problem and is unavailable")
);
}
string name = this.account.nickname;
if (Geary.String.is_empty(name)) {
name = account.primary_mailbox.to_address_display("", "");
}
this.account_name.set_text(name);
string? details = this.account.service_label;
switch (account.service_provider) {
case Geary.ServiceProvider.GMAIL:
details = _("GMail");
break;
case Geary.ServiceProvider.OUTLOOK:
details = _("Outlook.com");
break;
case Geary.ServiceProvider.YAHOO:
details = _("Yahoo");
break;
}
this.account_details.set_text(details);
if (status == AccountManager.Status.ENABLED) {
this.account_name.get_style_context().remove_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
this.account_details.get_style_context().remove_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
} else {
this.account_name.get_style_context().add_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
this.account_details.get_style_context().add_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
}
}
}

View file

@ -6,14 +6,18 @@
*/
/**
* The main account editor window.
* An account editor pane for removing an account from the client.
*/
[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_remove_pane.ui")]
public class Accounts.EditorRemovePane : Gtk.Grid {
internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
private weak Editor editor; // circular ref
private Geary.AccountInformation account;
internal Geary.AccountInformation account { get ; protected set; }
protected weak Accounts.Editor editor { get; set; }
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.Stack confirm_stack;
@ -41,6 +45,21 @@ public class Accounts.EditorRemovePane : Gtk.Grid {
this.remove_label.set_text(
this.remove_label.get_text().printf(account.nickname)
);
this.account.information_changed.connect(on_account_changed);
update_header();
}
~EditorRemovePane() {
this.account.information_changed.disconnect(on_account_changed);
}
internal Gtk.HeaderBar get_header() {
return this.header;
}
private void on_account_changed() {
update_header();
}
[GtkCallback]
@ -50,4 +69,9 @@ public class Accounts.EditorRemovePane : Gtk.Grid {
this.confirm_stack.set_visible_child_name("remove");
}
[GtkCallback]
private void on_back_button_clicked() {
this.editor.pop();
}
}

View file

@ -6,7 +6,7 @@
*/
internal class Accounts.EditorRow : Gtk.ListBoxRow {
internal class Accounts.EditorRow<PaneType> : Gtk.ListBoxRow {
protected Gtk.Grid layout { get; private set; default = new Gtk.Grid(); }
@ -22,14 +22,14 @@ internal class Accounts.EditorRow : Gtk.ListBoxRow {
this.show();
}
public virtual void activated(Accounts.Editor editor) {
public virtual void activated(PaneType pane) {
// No-op by default
}
}
internal class Accounts.LabelledEditorRow<V> : EditorRow {
internal class Accounts.LabelledEditorRow<PaneType,V> : EditorRow<PaneType> {
protected Gtk.Label label { get; private set; default = new Gtk.Label(""); }
@ -64,7 +64,7 @@ internal class Accounts.LabelledEditorRow<V> : EditorRow {
}
internal class Accounts.AddRow : EditorRow {
internal class Accounts.AddRow<PaneType> : EditorRow<PaneType> {
public AddRow() {
@ -81,7 +81,8 @@ internal class Accounts.AddRow : EditorRow {
}
internal abstract class Accounts.AccountRow<V> : LabelledEditorRow<V> {
internal abstract class Accounts.AccountRow<PaneType,V> :
LabelledEditorRow<PaneType,V> {
internal Geary.AccountInformation account { get; private set; }
@ -108,7 +109,7 @@ internal abstract class Accounts.AccountRow<V> : LabelledEditorRow<V> {
}
private abstract class Accounts.ServiceRow<V> : AccountRow<V> {
private abstract class Accounts.ServiceRow<PaneType,V> : AccountRow<PaneType,V> {
internal Geary.ServiceInformation service { get; private set; }

View file

@ -7,14 +7,18 @@
*/
/**
* The main account editor window.
* An account editor pane for editing server details for an account.
*/
[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor_servers_pane.ui")]
public class Accounts.EditorServersPane : Gtk.Grid {
internal class Accounts.EditorServersPane : Gtk.Grid, EditorPane, AccountPane {
private weak Editor editor; // circular ref
private Geary.AccountInformation account;
internal Geary.AccountInformation account { get ; protected set; }
protected weak Accounts.Editor editor { get; set; }
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.ListBox details_list;
@ -43,6 +47,17 @@ public class Accounts.EditorServersPane : Gtk.Grid {
this.sending_list.set_header_func(Editor.seperator_headers);
build_service(account.smtp, this.sending_list);
this.account.information_changed.connect(on_account_changed);
update_header();
}
~EditorServersPane() {
this.account.information_changed.disconnect(on_account_changed);
}
internal Gtk.HeaderBar get_header() {
return this.header;
}
private void build_service(Geary.ServiceInformation service,
@ -52,10 +67,24 @@ public class Accounts.EditorServersPane : Gtk.Grid {
settings_list.add(new ServiceAuthRow(this.account, service));
}
[GtkCallback]
private void on_cancel_button_clicked() {
this.editor.pop();
}
[GtkCallback]
private void on_apply_button_clicked() {
}
private void on_account_changed() {
update_header();
}
}
private class Accounts.ServiceProviderRow : AccountRow<Gtk.Label> {
private class Accounts.ServiceProviderRow :
AccountRow<EditorServersPane,Gtk.Label> {
public ServiceProviderRow(Geary.AccountInformation account) {
@ -96,7 +125,8 @@ private class Accounts.ServiceProviderRow : AccountRow<Gtk.Label> {
}
private class Accounts.AccountProviderRow : AccountRow<Gtk.Label> {
private class Accounts.AccountProviderRow :
AccountRow<EditorServersPane,Gtk.Label> {
public AccountProviderRow(Geary.AccountInformation account) {
@ -129,7 +159,8 @@ private class Accounts.AccountProviderRow : AccountRow<Gtk.Label> {
}
private class Accounts.SaveDraftsRow : AccountRow<Gtk.Switch> {
private class Accounts.SaveDraftsRow :
AccountRow<EditorServersPane,Gtk.Switch> {
public SaveDraftsRow(Geary.AccountInformation account) {
@ -151,7 +182,8 @@ private class Accounts.SaveDraftsRow : AccountRow<Gtk.Switch> {
}
private class Accounts.ServiceHostRow : ServiceRow<Gtk.Label> {
private class Accounts.ServiceHostRow :
ServiceRow<EditorServersPane,Gtk.Label> {
public ServiceHostRow(Geary.AccountInformation account,
Geary.ServiceInformation service) {
@ -191,7 +223,8 @@ private class Accounts.ServiceHostRow : ServiceRow<Gtk.Label> {
}
private class Accounts.ServiceSecurityRow : ServiceRow<Gtk.ComboBoxText> {
private class Accounts.ServiceSecurityRow :
ServiceRow<EditorServersPane,Gtk.ComboBoxText> {
private const string INSECURE_ICON = "channel-insecure-symbolic";
private const string SECURE_ICON = "channel-secure-symbolic";
@ -246,7 +279,8 @@ private class Accounts.ServiceSecurityRow : ServiceRow<Gtk.ComboBoxText> {
}
private class Accounts.ServiceAuthRow : ServiceRow<Gtk.Label> {
private class Accounts.ServiceAuthRow :
ServiceRow<EditorServersPane,Gtk.Label> {
public ServiceAuthRow(Geary.AccountInformation account,
Geary.ServiceInformation service) {

View file

@ -8,7 +8,6 @@
/**
* The main account editor window.
*/
[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor.ui")]
public class Accounts.Editor : Gtk.Dialog {
@ -27,123 +26,73 @@ public class Accounts.Editor : Gtk.Dialog {
}
}
private static int ordinal_sort(Gtk.ListBoxRow a, Gtk.ListBoxRow b) {
AccountListRow? account_a = a as AccountListRow;
AccountListRow? account_b = b as AccountListRow;
if (account_a == null) {
return (account_b == null) ? 0 : 1;
} else if (account_b == null) {
return -1;
}
return Geary.AccountInformation.compare_ascending(
account_a.account, account_b.account
);
}
/** 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;
[GtkChild]
private Gtk.Stack editor_panes;
private Gtk.Stack editor_panes = new Gtk.Stack();
[GtkChild]
private Gtk.Button back_button;
[GtkChild]
private Gtk.Button undo_button;
[GtkChild]
private Gtk.Grid list_pane;
[GtkChild]
private Gtk.ListBox accounts_list;
private Gee.LinkedList<Gtk.Widget> editor_pane_stack =
new Gee.LinkedList<Gtk.Widget>();
private Gee.LinkedList<EditorPane> editor_pane_stack =
new Gee.LinkedList<EditorPane>();
public Editor(GearyApplication application, Gtk.Window parent) {
this.application = application;
this.accounts = application.controller.account_manager;
set_default_size(700, 450);
set_icon_name(GearyApplication.APP_ID);
set_modal(true);
set_title(_("Accounts"));
set_transient_for(parent);
get_content_area().border_width = 0;
get_content_area().add(this.editor_panes);
this.editor_panes.set_transition_type(
Gtk.StackTransitionType.SLIDE_LEFT_RIGHT
);
this.editor_panes.notify["visible-child"].connect(on_pane_changed);
this.editor_panes.show();
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(false);
// XXX Glade 3.22 won't let us set this
get_content_area().border_width = 2;
this.accounts_list.set_header_func(seperator_headers);
this.accounts_list.set_sort_func(ordinal_sort);
this.editor_pane_stack.add(list_pane);
foreach (Geary.AccountInformation account in accounts.iterable()) {
add_account(account, accounts.get_status(account));
}
this.accounts_list.add(new AddRow());
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);
push(new EditorListPane(this));
}
~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);
public override void destroy() {
this.editor_panes.notify["visible-child"].disconnect(on_pane_changed);
base.destroy();
}
internal void push(Gtk.Widget child) {
// Since keep old, already-popped panes around (see pop for
internal void push(EditorPane pane) {
// Since we keep old, already-popped panes around (see pop for
// details), when a new pane is pushed on they need to be
// truncated.
Gtk.Widget current = this.editor_panes.get_visible_child();
EditorPane current = get_current_pane();
int target_length = this.editor_pane_stack.index_of(current) + 1;
while (target_length < this.editor_pane_stack.size) {
Gtk.Widget old = this.editor_pane_stack.remove_at(target_length);
EditorPane old = this.editor_pane_stack.remove_at(target_length);
this.editor_panes.remove(old);
}
get_action(GearyController.ACTION_UNDO).set_enabled(false);
get_action(GearyController.ACTION_REDO).set_enabled(false);
// Now push the new pane on
this.editor_pane_stack.add(child);
this.editor_panes.add(child);
this.editor_panes.set_visible_child(child);
this.back_button.show();
this.undo_button.show();
this.editor_pane_stack.add(pane);
this.editor_panes.add(pane);
this.editor_panes.set_visible_child(pane);
pane.pane_shown();
}
internal void pop() {
@ -151,192 +100,95 @@ public class Accounts.Editor : Gtk.Dialog {
// 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.
//
// XXX work out a way to reuse the old ones if we go back to
// them?
Gtk.Widget current = this.editor_panes.get_visible_child();
int next = this.editor_pane_stack.index_of(current) - 1;
EditorPane current = get_current_pane();
int prev_index = this.editor_pane_stack.index_of(current) - 1;
EditorPane prev = this.editor_pane_stack.get(prev_index);
this.editor_panes.set_visible_child(prev);
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) {
if (prev_index == 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 AccountListRow(account, status));
}
private void show_account(Geary.AccountInformation account) {
this.selected_account = account;
push(new EditorEditPane(this, account));
}
private AccountListRow? get_account_row(Geary.AccountInformation account) {
AccountListRow? row = null;
this.accounts_list.foreach((child) => {
AccountListRow? account_row = child as AccountListRow;
if (account_row != null && account_row.account == account) {
row = account_row;
}
});
return row;
}
private inline GLib.SimpleAction get_action(string name) {
internal 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);
}
private void on_account_status_changed(Geary.AccountInformation account,
AccountManager.Status status) {
AccountListRow? row = get_account_row(account);
if (row != null) {
row.update(status);
}
}
private void on_account_removed(Geary.AccountInformation 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 inline EditorPane? get_current_pane() {
return this.editor_panes.get_visible_child() as EditorPane;
}
private void on_undo() {
this.commands.undo.begin(null);
get_current_pane().undo();
}
private void on_redo() {
this.commands.redo.begin(null);
get_current_pane().redo();
}
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) {
AccountListRow? row = activated as AccountListRow;
if (row != null) {
show_account(row.account);
}
}
[GtkCallback]
private void on_back_button_clicked() {
pop();
private void on_pane_changed() {
EditorPane? visible = get_current_pane();
set_titlebar(visible != null ? visible.get_header() : null);
}
}
private class Accounts.AccountListRow : EditorRow {
// XXX I'd really like to make EditorPane an abstract class,
// AccountPane an abstract class extending that, and the four concrete
// panes extend those, but the GTK+ Builder XML template system
// requires a template class to designate its immediate parent
// class. I.e. if accounts-editor-list-pane.ui specifies GtkGrid as
// the parent of EditorListPane, then it much exactly be that and not
// an instance of EditorPane, even if that extends GtkGrid. As a
// result, both EditorPane and AccountPane must both be interfaces so
// that the concrete pane classes can derive from GtkGrid directly,
// and everything becomes horrible. See GTK+ Issue #1151:
// https://gitlab.gnome.org/GNOME/gtk/issues/1151
/**
* Base interface for panes that can be shown by the accounts editor.
*/
internal interface Accounts.EditorPane : Gtk.Grid {
internal Geary.AccountInformation account;
private Gtk.Image unavailable_icon = new Gtk.Image.from_icon_name(
"dialog-warning-symbolic", Gtk.IconSize.BUTTON
);
private Gtk.Label account_name = new Gtk.Label("");
private Gtk.Label account_details = new Gtk.Label("");
/** The editor displaying this pane. */
protected abstract weak Accounts.Editor editor { get; set; }
public AccountListRow(Geary.AccountInformation account,
AccountManager.Status status) {
this.account = account;
/** The GTK header bar to display for this pane. */
internal abstract Gtk.HeaderBar get_header();
this.account_name.show();
this.account_name.set_hexpand(true);
this.account_name.halign = Gtk.Align.START;
this.account_details.show();
this.layout.add(this.unavailable_icon);
this.layout.add(this.account_name);
this.layout.add(this.account_details);
update(status);
/** Notifies the pane that it has been displayed in the dialog. */
internal virtual void pane_shown() {
// no-op by default
}
public void update(AccountManager.Status status) {
if (status != AccountManager.Status.UNAVAILABLE) {
this.unavailable_icon.hide();
this.set_tooltip_text("");
} else {
this.unavailable_icon.show();
this.set_tooltip_text(
_("This account has encountered a problem and is unavailable")
);
}
/** Un-does the last user action, if enabled and supported. */
internal virtual void undo() {
// no-op by default
}
string name = this.account.nickname;
if (Geary.String.is_empty(name)) {
name = account.primary_mailbox.to_address_display("", "");
}
this.account_name.set_text(name);
string? details = this.account.service_label;
switch (account.service_provider) {
case Geary.ServiceProvider.GMAIL:
details = _("GMail");
break;
case Geary.ServiceProvider.OUTLOOK:
details = _("Outlook.com");
break;
case Geary.ServiceProvider.YAHOO:
details = _("Yahoo");
break;
}
this.account_details.set_text(details);
if (status == AccountManager.Status.ENABLED) {
this.account_name.get_style_context().remove_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
this.account_details.get_style_context().remove_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
} else {
this.account_name.get_style_context().add_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
this.account_details.get_style_context().add_class(
Gtk.STYLE_CLASS_DIM_LABEL
);
}
/** Re-does the last user action, if enabled and supported. */
internal virtual void redo() {
// no-op by default
}
}
/**
* Base class for editor panes that display a specific account
*/
internal interface Accounts.AccountPane : Gtk.Grid, EditorPane {
/** Account being displayed by this pane. */
internal abstract Geary.AccountInformation account { get; protected set; }
protected void update_header() {
get_header().subtitle = this.account.nickname;
}
}

View file

@ -22,6 +22,7 @@ geary_client_vala_sources = files(
'accounts/account-spinner-page.vala',
'accounts/accounts-editor.vala',
'accounts/accounts-editor-edit-pane.vala',
'accounts/accounts-editor-list-pane.vala',
'accounts/accounts-editor-remove-pane.vala',
'accounts/accounts-editor-row.vala',
'accounts/accounts-editor-servers-pane.vala',

View file

@ -1,179 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="AccountsEditor" parent="GtkDialog">
<property name="can_focus">False</property>
<property name="modal">True</property>
<property name="default_width">700</property>
<property name="default_height">450</property>
<property name="icon_name">org.gnome.Geary</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkStack" id="editor_panes">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">slide-left-right</property>
<child>
<object class="GtkGrid" id="list_pane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">400</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="accounts_list">
<property name="width_request">0</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="row-activated" handler="on_accounts_list_row_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<style>
<class name="geary-account-view"/>
</style>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="name">account_list</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
<style>
<class name="geary-accounts-editor"/>
</style>
</template>
<object class="GtkHeaderBar" id="default_header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Accounts</property>
<property name="has_subtitle">False</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="back_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
<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">go-previous-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</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>

View file

@ -223,4 +223,68 @@
</packing>
</child>
</template>
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Edit Account</property>
<property name="subtitle">Account Name</property>
<property name="has_subtitle">False</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="back_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
<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">go-previous-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</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="visible">True</property>
<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>

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="AccountsEditorListPane" parent="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">400</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="accounts_list">
<property name="width_request">0</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="row-activated" handler="on_accounts_list_row_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<style>
<class name="geary-account-view"/>
</style>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</template>
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Accounts</property>
<property name="has_subtitle">False</property>
<property name="show_close_button">True</property>
</object>
</interface>

View file

@ -166,4 +166,37 @@
</packing>
</child>
</template>
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Remove account</property>
<property name="subtitle">Account name</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="back_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_back_button_clicked" swapped="no"/>
<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">go-previous-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View file

@ -135,4 +135,56 @@
</packing>
</child>
</template>
<object class="GtkHeaderBar" id="header">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Server Settings</property>
<property name="subtitle">Account Name</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_cancel_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="apply_button">
<property name="label" translatable="yes">Apply</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_apply_button_clicked" swapped="no"/>
<style>
<class name="suggested-action"/>
</style>
</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>

View file

@ -4,8 +4,8 @@
<file compressed="true" preprocess="xml-stripblanks">account_cannot_remove.glade</file>
<file compressed="true" preprocess="xml-stripblanks">account_list.glade</file>
<file compressed="true" preprocess="xml-stripblanks">account_spinner.glade</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_edit_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_list_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_remove_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_servers_pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">certificate_warning_dialog.glade</file>