Merge branch 'wip/account-editor-refinements-v1' into 'master'

Account editor refinements v1

See merge request GNOME/geary!76
This commit is contained in:
Michael Gratton 2019-01-09 02:39:32 +00:00
commit 3000334955
13 changed files with 725 additions and 513 deletions

View file

@ -401,6 +401,7 @@ src/engine/util/util-time.vala
src/engine/util/util-timeout-manager.vala
src/engine/util/util-trillian.vala
src/mailer/main.vala
ui/accounts_editor.ui
ui/accounts_editor_add_pane.ui
ui/accounts_editor_edit_pane.ui
ui/accounts_editor_list_pane.ui

View file

@ -1,5 +1,5 @@
/*
* Copyright 2018 Michael Gratton <mike@vee.net>
* Copyright 2018-2019 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.
@ -16,6 +16,17 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
get { return this.real_name.value; }
}
/** {@inheritDoc} */
internal bool is_operation_running {
get { return !this.sensitive; }
protected set { update_operation_ui(value); }
}
/** {@inheritDoc} */
internal GLib.Cancellable? op_cancellable {
get; protected set; default = new GLib.Cancellable();
}
protected weak Accounts.Editor editor { get; set; }
private Geary.ServiceProvider provider;
@ -27,9 +38,6 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.Overlay osd_overlay;
[GtkChild]
private Gtk.Grid pane_content;
@ -54,6 +62,9 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
[GtkChild]
private Gtk.Button create_button;
[GtkChild]
private Gtk.Button back_button;
[GtkChild]
private Gtk.Spinner create_spinner;
@ -148,16 +159,8 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
return this.header;
}
private void add_notification(InAppNotification notification) {
this.osd_overlay.add_overlay(notification);
notification.show();
}
private async void validate_account(GLib.Cancellable? cancellable) {
this.create_spinner.show();
this.create_spinner.start();
this.create_button.set_sensitive(false);
this.set_sensitive(false);
this.is_operation_running = true;
bool is_valid = false;
string message = "";
@ -190,6 +193,9 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
to_focus = this.imap_login.value;
// Translators: In-app notification label
message = _("Check your receiving login and password");
} catch (GLib.IOError.CANCELLED err) {
// Nothing to do here, someone just cancelled
debug("IMAP validation was cancelled: %s", err.message);
} catch (GLib.Error err) {
debug("Error validating IMAP service: %s", err.message);
this.imap_tls.show();
@ -218,6 +224,9 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
to_focus = this.smtp_login.value;
// Translators: In-app notification label
message = _("Check your sending login and password");
} catch (GLib.IOError.CANCELLED err) {
// Nothing to do here, someone just cancelled
debug("SMTP validation was cancelled: %s", err.message);
} catch (GLib.Error err) {
debug("Error validating SMTP service: %s", err.message);
this.smtp_tls.show();
@ -260,10 +269,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
}
}
this.create_spinner.stop();
this.create_spinner.hide();
this.create_button.set_sensitive(true);
this.set_sensitive(true);
this.is_operation_running = false;
// Focus and pop up the notification after re-sensitising
// so it actually succeeds.
@ -271,7 +277,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
if (to_focus != null) {
to_focus.grab_focus();
}
add_notification(
this.editor.add_notification(
new InAppNotification(
// Translators: In-app notification label, the
// string substitution is a more detailed reason.
@ -365,6 +371,14 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
this.controls_valid = controls_valid;
}
private void update_operation_ui(bool is_running) {
this.create_spinner.visible = is_running;
this.create_spinner.active = is_running;
this.create_button.sensitive = !is_running;
this.back_button.sensitive = !is_running;
this.sensitive = !is_running;
}
private void on_validated(Components.Validator.Trigger reason) {
check_validation();
if (this.controls_valid && reason == Components.Validator.Trigger.ACTIVATED) {
@ -407,7 +421,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane {
[GtkCallback]
private void on_create_button_clicked() {
this.validate_account.begin(null);
this.validate_account.begin(this.op_cancellable);
}
[GtkCallback]

View file

@ -1,5 +1,5 @@
/*
* Copyright 2018 Michael Gratton <mike@vee.net>
* Copyright 2018-2019 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.
@ -26,6 +26,14 @@ internal class Accounts.EditorEditPane :
get; protected set; default = new Application.CommandStack();
}
/** {@inheritDoc} */
internal bool is_operation_running { get; protected set; default = false; }
/** {@inheritDoc} */
internal GLib.Cancellable? op_cancellable {
get; protected set; default = null;
}
/** {@inheritDoc} */
protected weak Accounts.Editor editor { get; set; }
@ -67,7 +75,9 @@ internal class Accounts.EditorEditPane :
this.pane_content.set_focus_vadjustment(this.pane_adjustment);
this.details_list.set_header_func(Editor.seperator_headers);
this.details_list.add(new DisplayNameRow(account, this.commands));
this.details_list.add(
new DisplayNameRow(account, this.commands, this.op_cancellable)
);
this.senders_list.set_header_func(Editor.seperator_headers);
foreach (Geary.RFC822.MailboxAddress sender in
@ -85,7 +95,9 @@ internal class Accounts.EditorEditPane :
this.signature_preview.content_loaded.connect(() => {
// 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.set_editable.begin(
true, this.op_cancellable
);
});
this.signature_preview.document_modified.connect(() => {
this.signature_changed = true;
@ -101,7 +113,7 @@ internal class Accounts.EditorEditPane :
new SignatureChangedCommand(
this.signature_preview, account
),
null
this.op_cancellable
);
}
return Gdk.EVENT_PROPAGATE;
@ -154,7 +166,7 @@ internal class Accounts.EditorEditPane :
/** {@inheritDoc} */
protected void command_executed() {
update_command_actions();
this.editor.update_command_actions();
Application.Command next_undo = this.commands.peek_undo();
this.undo_button.set_tooltip_text(
@ -175,7 +187,7 @@ internal class Accounts.EditorEditPane :
this.account,
this.senders_list
),
null
this.op_cancellable
);
}
@ -187,7 +199,7 @@ internal class Accounts.EditorEditPane :
this.account,
this.senders_list
),
null
this.op_cancellable
);
}
@ -253,9 +265,12 @@ private class Accounts.DisplayNameRow : AccountRow<EditorEditPane,Gtk.Entry> {
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
public DisplayNameRow(Geary.AccountInformation account,
Application.CommandStack commands) {
Application.CommandStack commands,
GLib.Cancellable? cancellable) {
base(
account,
// Translators: Label in the account editor for the user's
@ -265,6 +280,7 @@ private class Accounts.DisplayNameRow : AccountRow<EditorEditPane,Gtk.Entry> {
);
this.activatable = false;
this.commands = commands;
this.cancellable = cancellable;
update();
@ -295,7 +311,7 @@ private class Accounts.DisplayNameRow : AccountRow<EditorEditPane,Gtk.Entry> {
// account.
_("Change account name back to “%s”")
),
null
this.cancellable
);
}
@ -335,7 +351,7 @@ private class Accounts.AddMailboxRow : AddRow<EditorEditPane> {
)
)
),
null
pane.op_cancellable
);
popover.popdown();
});
@ -376,13 +392,14 @@ private class Accounts.MailboxRow : AccountRow<EditorEditPane,Gtk.Label> {
popover.address
)
),
null
pane.op_cancellable
);
popover.popdown();
});
popover.remove_clicked.connect(() => {
pane.commands.execute.begin(
new RemoveMailboxCommand(this), null
new RemoveMailboxCommand(this),
pane.op_cancellable
);
popover.popdown();
});
@ -788,7 +805,7 @@ private class Accounts.EmailPrefetchRow :
get_label(this.account.prefetch_period_days)
)
),
null
pane.op_cancellable
);
});
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2018 Michael Gratton <mike@vee.net>
* Copyright 2018-2019 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.
@ -28,21 +28,29 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
}
/** {@iinheritDoc} */
/** {@inheritDoc} */
internal Gtk.Widget initial_widget {
get {
return this.show_welcome ? this.service_list : this.accounts_list;
}
}
/** {@iinheritDoc} */
/** {@inheritDoc} */
internal Application.CommandStack commands {
get; protected set; default = new Application.CommandStack();
}
/** {@inheritDoc} */
internal bool is_operation_running { get; protected set; default = false; }
/** {@inheritDoc} */
internal GLib.Cancellable? op_cancellable {
get; protected set; default = null;
}
internal Manager accounts { get; private set; }
/** {@iinheritDoc} */
/** {@inheritDoc} */
protected weak Accounts.Editor editor { get; set; }
private bool show_welcome {
@ -54,9 +62,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.Overlay osd_overlay;
[GtkChild]
private Gtk.Grid pane_content;
@ -110,7 +115,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
this.commands.executed.connect(on_execute);
this.commands.undone.connect(on_undo);
this.commands.redone.connect(on_execute);
connect_command_signals();
update_welcome_panel();
}
@ -118,6 +123,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
this.commands.executed.disconnect(on_execute);
this.commands.undone.disconnect(on_undo);
this.commands.redone.disconnect(on_execute);
disconnect_command_signals();
this.accounts.account_added.disconnect(on_account_added);
this.accounts.account_status_changed.disconnect(on_account_status_changed);
@ -146,7 +152,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
if (row != null) {
this.commands.execute.begin(
new RemoveAccountCommand(account, this.accounts),
null
this.op_cancellable
);
}
}
@ -164,20 +170,6 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
this.accounts_list.add(row);
}
private void add_notification(InAppNotification notification) {
this.osd_overlay.add_overlay(notification);
notification.show();
}
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
);
}
private void update_welcome_panel() {
if (this.show_welcome) {
// No accounts are available, so show only the welcome
@ -224,7 +216,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
new ReorderAccountCommand(
(AccountListRow) source, new_position, this.accounts
),
null
this.op_cancellable
);
}
@ -233,7 +225,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
new ReorderAccountCommand(
(AccountListRow) source, target.get_index(), this.accounts
),
null
this.op_cancellable
);
}
@ -249,20 +241,16 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
if (command.executed_label != null) {
InAppNotification ian = new InAppNotification(command.executed_label);
ian.set_button(_("Undo"), "win." + GearyController.ACTION_UNDO);
add_notification(ian);
this.editor.add_notification(ian);
}
update_actions();
}
private void on_undo(Application.Command command) {
if (command.undone_label != null) {
InAppNotification ian = new InAppNotification(command.undone_label);
ian.set_button(_("Redo"), "win." + GearyController.ACTION_REDO);
add_notification(ian);
this.editor.add_notification(ian);
}
update_actions();
}
[GtkCallback]
@ -326,7 +314,7 @@ private class Accounts.AccountListRow : AccountRow<EditorListPane,Gtk.Grid> {
// GOA account but it's disabled, so just take people
// directly to the GOA panel
manager.show_goa_account.begin(
account, null,
account, pane.op_cancellable,
(obj, res) => {
try {
manager.show_goa_account.end(res);
@ -379,7 +367,7 @@ private class Accounts.AccountListRow : AccountRow<EditorListPane,Gtk.Grid> {
case DISABLED:
this.set_tooltip_text(
// Translators: Tooltip for accounts that have been
// loaded by disabled by the user.
// loaded but disabled by the user.
_("This account has been disabled")
);
break;
@ -466,7 +454,7 @@ private class Accounts.AddServiceProviderRow : EditorRow<EditorListPane> {
public override void activated(EditorListPane pane) {
pane.accounts.add_goa_account.begin(
this.provider, null,
this.provider, pane.op_cancellable,
(obj, res) => {
bool add_local = false;
try {

View file

@ -1,5 +1,5 @@
/*
* Copyright 2018 Michael Gratton <mike@vee.net>
* Copyright 2018-2019 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.
@ -23,6 +23,14 @@ internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
get { return this.remove_button; }
}
/** {@inheritDoc} */
internal bool is_operation_running { get; protected set; default = false; }
/** {@inheritDoc} */
internal GLib.Cancellable? op_cancellable {
get; protected set; default = null;
}
[GtkChild]
private Gtk.HeaderBar header;

View file

@ -1,6 +1,6 @@
/*
* Copyright 2016 Software Freedom Conservancy Inc.
* Copyright 2018 Michael Gratton <mike@vee.net>
* Copyright 2018-2019 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.
@ -30,6 +30,17 @@ internal class Accounts.EditorServersPane :
get { return this.details_list; }
}
/** {@inheritDoc} */
internal bool is_operation_running {
get { return !this.sensitive; }
protected set { update_operation_ui(value); }
}
/** {@inheritDoc} */
internal GLib.Cancellable? op_cancellable {
get; protected set; default = new GLib.Cancellable();
}
private Geary.Engine engine;
// These are copies of the originals that can be updated before
@ -44,9 +55,6 @@ internal class Accounts.EditorServersPane :
[GtkChild]
private Gtk.HeaderBar header;
[GtkChild]
private Gtk.Overlay osd_overlay;
[GtkChild]
private Gtk.Grid pane_content;
@ -105,7 +113,9 @@ internal class Accounts.EditorServersPane :
service_provider.activatable = false;
add_row(this.details_list, service_provider);
this.save_drafts = new SaveDraftsRow(this.account, this.commands);
this.save_drafts = new SaveDraftsRow(
this.account, this.commands, this.op_cancellable
);
add_row(this.details_list, this.save_drafts);
// Receiving
@ -113,19 +123,36 @@ internal class Accounts.EditorServersPane :
this.receiving_list.set_header_func(Editor.seperator_headers);
add_row(
this.receiving_list,
new ServiceHostRow(account, this.incoming_mutable, this.commands)
new ServiceHostRow(
account,
this.incoming_mutable,
this.commands,
this.op_cancellable
)
);
add_row(
this.receiving_list,
new ServiceSecurityRow(account, this.incoming_mutable, this.commands)
new ServiceSecurityRow(
account,
this.incoming_mutable,
this.commands,
this.op_cancellable
)
);
this.incoming_password = new ServicePasswordRow(
account, this.incoming_mutable, this.commands
account,
this.incoming_mutable,
this.commands,
this.op_cancellable
);
this.incoming_login = new ServiceLoginRow(
account, this.incoming_mutable, this.commands, this.incoming_password
account,
this.incoming_mutable,
this.commands,
this.op_cancellable,
this.incoming_password
);
add_row(this.receiving_list, this.incoming_login);
@ -136,24 +163,45 @@ internal class Accounts.EditorServersPane :
this.sending_list.set_header_func(Editor.seperator_headers);
add_row(
this.sending_list,
new ServiceHostRow(account, this.outgoing_mutable, this.commands)
new ServiceHostRow(
account,
this.outgoing_mutable,
this.commands,
this.op_cancellable
)
);
add_row(
this.sending_list,
new ServiceSecurityRow(account, this.outgoing_mutable, this.commands)
new ServiceSecurityRow(
account,
this.outgoing_mutable,
this.commands,
this.op_cancellable
)
);
this.outgoing_auth = new ServiceOutgoingAuthRow(
account, this.outgoing_mutable, this.incoming_mutable, this.commands
account,
this.outgoing_mutable,
this.incoming_mutable,
this.commands,
this.op_cancellable
);
this.outgoing_auth.value.changed.connect(on_outgoing_auth_changed);
add_row(this.sending_list, this.outgoing_auth);
this.outgoing_password = new ServicePasswordRow(
account, this.outgoing_mutable, this.commands
account,
this.outgoing_mutable,
this.commands,
this.op_cancellable
);
this.outgoing_login = new ServiceLoginRow(
account, this.outgoing_mutable, this.commands, this.outgoing_password
account,
this.outgoing_mutable,
this.commands,
this.op_cancellable,
this.outgoing_password
);
add_row(this.sending_list, this.outgoing_login);
@ -179,7 +227,7 @@ internal class Accounts.EditorServersPane :
/** {@inheritDoc} */
protected void command_executed() {
update_command_actions();
this.editor.update_command_actions();
this.apply_button.set_sensitive(this.commands.can_undo);
}
@ -188,17 +236,16 @@ internal class Accounts.EditorServersPane :
}
private async void save(GLib.Cancellable? cancellable) {
this.apply_button.set_sensitive(false);
this.apply_spinner.show();
this.apply_spinner.start();
this.set_sensitive(false);
this.is_operation_running = true;
// Only need to validate if a generic account
// Only need to validate if a generic, local account since
// other account types have read-only incoming/outgoing
// settings
bool is_valid = true;
bool has_changed = false;
if (this.account.service_provider == Geary.ServiceProvider.OTHER) {
if (this.account.service_provider == Geary.ServiceProvider.OTHER &&
!this.editor.accounts.is_goa_account(this.account)) {
is_valid = yield validate(cancellable);
if (is_valid) {
has_changed |= yield update_service(
this.account.incoming, this.incoming_mutable, cancellable
@ -209,6 +256,8 @@ internal class Accounts.EditorServersPane :
}
}
this.is_operation_running = false;
if (is_valid) {
if (this.save_drafts.value_changed) {
has_changed = true;
@ -229,14 +278,10 @@ internal class Accounts.EditorServersPane :
// updated already by the command
this.account.save_drafts = this.save_drafts.initial_value;
}
this.apply_spinner.stop();
this.apply_spinner.hide();
this.set_sensitive(true);
}
private async bool validate(GLib.Cancellable? cancellable) {
string message = "";
string? message = null;
bool imap_valid = false;
try {
yield this.engine.validate_imap(
@ -247,6 +292,9 @@ internal class Accounts.EditorServersPane :
debug("Error authenticating IMAP service: %s", err.message);
// Translators: In-app notification label
message = _("Check your receiving login and password");
} catch (GLib.IOError.CANCELLED err) {
// Nothing to do here, someone just cancelled
debug("IMAP validation was cancelled: %s", err.message);
} catch (GLib.Error err) {
debug("Error validating IMAP service: %s", err.message);
// Translators: In-app notification label
@ -272,6 +320,9 @@ internal class Accounts.EditorServersPane :
this.outgoing_auth.value.source = Geary.Credentials.Requirement.CUSTOM;
// Translators: In-app notification label
message = _("Check your sending login and password");
} catch (GLib.IOError.CANCELLED err) {
// Nothing to do here, someone just cancelled
debug("SMTP validation was cancelled: %s", err.message);
} catch (GLib.Error err) {
debug("Error validating SMTP service: %s", err.message);
// Translators: In-app notification label
@ -282,8 +333,8 @@ internal class Accounts.EditorServersPane :
bool is_valid = imap_valid && smtp_valid;
debug("Validation complete, is valid: %s", is_valid.to_string());
if (!is_valid) {
add_notification(
if (!is_valid && message != null) {
this.editor.add_notification(
new InAppNotification(
// Translators: In-app notification label, the
// string substitution is a more detailed reason.
@ -329,11 +380,6 @@ internal class Accounts.EditorServersPane :
return has_changed;
}
private void add_notification(InAppNotification notification) {
this.osd_overlay.add_overlay(notification);
notification.show();
}
private void add_row(Gtk.ListBox list, EditorRow<EditorServersPane> row) {
list.add(row);
ValidatingRow? validating = row as ValidatingRow;
@ -350,6 +396,13 @@ internal class Accounts.EditorServersPane :
);
}
private void update_operation_ui(bool is_running) {
this.apply_spinner.visible = is_running;
this.apply_spinner.active = is_running;
this.apply_button.sensitive = !is_running;
this.sensitive = !is_running;
}
private void on_validator_changed() {
this.apply_button.set_sensitive(is_valid());
}
@ -362,12 +415,16 @@ internal class Accounts.EditorServersPane :
[GtkCallback]
private void on_cancel_button_clicked() {
this.editor.pop();
if (this.is_operation_running) {
cancel_operation();
} else {
this.editor.pop();
}
}
[GtkCallback]
private void on_apply_button_clicked() {
this.save.begin(null);
this.save.begin(this.op_cancellable);
}
[GtkCallback]
@ -455,7 +512,7 @@ private class Accounts.AccountProviderRow :
public override void activated(EditorServersPane pane) {
if (this.accounts.is_goa_account(this.account)) {
this.accounts.show_goa_account.begin(
account, null,
account, pane.op_cancellable,
(obj, res) => {
try {
this.accounts.show_goa_account.end(res);
@ -484,9 +541,12 @@ private class Accounts.SaveDraftsRow :
public bool initial_value { get; private set; }
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
public SaveDraftsRow(Geary.AccountInformation account,
Application.CommandStack commands) {
Application.CommandStack commands,
GLib.Cancellable? cancellable) {
Gtk.Switch value = new Gtk.Switch();
base(
account,
@ -497,6 +557,7 @@ private class Accounts.SaveDraftsRow :
);
update();
this.commands = commands;
this.cancellable = cancellable;
this.activatable = false;
this.initial_value = this.account.save_drafts;
this.account.notify["save-drafts"].connect(on_account_changed);
@ -513,7 +574,7 @@ private class Accounts.SaveDraftsRow :
new Application.PropertyCommand<bool>(
this.account, "save_drafts", this.value.state
),
null
this.cancellable
);
}
}
@ -540,11 +601,13 @@ private class Accounts.ServiceHostRow :
}
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
public ServiceHostRow(Geary.AccountInformation account,
Geary.ServiceInformation service,
Application.CommandStack commands) {
Application.CommandStack commands,
GLib.Cancellable? cancellable) {
string label = "";
switch (service.protocol) {
case Geary.Protocol.IMAP:
@ -562,6 +625,7 @@ private class Accounts.ServiceHostRow :
base(account, service, label, new Gtk.Entry());
this.commands = commands;
this.cancellable = cancellable;
this.activatable = false;
this.validator = new Components.NetworkAddressValidator(this.value);
@ -596,8 +660,8 @@ private class Accounts.ServiceHostRow :
this.service, "port", port
)
}),
null
);
this.cancellable
);
}
}
@ -621,16 +685,19 @@ private class Accounts.ServiceSecurityRow :
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
public ServiceSecurityRow(Geary.AccountInformation account,
Geary.ServiceInformation service,
Application.CommandStack commands) {
Application.CommandStack commands,
GLib.Cancellable? cancellable) {
TlsComboBox value = new TlsComboBox();
base(account, service, value.label, value);
update();
this.commands = commands;
this.cancellable = cancellable;
this.activatable = false;
value.changed.connect(on_value_changed);
}
@ -663,7 +730,7 @@ private class Accounts.ServiceSecurityRow :
)
});
}
this.commands.execute.begin(cmd, null);
this.commands.execute.begin(cmd, this.cancellable);
}
}
@ -685,12 +752,14 @@ private class Accounts.ServiceLoginRow :
}
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
private ServicePasswordRow? password_row;
public ServiceLoginRow(Geary.AccountInformation account,
Geary.ServiceInformation service,
Application.CommandStack commands,
GLib.Cancellable? cancellable,
ServicePasswordRow? password_row = null) {
base(
account,
@ -702,6 +771,7 @@ private class Accounts.ServiceLoginRow :
);
this.commands = commands;
this.cancellable = cancellable;
this.activatable = false;
this.validator = new Components.Validator(this.value);
this.password_row = password_row;
@ -745,7 +815,7 @@ private class Accounts.ServiceLoginRow :
});
}
this.commands.execute.begin(cmd, null);
this.commands.execute.begin(cmd, this.cancellable);
}
}
@ -803,11 +873,13 @@ private class Accounts.ServicePasswordRow :
}
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
public ServicePasswordRow(Geary.AccountInformation account,
Geary.ServiceInformation service,
Application.CommandStack commands) {
Application.CommandStack commands,
GLib.Cancellable? cancellable) {
base(
account,
service,
@ -818,6 +890,7 @@ private class Accounts.ServicePasswordRow :
);
this.commands = commands;
this.cancellable = cancellable;
this.activatable = false;
this.value.visibility = false;
this.value.input_purpose = Gtk.InputPurpose.PASSWORD;
@ -841,7 +914,7 @@ private class Accounts.ServicePasswordRow :
"credentials",
this.service.credentials.copy_with_token(this.value.text)
),
null
this.cancellable
);
}
}
@ -860,18 +933,21 @@ private class Accounts.ServiceOutgoingAuthRow :
private Application.CommandStack commands;
private GLib.Cancellable? cancellable;
private Geary.ServiceInformation imap_service;
public ServiceOutgoingAuthRow(Geary.AccountInformation account,
Geary.ServiceInformation smtp_service,
Geary.ServiceInformation imap_service,
Application.CommandStack commands) {
Application.CommandStack commands,
GLib.Cancellable? cancellable) {
OutgoingAuthComboBox value = new OutgoingAuthComboBox();
base(account, smtp_service, value.label, value);
update();
this.commands = commands;
this.cancellable = cancellable;
this.imap_service = imap_service;
this.activatable = false;
value.changed.connect(on_value_changed);
@ -919,7 +995,7 @@ private class Accounts.ServiceOutgoingAuthRow :
);
}
this.commands.execute.begin(seq, null);
this.commands.execute.begin(seq, this.cancellable);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2018 Michael Gratton <mike@vee.net>
* Copyright 2018-2019 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.
@ -7,7 +7,14 @@
/**
* The main account editor window.
*
* The editor is a dialog window that manages a stack of {@link
* EditorPane} instances. Each pane handles a specific task (listing
* accounts, adding a new account, editing an existing one, etc.). The
* editor displaying panes as needed, and provides some common command
* management, account management and other common code for the panes.
*/
[GtkTemplate (ui = "/org/gnome/Geary/accounts_editor.ui")]
public class Accounts.Editor : Gtk.Dialog {
@ -32,7 +39,12 @@ public class Accounts.Editor : Gtk.Dialog {
private SimpleActionGroup actions = new SimpleActionGroup();
private Gtk.Stack editor_panes = new Gtk.Stack();
[GtkChild]
private Gtk.Overlay notifications_pane;
[GtkChild]
private Gtk.Stack editor_panes;
private EditorListPane editor_list_pane;
private Gee.LinkedList<EditorPane> editor_pane_stack =
@ -41,51 +53,73 @@ public class Accounts.Editor : Gtk.Dialog {
public Editor(GearyApplication application, Gtk.Window parent) {
this.application = application;
this.transient_for = parent;
// Can't set this in Glade 3.22.1 :(
this.get_content_area().border_width = 0;
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_after(on_pane_changed);
this.editor_panes.show();
this.actions.add_action_entries(ACTION_ENTRIES, this);
insert_action_group("win", this.actions);
get_action(GearyController.ACTION_UNDO).set_enabled(false);
get_action(GearyController.ACTION_REDO).set_enabled(false);
this.editor_list_pane = new EditorListPane(this);
push(this.editor_list_pane);
update_command_actions();
}
public override bool key_press_event(Gdk.EventKey event) {
bool ret = Gdk.EVENT_PROPAGATE;
// Allow the user to use Esc, Back and Alt+arrow keys to
// navigate between panes.
if (get_current_pane() != this.editor_list_pane) {
// navigate between panes. If a pane is executing a long
// running operation, only allow Esc and use it to cancel the
// operation instead.
EditorPane? current_pane = get_current_pane();
if (current_pane != null &&
current_pane != this.editor_list_pane) {
Gdk.ModifierType state = (
event.state & Gtk.accelerator_get_default_mod_mask()
);
bool is_ltr = (get_direction() == Gtk.TextDirection.LTR);
if (event.keyval == Gdk.Key.Escape ||
event.keyval == Gdk.Key.Back ||
(state == Gdk.ModifierType.MOD1_MASK &&
(is_ltr && event.keyval == Gdk.Key.Left) ||
(!is_ltr && event.keyval == Gdk.Key.Right))) {
pop();
switch (event.keyval) {
case Gdk.Key.Escape:
if (current_pane.is_operation_running) {
current_pane.cancel_operation();
} else {
pop();
}
ret = Gdk.EVENT_STOP;
break;
case Gdk.Key.Back:
if (!current_pane.is_operation_running) {
pop();
ret = Gdk.EVENT_STOP;
}
break;
case Gdk.Key.Left:
if (!current_pane.is_operation_running &&
state == Gdk.ModifierType.MOD1_MASK &&
is_ltr) {
pop();
ret = Gdk.EVENT_STOP;
}
break;
case Gdk.Key.Right:
if (!current_pane.is_operation_running &&
state == Gdk.ModifierType.MOD1_MASK &&
!is_ltr) {
pop();
ret = Gdk.EVENT_STOP;
}
break;
}
}
if (ret != Gdk.EVENT_STOP) {
@ -95,11 +129,9 @@ public class Accounts.Editor : Gtk.Dialog {
return ret;
}
public override void destroy() {
this.editor_panes.notify["visible-child"].disconnect(on_pane_changed);
base.destroy();
}
/**
* Adds and shows a new pane in the editor.
*/
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
@ -111,15 +143,15 @@ public class Accounts.Editor : Gtk.Dialog {
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(pane);
this.editor_panes.add(pane);
this.editor_panes.set_visible_child(pane);
}
/**
* Removes the current pane from the editor, showing the last one.
*/
internal void pop() {
// One can't simply remove old panes fro the GTK stack since
// there won't be any transition between them - the old one
@ -131,19 +163,40 @@ public class Accounts.Editor : Gtk.Dialog {
this.editor_panes.set_visible_child(prev);
}
internal GLib.SimpleAction get_action(string name) {
return (GLib.SimpleAction) this.actions.lookup_action(name);
/** Displays an in-app notification in the dialog. */
internal void add_notification(InAppNotification notification) {
this.notifications_pane.add_overlay(notification);
notification.show();
}
/** Removes an account from the editor. */
internal void remove_account(Geary.AccountInformation account) {
this.editor_panes.set_visible_child(this.editor_list_pane);
this.editor_list_pane.remove_account(account);
}
/** Updates the state of the editor's undo and redo actions. */
internal void update_command_actions() {
bool can_undo = false;
bool can_redo = false;
CommandPane? pane = get_current_pane() as CommandPane;
if (pane != null) {
can_undo = pane.commands.can_undo;
can_redo = pane.commands.can_redo;
}
get_action(GearyController.ACTION_UNDO).set_enabled(can_undo);
get_action(GearyController.ACTION_REDO).set_enabled(can_redo);
}
private inline EditorPane? get_current_pane() {
return this.editor_panes.get_visible_child() as EditorPane;
}
private inline GLib.SimpleAction get_action(string name) {
return (GLib.SimpleAction) this.actions.lookup_action(name);
}
private void on_undo() {
CommandPane? pane = get_current_pane() as CommandPane;
if (pane != null) {
@ -158,6 +211,7 @@ public class Accounts.Editor : Gtk.Dialog {
}
}
[GtkCallback]
private void on_pane_changed() {
EditorPane? visible = get_current_pane();
Gtk.Widget? header = null;
@ -171,11 +225,7 @@ public class Accounts.Editor : Gtk.Dialog {
header = visible.get_header();
}
set_titlebar(header);
CommandPane? commands = visible as CommandPane;
if (commands != null) {
commands.update_command_actions();
}
update_command_actions();
}
}
@ -205,9 +255,41 @@ internal interface Accounts.EditorPane : Gtk.Grid {
/** The editor displaying this pane. */
internal abstract Gtk.Widget initial_widget { get; }
/**
* Determines if a long running operation is being executed.
*
* @see cancel_operation
*/
internal abstract bool is_operation_running { get; protected set; }
/**
* Long running operation cancellable.
*
* This cancellable must be passed to any long-running operations
* involving I/O. If not null and operation is cancelled, the
* value should be cancelled and replaced with a new instance.
*
* @see cancel_operation
*/
internal abstract GLib.Cancellable? op_cancellable { get; protected set; }
/** The GTK header bar to display for this pane. */
internal abstract Gtk.HeaderBar get_header();
/**
* Cancels this pane's current operation, any.
*
* Sets {@link is_operation_running} to false and if {@link
* op_cancellable} is not null, it is cancelled and replaced with
* a new instance.
*/
internal void cancel_operation() {
this.is_operation_running = false;
if (this.op_cancellable != null) {
this.op_cancellable.cancel();
this.op_cancellable = new GLib.Cancellable();
}
}
}
@ -279,18 +361,6 @@ internal interface Accounts.CommandPane : EditorPane {
this.commands.redo.begin(null);
}
/**
* Updates the state of the editor's undo and redo actions.
*/
internal virtual void update_command_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
);
}
/**
* Connects to command stack signals.
*
@ -316,10 +386,10 @@ internal interface Accounts.CommandPane : EditorPane {
/**
* Called when a command is executed, undone or redone.
*
* By default, calls {@link update_command_actions}.
* By default, calls {@link Accounts.Editor.update_command_actions}.
*/
protected virtual void command_executed() {
update_command_actions();
this.editor.update_command_actions();
}
private void on_command() {

View file

@ -881,14 +881,16 @@ public class Geary.Imap.ClientSession : BaseObject {
}
if (login_err != null) {
// Throw an error indicating auth failed here, unless the
// response code indicated that the server is merely
// unavailable, then don't since the creds might actually
// be fine.
ResponseCode? code = cmd.status.response_code;
// Throw an error indicating auth failed here, unless
// there is a status response and it indicates that the
// server is merely reporting login as being unavailable,
// then don't since the creds might actually be fine.
ResponseCodeType? code_type = null;
if (code != null) {
code_type = code.get_response_code_type();
if (cmd.status != null) {
ResponseCode? code = cmd.status.response_code;
if (code != null) {
code_type = code.get_response_code_type();
}
}
if (code_type == null ||

64
ui/accounts_editor.ui Normal file
View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<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 type="titlebar">
<placeholder/>
</child>
<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="GtkOverlay" id="notifications_pane">
<property name="visible">True</property>
<property name="can_focus">False</property>
<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>
<signal name="notify::visible-child" handler="on_pane_changed" swapped="no"/>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

View file

@ -81,27 +81,65 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkOverlay" id="osd_overlay">
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkScrolledWindow">
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkViewport">
<object class="GtkGrid" id="pane_content">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkGrid" id="pane_content">
<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="GtkListBox" id="details_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="receiving_panel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<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">Receiving</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
@ -109,7 +147,7 @@
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="details_list">
<object class="GtkListBox" id="receiving_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
@ -117,52 +155,52 @@
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="sending_panel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<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">Sending</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="receiving_panel">
<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="GtkLabel">
<object class="GtkListBox" id="sending_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Receiving</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="geary-settings-heading"/>
</style>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</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="GtkListBox" id="receiving_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
@ -170,65 +208,18 @@
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="sending_panel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<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">Sending</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</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="GtkListBox" id="sending_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<style>
<class name="geary-accounts-editor-pane-content"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<style>
<class name="geary-accounts-editor-pane-content"/>
</style>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>

View file

@ -18,171 +18,160 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkOverlay" id="osd_overlay">
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">400</property>
<child>
<object class="GtkScrolledWindow">
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">400</property>
<property name="can_focus">False</property>
<child>
<object class="GtkViewport">
<object class="GtkGrid" id="pane_content">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid" id="pane_content">
<object class="GtkGrid" id="welcome_panel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkGrid" id="welcome_panel">
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixel_size">64</property>
<property name="icon_name">org.gnome.Geary</property>
<property name="use_fallback">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="height">2</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="valign">start</property>
<property name="label" translatable="yes">To get started, select an email provider below.</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</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="valign">end</property>
<property name="label" translatable="yes">Welcome to Geary</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<style>
<class name="geary-welcome-panel"/>
</style>
<property name="pixel_size">64</property>
<property name="icon_name">org.gnome.Geary</property>
<property name="use_fallback">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="accounts_list_frame">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">start</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="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_row_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
<property name="label" translatable="yes">To get started, select an email provider below.</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="add_service_label">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Add an account</property>
<property name="valign">end</property>
<property name="label" translatable="yes">Welcome to Geary</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</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="service_list">
<property name="width_request">0</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_row_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<style>
<class name="geary-accounts-editor-pane-content"/>
<class name="geary-welcome-panel"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="accounts_list_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</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="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_row_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="add_service_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Add an account</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</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="service_list">
<property name="width_request">0</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_row_activated" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<style>
<class name="geary-accounts-editor-pane-content"/>
</style>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>

View file

@ -76,142 +76,133 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkOverlay" id="osd_overlay">
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">400</property>
<child>
<object class="GtkScrolledWindow">
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="vadjustment">pane_adjustment</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">400</property>
<property name="can_focus">False</property>
<child>
<object class="GtkViewport">
<object class="GtkGrid" id="pane_content">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkGrid" id="pane_content">
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkFrame">
<object class="GtkListBox" id="receiving_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="receiving_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</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">Receiving</property>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="sending_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</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">Sending</property>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="details_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" 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 type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</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">Receiving</property>
<style>
<class name="geary-accounts-editor-pane-content"/>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="sending_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" swapped="no"/>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</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">Sending</property>
<style>
<class name="geary-settings-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="details_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="keynav-failed" handler="on_list_keynav_failed" swapped="no"/>
<signal name="row-activated" handler="on_activate" 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-accounts-editor-pane-content"/>
</style>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>

View file

@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<gresources>
<gresource prefix="/org/gnome/Geary">
<file compressed="true" preprocess="xml-stripblanks">accounts_editor.ui</file>
<file compressed="true" preprocess="xml-stripblanks">accounts_editor_add_pane.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>