[geary/wip/721828-undo-2: 1/2] UI plumbing
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/721828-undo-2: 1/2] UI plumbing
- Date: Wed, 14 Jan 2015 00:09:27 +0000 (UTC)
commit b652eb69c77c9c9d7bd6cc1b071a9bf8966fa30c
Author: Jim Nelson <jim yorba org>
Date: Tue Jan 13 15:07:01 2015 -0800
UI plumbing
src/CMakeLists.txt | 1 +
src/client/application/geary-controller.vala | 50 ++++++++++++++++++++++++++
src/client/components/main-toolbar.vala | 6 +++
src/engine/api/geary-revokable.vala | 49 +++++++++++++++++++++++++
4 files changed, 106 insertions(+), 0 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0d04959..b8375dd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -37,6 +37,7 @@ engine/api/geary-logging.vala
engine/api/geary-named-flag.vala
engine/api/geary-named-flags.vala
engine/api/geary-progress-monitor.vala
+engine/api/geary-revokable.vala
engine/api/geary-search-folder.vala
engine/api/geary-search-query.vala
engine/api/geary-service.vala
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 998fc72..9622aea 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -33,6 +33,7 @@ public class GearyController : Geary.BaseObject {
public const string ACTION_EMPTY_MENU = "GearyEmptyMenu";
public const string ACTION_EMPTY_SPAM = "GearyEmptySpam";
public const string ACTION_EMPTY_TRASH = "GearyEmptyTrash";
+ public const string ACTION_UNDO = "GearyUndo";
public const string ACTION_FIND_IN_CONVERSATION = "GearyFindInConversation";
public const string ACTION_FIND_NEXT_IN_CONVERSATION = "GearyFindNextInConversation";
public const string ACTION_FIND_PREVIOUS_IN_CONVERSATION = "GearyFindPreviousInConversation";
@@ -125,6 +126,7 @@ public class GearyController : Geary.BaseObject {
private Gee.List<string> pending_mailtos = new Gee.ArrayList<string>();
private Geary.Nonblocking.Mutex untrusted_host_prompt_mutex = new Geary.Nonblocking.Mutex();
private Gee.HashSet<Geary.Endpoint> validating_endpoints = new Gee.HashSet<Geary.Endpoint>();
+ private Geary.Revokable? revokable = null;
// List of windows we're waiting to close before Geary closes.
private Gee.List<ComposerWidget> waiting_to_close = new Gee.ArrayList<ComposerWidget>();
@@ -238,6 +240,9 @@ public class GearyController : Geary.BaseObject {
// instantiate here to ensure that Config is initialized and ready
autostart_manager = new AutostartManager();
+ // initialize revokable
+ save_revokable(null, null);
+
// Start Geary.
try {
yield Geary.Engine.instance.open_async(GearyApplication.instance.get_user_data_directory(),
@@ -404,6 +409,9 @@ public class GearyController : Geary.BaseObject {
empty_trash.label = _("Empty _Trash…");
entries += empty_trash;
+ Gtk.ActionEntry undo = { ACTION_UNDO, "edit-undo-symbolic", null, "<Ctrl>Z", null, on_revoke };
+ entries += undo;
+
Gtk.ActionEntry zoom_in = { ACTION_ZOOM_IN, null, null, "<Ctrl>equal",
null, on_zoom_in };
entries += zoom_in;
@@ -2433,6 +2441,48 @@ public class GearyController : Geary.BaseObject {
}
}
+ private void save_revokable(Geary.Revokable? new_revokable, string? description) {
+ // disconnect old revokable
+ if (revokable != null)
+ revokable.notify[Geary.Revokable.PROP_CAN_REVOKE].disconnect(on_can_revoke_changed);
+
+ // store new revokable
+ revokable = new_revokable;
+
+ // connect to new revokable
+ if (revokable != null)
+ revokable.notify[Geary.Revokable.PROP_CAN_REVOKE].connect(on_can_revoke_changed);
+
+ Gtk.Action undo_action = GearyApplication.instance.get_action(ACTION_UNDO);
+ undo_action.sensitive = revokable != null && revokable.can_revoke;
+ undo_action.tooltip = (revokable != null && description != null) ? description : _("Undo");
+ }
+
+ private void on_can_revoke_changed() {
+ // remove revokable if it goes invalid
+ if (revokable != null && !revokable.can_revoke)
+ save_revokable(null, null);
+ }
+
+ private void on_revoke() {
+ if (revokable != null && revokable.can_revoke)
+ revokable.revoke_async.begin(null, on_revoke_completed);
+ }
+
+ private void on_revoke_completed(Object? object, AsyncResult result) {
+ // Don't use the "revokable" instance because it might have gone null before this callback
+ // was reached
+ Geary.Revokable? origin = object as Geary.Revokable;
+ if (origin == null)
+ return;
+
+ try {
+ origin.revoke_async.end(result);
+ } catch (Error err) {
+ debug("Unable to revoke operation: %s", err.message);
+ }
+ }
+
private void on_zoom_in() {
main_window.conversation_viewer.web_view.zoom_in();
}
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 6b787e8..39c52a8 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -72,6 +72,10 @@ public class MainToolbar : PillHeaderbar {
insert.add(create_menu_button(null, empty_menu, GearyController.ACTION_EMPTY_MENU));
Gtk.Box archive_trash_delete_empty = create_pill_buttons(insert);
+ insert.clear();
+ insert.add(create_toolbar_button(null, GearyController.ACTION_UNDO, false));
+ Gtk.Box undo = create_pill_buttons(insert);
+
// Search bar.
search_entry.width_chars = 28;
search_entry.tooltip_text = _("Search all mail in account for keywords (Ctrl+S)");
@@ -88,6 +92,7 @@ public class MainToolbar : PillHeaderbar {
// pack_end() ordering is reversed in GtkHeaderBar in 3.12 and above
#if !GTK_3_12
add_end(archive_trash_delete_empty);
+ add_end(undo);
add_end(search_upgrade_progress_bar);
add_end(search_entry);
#endif
@@ -103,6 +108,7 @@ public class MainToolbar : PillHeaderbar {
#if GTK_3_12
add_end(search_entry);
add_end(search_upgrade_progress_bar);
+ add_end(undo);
add_end(archive_trash_delete_empty);
#endif
diff --git a/src/engine/api/geary-revokable.vala b/src/engine/api/geary-revokable.vala
new file mode 100644
index 0000000..51b6f95
--- /dev/null
+++ b/src/engine/api/geary-revokable.vala
@@ -0,0 +1,49 @@
+/* Copyright 2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * A representation of an operation with the Geary Engine that make be revoked (undone) at a later
+ * time.
+ */
+
+public abstract class Geary.Revokable : BaseObject {
+ public const string PROP_CAN_REVOKE = "can-revoke";
+ public const string PROP_IS_REVOKING = "is-revoking";
+
+ /**
+ * Indicates if { link revoke_async} is a valid operation for this { link Revokable}.
+ *
+ * Due to later operations or notifications, it's possible for the Revokable to go invalid.
+ * In some circumstances, this may be that it cannot fully revoke the original operation, in
+ * others it may be that it can't revoke any part of the original operation, depending on the
+ * nature of the operation.
+ */
+ public bool can_revoke { get; protected set; default = true; }
+
+ /**
+ * Indicates a { link revoke_async} operation is underway.
+ *
+ * Only one revoke operation can occur at a time. If this is true when revoke_async() is
+ * called, it will throw an Error.
+ */
+ public bool is_revoking { get; protected set; default = false; }
+
+ protected Revokable() {
+ }
+
+ /**
+ * Revoke (undo) the operation.
+ *
+ * Returns false if the operation failed and is no longer revokable.
+ *
+ * If the call throws an Error that does not necessarily mean the { link Revokable} is
+ * invalid. Check the return value or { link can_revoke}.
+ *
+ * @throws EngineError.ALREADY_OPEN if { link is_revoking} is true when called.
+ */
+ public abstract async bool revoke_async(Cancellable? cancellable = null) throws Error;
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]