[geary/wip/714104-refine-account-dialog: 30/69] Implement undo-able account removal.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/714104-refine-account-dialog: 30/69] Implement undo-able account removal.
- Date: Fri, 30 Nov 2018 12:50:51 +0000 (UTC)
commit 9c8990d18d9b669caa3a7399f7e14ee907c1a48c
Author: Michael James Gratton <mike vee net>
Date: Sun Jun 17 18:03:09 2018 +1000
Implement undo-able account removal.
src/client/accounts/account-dialog.vala | 4 +-
src/client/accounts/account-manager.vala | 151 ++++++++++++------
src/client/accounts/accounts-editor-list-pane.vala | 105 ++++++++++++-
.../accounts/accounts-editor-remove-pane.vala | 19 +--
src/client/accounts/accounts-editor.vala | 18 +--
src/client/application/geary-application.vala | 1 +
src/client/application/geary-controller.vala | 63 ++++++--
ui/accounts_editor_list_pane.ui | 65 ++++----
ui/accounts_editor_remove_pane.ui | 171 +++++++--------------
9 files changed, 366 insertions(+), 231 deletions(-)
---
diff --git a/src/client/accounts/account-dialog.vala b/src/client/accounts/account-dialog.vala
index 18ddb668..971269c8 100644
--- a/src/client/accounts/account-dialog.vala
+++ b/src/client/accounts/account-dialog.vala
@@ -138,8 +138,8 @@ public class AccountDialog : Gtk.Dialog {
assert(account != null); // Should not be able to happen since we checked earlier.
// Remove account, then set the page back to the account list.
- this.application.controller.remove_account_async.begin(account, null, () => {
- account_list_pane.present(); });
+ //this.application.controller.remove_account_async.begin(account, null, () => {
+ // account_list_pane.present(); });
}
private void on_save_add_or_edit(Geary.AccountInformation info) {
diff --git a/src/client/accounts/account-manager.vala b/src/client/accounts/account-manager.vala
index 9e654466..aa10d711 100644
--- a/src/client/accounts/account-manager.vala
+++ b/src/client/accounts/account-manager.vala
@@ -48,6 +48,7 @@ public enum CredentialsProvider {
errordomain AccountError {
INVALID,
+ LOCAL_REMOVED,
GOA_REMOVED;
}
@@ -69,6 +70,7 @@ public class AccountManager : GLib.Object {
private const string ACCOUNT_CONFIG_GROUP = "AccountInformation";
+ private const string ACCOUNT_MANAGER_GROUP = "AccountManager";
private const string IMAP_CONFIG_GROUP = "IMAP";
private const string SMTP_CONFIG_GROUP = "SMTP";
@@ -82,6 +84,7 @@ public class AccountManager : GLib.Object {
private const string ORDINAL_KEY = "ordinal";
private const string PREFETCH_PERIOD_DAYS_KEY = "prefetch_period_days";
private const string PRIMARY_EMAIL_KEY = "primary_email";
+ private const string REMOVED_KEY = "removed";
private const string REAL_NAME_KEY = "real_name";
private const string SAVE_DRAFTS_KEY = "save_drafts";
private const string SAVE_SENT_MAIL_KEY = "save_sent_mail";
@@ -151,6 +154,10 @@ public class AccountManager : GLib.Object {
private Gee.Map<string,AccountState> accounts =
new Gee.HashMap<string,AccountState>();
+ private Gee.LinkedList<Geary.AccountInformation> removed =
+ new Gee.LinkedList<Geary.AccountInformation>();
+
+
private GearyApplication application;
private GLib.File user_config_dir;
private GLib.File user_data_dir;
@@ -284,9 +291,6 @@ public class AccountManager : GLib.Object {
} else {
set_available(info, false);
}
- } catch (AccountError.GOA_REMOVED err) {
- // Since the GOA account for this account does
- // not longer exist, remove it.
} catch (GLib.Error err) {
report_problem(
new Geary.ProblemReport(
@@ -316,6 +320,73 @@ public class AccountManager : GLib.Object {
}
}
+ /**
+ * Removes an account from the manager's set of known accounts.
+ *
+ * This removes the account from the known set, marks the account
+ * as deleted, and queues it for deletion. The account will not
+ * actually be deleted until {@link expunge_accounts} is called,
+ * and until then the account can be re-added using {@link
+ * restore_account}.
+ */
+ public async void remove_account(Geary.AccountInformation account,
+ GLib.Cancellable cancellable)
+ throws GLib.Error {
+ this.accounts.unset(account.id);
+ this.removed.add(account);
+ yield save_account(account, cancellable);
+ account_removed(account);
+ }
+
+ /**
+ * Restores an account that has previously been removed.
+ *
+ * This restores an account previously removed via a call to
+ * {@link remove_account}, adding it back to the known set, as
+ * long as {@link expunge_accounts} has not been called since
+ * he account was removed.
+ */
+ public async void restore_account(Geary.AccountInformation account,
+ GLib.Cancellable cancellable)
+ throws GLib.Error {
+ if (this.removed.remove(account)) {
+ yield save_account(account, cancellable);
+ set_enabled(account, true);
+ }
+ }
+
+ /**
+ * Deletes all local data for all accounts that have been removed.
+ */
+ public async void expunge_accounts(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ while (!this.removed.is_empty && !cancellable.is_cancelled()) {
+ yield delete_account(this.removed.remove_at(0), cancellable);
+ }
+ }
+
+ public async void save_account(Geary.AccountInformation info,
+ GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ // Ensure only one async task is saving an info at once, since
+ // at least the Engine can cause multiple saves to be called
+ // in quick succession when updating special folder config.
+ int token = yield info.write_lock.claim_async(cancellable);
+
+ GLib.Error? thrown = null;
+ try {
+ yield save_account_locked(info, cancellable);
+ } catch (GLib.Error err) {
+ thrown = err;
+ }
+
+ info.write_lock.release(ref token);
+
+ if (thrown != null) {
+ throw thrown;
+ }
+ }
+
/**
* Loads an account info from a config directory.
*
@@ -438,29 +509,17 @@ public class AccountManager : GLib.Object {
info.save_drafts = config.get_bool(SAVE_DRAFTS_KEY, true);
- return info;
- }
-
- public async void save_account(Geary.AccountInformation info,
- GLib.Cancellable? cancellable)
- throws GLib.Error {
- // Ensure only one async task is saving an info at once, since
- // at least the Engine can cause multiple saves to be called
- // in quick succession when updating special folder config.
- int token = yield info.write_lock.claim_async(cancellable);
-
- GLib.Error? thrown = null;
- try {
- yield save_account_locked(info, cancellable);
- } catch (GLib.Error err) {
- thrown = err;
+ // If the account has been removed, add it to the removed list
+ // and bail out
+ Geary.ConfigFile.Group manager_config =
+ config_file.get_group(ACCOUNT_MANAGER_GROUP);
+ if (manager_config.exists &&
+ manager_config.get_bool(REMOVED_KEY, false)) {
+ this.removed.add(info);
+ throw new AccountError.LOCAL_REMOVED("Account marked for removal");
}
- info.write_lock.release(ref token);
-
- if (thrown != null) {
- throw thrown;
- }
+ return info;
}
private async void save_account_locked(Geary.AccountInformation info,
@@ -483,6 +542,16 @@ public class AccountManager : GLib.Object {
debug("Could not load existing config file: %s", err.message);
}
+ // If the account has been removed, set it as such. Otherwise
+ // ensure it is not set as such.
+ Geary.ConfigFile.Group manager_config =
+ config_file.get_group(ACCOUNT_MANAGER_GROUP);
+ if (this.removed.contains(info)) {
+ manager_config.set_bool(REMOVED_KEY, true);
+ } else if (manager_config.exists) {
+ manager_config.remove();
+ }
+
Geary.ConfigFile.Group config = config_file.get_group(ACCOUNT_CONFIG_GROUP);
if (info.imap is LocalServiceInformation) {
config.set_string(
@@ -548,24 +617,9 @@ public class AccountManager : GLib.Object {
yield config_file.save(cancellable);
}
- /**
- * Removes an account's configuration, storage and auth tokens.
- */
- public async void remove_account(Geary.AccountInformation info,
- GLib.Cancellable? cancellable)
+ private async void delete_account(Geary.AccountInformation info,
+ GLib.Cancellable? cancellable)
throws GLib.Error {
- if (info.data_dir != null) {
- yield Geary.Files.recursive_delete_async(
- info.data_dir, GLib.Priority.DEFAULT, cancellable
- );
- }
-
- if (info.config_dir != null) {
- yield Geary.Files.recursive_delete_async(
- info.config_dir, GLib.Priority.DEFAULT, cancellable
- );
- }
-
SecretMediator? mediator = info.imap.mediator as SecretMediator;
if (mediator != null) {
try {
@@ -584,8 +638,19 @@ public class AccountManager : GLib.Object {
}
}
- this.accounts.unset(info.id);
- account_removed(info);
+ if (info.data_dir != null) {
+ yield Geary.Files.recursive_delete_async(
+ info.data_dir, GLib.Priority.LOW, cancellable
+ );
+ }
+
+ // Delete config last so if there are any errors above, it
+ // will be re-tried at next startup.
+ if (info.config_dir != null) {
+ yield Geary.Files.recursive_delete_async(
+ info.config_dir, GLib.Priority.LOW, cancellable
+ );
+ }
}
private inline AccountState lookup_state(Geary.AccountInformation account) {
diff --git a/src/client/accounts/accounts-editor-list-pane.vala
b/src/client/accounts/accounts-editor-list-pane.vala
index bc47fb5e..a1aaf0ac 100644
--- a/src/client/accounts/accounts-editor-list-pane.vala
+++ b/src/client/accounts/accounts-editor-list-pane.vala
@@ -32,9 +32,16 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
private AccountManager accounts { get; private set; }
+ private Application.CommandStack commands {
+ get; private set; default = new Application.CommandStack();
+ }
+
[GtkChild]
private Gtk.HeaderBar header;
+ [GtkChild]
+ private Gtk.Overlay osd_overlay;
+
[GtkChild]
private Gtk.ListBox accounts_list;
@@ -59,9 +66,17 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
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_execute);
+ this.commands.undone.connect(on_undo);
+ this.commands.redone.connect(on_execute);
}
public override void destroy() {
+ this.commands.executed.disconnect(on_execute);
+ this.commands.undone.disconnect(on_undo);
+ this.commands.redone.disconnect(on_execute);
+
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);
@@ -80,14 +95,43 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
internal void remove_account(Geary.AccountInformation account) {
AccountListRow? row = get_account_row(account);
if (row != null) {
- this.accounts_list.remove(row);
+ this.commands.execute.begin(
+ new RemoveAccountCommand(account, this.accounts),
+ null
+ );
}
}
+ internal void pane_shown() {
+ update_actions();
+ }
+
+ internal void undo() {
+ this.commands.undo.begin(null);
+ }
+
+ internal void redo() {
+ this.commands.redo.begin(null);
+ }
+
internal Gtk.HeaderBar get_header() {
return this.header;
}
-
+
+ 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 AccountListRow? get_account_row(Geary.AccountInformation account) {
AccountListRow? row = null;
this.accounts_list.foreach((child) => {
@@ -113,7 +157,26 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane {
}
private void on_account_removed(Geary.AccountInformation account) {
- remove_account(account);
+ AccountListRow? row = get_account_row(account);
+ if (row != null) {
+ this.accounts_list.remove(row);
+ }
+ }
+
+ private void on_execute(Application.Command command) {
+ InAppNotification ian = new InAppNotification(command.executed_label);
+ ian.set_button(_("Undo"), "win." + GearyController.ACTION_UNDO);
+ add_notification(ian);
+
+ update_actions();
+ }
+
+ private void on_undo(Application.Command command) {
+ InAppNotification ian = new InAppNotification(command.undone_label);
+ ian.set_button(_("Redo"), "win." + GearyController.ACTION_REDO);
+ add_notification(ian);
+
+ update_actions();
}
[GtkCallback]
@@ -213,3 +276,39 @@ private class Accounts.AccountListRow : EditorRow<EditorListPane> {
}
}
+
+
+internal class Accounts.RemoveAccountCommand : Application.Command {
+
+
+ private Geary.AccountInformation account;
+ private AccountManager manager;
+
+
+ public RemoveAccountCommand(Geary.AccountInformation account,
+ AccountManager manager) {
+ this.account = account;
+ this.manager = manager;
+
+ // Translators: Notification shown after removing an
+ // account. The string substitution is the name of the
+ // account.
+ this.executed_label = _("Account “%s” removed").printf(account.nickname);
+
+ // Translators: Notification shown after removing an account
+ // is undone. The string substitution is the name of the
+ // account.
+ this.undone_label = _("Account “%s” restored").printf(account.nickname);
+ }
+
+ public async override void execute(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ yield this.manager.remove_account(this.account, cancellable);
+ }
+
+ public async override void undo(GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ yield this.manager.restore_account(this.account, cancellable);
+ }
+
+}
diff --git a/src/client/accounts/accounts-editor-remove-pane.vala
b/src/client/accounts/accounts-editor-remove-pane.vala
index 57542e57..d8976205 100644
--- a/src/client/accounts/accounts-editor-remove-pane.vala
+++ b/src/client/accounts/accounts-editor-remove-pane.vala
@@ -19,21 +19,9 @@ internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
[GtkChild]
private Gtk.HeaderBar header;
- [GtkChild]
- private Gtk.Stack confirm_stack;
-
[GtkChild]
private Gtk.Label warning_label;
- [GtkChild]
- private Gtk.Spinner remove_spinner;
-
- [GtkChild]
- private Gtk.Label remove_label;
-
- [GtkChild]
- private Gtk.Button remove_button;
-
public EditorRemovePane(Editor editor, Geary.AccountInformation account) {
this.editor = editor;
@@ -42,9 +30,6 @@ internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
this.warning_label.set_text(
this.warning_label.get_text().printf(account.nickname)
);
- this.remove_label.set_text(
- this.remove_label.get_text().printf(account.nickname)
- );
this.account.information_changed.connect(on_account_changed);
update_header();
@@ -64,9 +49,7 @@ internal class Accounts.EditorRemovePane : Gtk.Grid, EditorPane, AccountPane {
[GtkCallback]
private void on_remove_button_clicked() {
- this.remove_button.set_sensitive(false);
- this.remove_spinner.start();
- this.confirm_stack.set_visible_child_name("remove");
+ this.editor.remove_account(this.account);
}
[GtkCallback]
diff --git a/src/client/accounts/accounts-editor.vala b/src/client/accounts/accounts-editor.vala
index b293f38e..0079fa08 100644
--- a/src/client/accounts/accounts-editor.vala
+++ b/src/client/accounts/accounts-editor.vala
@@ -27,15 +27,11 @@ public class Accounts.Editor : Gtk.Dialog {
}
- /** The current account being edited, if any. */
- private Geary.AccountInformation selected_account {
- get; private set; default = null;
- }
-
private SimpleActionGroup actions = new SimpleActionGroup();
private Gtk.Stack editor_panes = new Gtk.Stack();
+ private EditorListPane editor_list_pane;
private Gee.LinkedList<EditorPane> editor_pane_stack =
new Gee.LinkedList<EditorPane>();
@@ -65,7 +61,8 @@ public class Accounts.Editor : Gtk.Dialog {
get_action(GearyController.ACTION_UNDO).set_enabled(false);
get_action(GearyController.ACTION_REDO).set_enabled(false);
- push(new EditorListPane(this));
+ this.editor_list_pane = new EditorListPane(this);
+ push(this.editor_list_pane);
}
public override void destroy() {
@@ -104,16 +101,17 @@ public class Accounts.Editor : Gtk.Dialog {
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);
-
- if (prev_index == 0) {
- this.selected_account = null;
- }
}
internal GLib.SimpleAction get_action(string name) {
return (GLib.SimpleAction) this.actions.lookup_action(name);
}
+ internal void remove_account(Geary.AccountInformation account) {
+ this.editor_panes.set_visible_child(this.editor_list_pane);
+ this.editor_list_pane.remove_account(account);
+ }
+
private inline EditorPane? get_current_pane() {
return this.editor_panes.get_visible_child() as EditorPane;
}
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index 96e831dc..a3f8149e 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -425,6 +425,7 @@ public class GearyApplication : Gtk.Application {
Accounts.Editor editor = new Accounts.Editor(this, get_active_window());
editor.run();
editor.destroy();
+ this.controller.expunge_accounts.begin();
}
private void on_activate_compose() {
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index ea91ead0..b26c757f 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -105,6 +105,10 @@ public class GearyController : Geary.BaseObject {
private Gee.Map<Geary.AccountInformation,AccountContext> accounts =
new Gee.HashMap<Geary.AccountInformation,AccountContext>();
+ // Created when controller is opened, cancelled and nulled out
+ // when closed.
+ private GLib.Cancellable? open_cancellable = null;
+
private Geary.Folder? current_folder = null;
private Cancellable cancellable_folder = new Cancellable();
private Cancellable cancellable_search = new Cancellable();
@@ -207,6 +211,8 @@ public class GearyController : Geary.BaseObject {
apply_app_menu_fix();
+ this.open_cancellable = new GLib.Cancellable();
+
// Listen for attempts to close the application.
this.application.exiting.connect(on_application_exiting);
@@ -246,7 +252,6 @@ public class GearyController : Geary.BaseObject {
enable_message_buttons(false);
engine.account_available.connect(on_account_available);
- engine.account_unavailable.connect(on_account_unavailable);
engine.untrusted_host.connect(on_untrusted_host);
// Connect to various UI signals.
@@ -308,6 +313,9 @@ public class GearyController : Geary.BaseObject {
this.account_manager.account_status_changed.connect(
on_account_status_changed
);
+ this.account_manager.account_removed.connect(
+ on_account_removed
+ );
try {
yield this.account_manager.connect_libsecret(cancellable);
@@ -332,6 +340,10 @@ public class GearyController : Geary.BaseObject {
} catch (Error e) {
warning("Error opening Geary.Engine instance: %s", e.message);
}
+
+ // Expunge any deleted accounts in the background, so we're
+ // not blocking the app continuing to open.
+ this.expunge_accounts.begin();
}
/**
@@ -339,8 +351,12 @@ public class GearyController : Geary.BaseObject {
* re-opened.
*/
public async void close_async() {
+ // Cancel internal processes early so they don't block
+ // shutdown
+ this.open_cancellable.cancel();
+ this.open_cancellable = null;
+
Geary.Engine.instance.account_available.disconnect(on_account_available);
- Geary.Engine.instance.account_unavailable.disconnect(on_account_unavailable);
Geary.Engine.instance.untrusted_host.disconnect(on_untrusted_host);
// Release folder and conversations in the main window
@@ -455,6 +471,9 @@ public class GearyController : Geary.BaseObject {
this.account_manager.account_status_changed.disconnect(
on_account_status_changed
);
+ this.account_manager.account_removed.disconnect(
+ on_account_removed
+ );
this.account_manager = null;
this.application.remove_window(this.main_window);
@@ -509,16 +528,11 @@ public class GearyController : Geary.BaseObject {
}
}
- /**
- * Closes an account and deletes it from disk.
- */
- public async void remove_account_async(Geary.AccountInformation info,
- Cancellable? cancellable = null) {
+ /** Expunges removed accounts while the controller remains open. */
+ internal async void expunge_accounts() {
try {
- yield close_account(info);
- this.application.engine.remove_account(info);
- yield this.account_manager.remove_account(info, cancellable);
- } catch (Error err) {
+ yield this.account_manager.expunge_accounts(this.open_cancellable);
+ } catch (GLib.Error err) {
report_problem(
new Geary.ProblemReport(Geary.ProblemType.GENERIC_ERROR, err)
);
@@ -3129,6 +3143,29 @@ public class GearyController : Geary.BaseObject {
}
}
+ private void on_account_removed(Geary.AccountInformation removed) {
+ debug("%s: Closing account for removal", removed.id);
+ this.close_account.begin(
+ removed,
+ (obj, res) => {
+ this.close_account.end(res);
+ debug("%s: Account closed", removed.id);
+ try {
+ this.application.engine.remove_account(removed);
+ debug("%s: Account removed from engine", removed.id);
+ } catch (GLib.Error err) {
+ report_problem(
+ new Geary.AccountProblemReport(
+ Geary.ProblemType.GENERIC_ERROR,
+ removed,
+ err
+ )
+ );
+ }
+ }
+ );
+ }
+
private void on_scan_completed() {
// Done scanning. Check if we have enough messages to fill
// the conversation list; if not, trigger a load_more();
@@ -3152,10 +3189,6 @@ public class GearyController : Geary.BaseObject {
);
}
- private void on_account_unavailable(Geary.AccountInformation info) {
- this.close_account.begin(info);
- }
-
private void on_save_attachments(Gee.Collection<Geary.Attachment> attachments) {
GLib.Cancellable? cancellable = null;
if (this.current_account != null) {
diff --git a/ui/accounts_editor_list_pane.ui b/ui/accounts_editor_list_pane.ui
index b34def99..db6c70d3 100644
--- a/ui/accounts_editor_list_pane.ui
+++ b/ui/accounts_editor_list_pane.ui
@@ -6,53 +6,62 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkScrolledWindow">
+ <object class="GtkOverlay" id="osd_overlay">
<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>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkViewport">
+ <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="hscrollbar_policy">never</property>
+ <property name="min_content_height">400</property>
<child>
- <object class="GtkGrid">
+ <object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkFrame">
+ <object class="GtkGrid">
<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>
+ <object class="GtkFrame">
<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"/>
+ <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>
- <child type="label_item">
- <placeholder/>
- </child>
+ <style>
+ <class name="geary-account-view"/>
+ </style>
</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>
+ <packing>
+ <property name="index">-1</property>
+ </packing>
</child>
</object>
<packing>
diff --git a/ui/accounts_editor_remove_pane.ui b/ui/accounts_editor_remove_pane.ui
index ba785dd2..e11dd170 100644
--- a/ui/accounts_editor_remove_pane.ui
+++ b/ui/accounts_editor_remove_pane.ui
@@ -3,7 +3,6 @@
<interface>
<requires lib="gtk+" version="3.20"/>
<template class="AccountsEditorRemovePane" parent="GtkGrid">
- <property name="name">1</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
@@ -14,116 +13,6 @@
<property name="valign">start</property>
<property name="vexpand">True</property>
<property name="row_spacing">32</property>
- <child>
- <object class="GtkStack" id="confirm_stack">
- <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>
- <property name="row_spacing">6</property>
- <property name="column_spacing">18</property>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">dialog-warning-symbolic</property>
- <property name="icon_size">6</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" id="warning_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="vexpand">True</property>
- <property name="label" translatable="yes" comments="This title is shown to users when
confirming if they want to remove an account. The string substitution is replaced with the account's
name.">Confirm removing: %s</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- <style>
- <class name="title"/>
- </style>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</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">Removing an account will delete it permanently
from your computer. This cannot be un-done.</property>
- <property name="wrap">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="name">confirm</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">18</property>
- <child>
- <object class="GtkSpinner" id="remove_spinner">
- <property name="width_request">16</property>
- <property name="height_request">16</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <property name="margin_left">16</property>
- <property name="margin_right">16</property>
- <property name="active">True</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="remove_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="hexpand">True</property>
- <property name="vexpand">True</property>
- <property name="label" translatable="yes" comments="This title is shown to users when
actually deleting an account. The string substitution is replaced with the account's name.">Please wait,
removing account %s…</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="name">remove</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
@@ -131,7 +20,7 @@
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="remove_button">
- <property name="label" translatable="yes" comments="This is the remove account button in the
account settings.">Remove Permanently</property>
+ <property name="label" translatable="yes" comments="This is the remove account button in the
account settings.">Remove Account</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
@@ -156,6 +45,64 @@
<property name="top_attach">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">18</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">dialog-warning-symbolic</property>
+ <property name="icon_size">6</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" id="warning_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="vexpand">True</property>
+ <property name="label" translatable="yes" comments="This title is shown to users when
confirming if they want to remove an account. The string substitution is replaced with the account's
name.">Confirm removing: %s</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</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">Removing an account will remove it from Geary and
delete locally cached email data from your computer, but not from your service provider.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
<style>
<class name="geary-account-view"/>
</style>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]