[geary: 1/4] Start the final part of bug 713991.



commit 1dddcebb92a94eea1dc80f4bbc864812ad424efe
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Mon May 15 23:21:32 2017 +0200

    Start the final part of bug 713991.
    
    * Ported Gtk.Action to GLib.Action in the GearyController.
    * Removed Gtk.AccelGroups (handled through Gtk.Application now).
    * Got rid of Gtk.UiManager (now all is done through Gtk.Builder).
    * Throw away workaround for conflicting Gtk.Actions in ComposerContainer.
    * Aggregate zoom in/out/normal into one parameterized zoom action.
    
    Signed-off-by: Niels De Graef <nielsdegraef gmail com>

 po/POTFILES.in                                     |    4 +-
 src/client/application/geary-application.vala      |   40 --
 src/client/application/geary-controller.vala       |  600 +++++++-------------
 src/client/components/folder-popover.vala          |    2 +
 src/client/components/main-toolbar.vala            |  208 +++----
 src/client/components/main-window.vala             |    6 +-
 src/client/composer/composer-container.vala        |   14 -
 .../conversation-list/conversation-list-view.vala  |   64 +--
 src/client/util/util-gtk.vala                      |   19 -
 ui/CMakeLists.txt                                  |    4 +-
 ui/accelerators.ui                                 |   17 -
 ui/main-toolbar-menus.ui                           |   40 ++
 ui/main-toolbar.ui                                 |   29 +-
 ui/toolbar_empty_menu.ui                           |    7 -
 ui/toolbar_mark_menu.ui                            |   10 -
 15 files changed, 392 insertions(+), 672 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 345dcbf..dea321b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -384,7 +384,6 @@ src/engine/util/util-time.vala
 src/engine/util/util-timeout-manager.vala
 src/engine/util/util-trillian.vala
 src/mailer/main.vala
-[type: gettext/glade]ui/accelerators.ui
 [type: gettext/glade]ui/account_cannot_remove.glade
 [type: gettext/glade]ui/account_list.glade
 [type: gettext/glade]ui/account_spinner.glade
@@ -407,10 +406,9 @@ src/mailer/main.vala
 [type: gettext/glade]ui/gtk/menus.ui
 [type: gettext/glade]ui/login.glade
 [type: gettext/glade]ui/main-toolbar.ui
+[type: gettext/glade]ui/main-toolbar-menus.ui
 [type: gettext/glade]ui/main-window.ui
 [type: gettext/glade]ui/password-dialog.glade
 [type: gettext/glade]ui/preferences-dialog.ui
 [type: gettext/glade]ui/remove_confirm.glade
-[type: gettext/glade]ui/toolbar_empty_menu.ui
-[type: gettext/glade]ui/toolbar_mark_menu.ui
 [type: gettext/glade]ui/upgrade_dialog.glade
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index 857cffb..8c51c3e 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -113,14 +113,6 @@ public class GearyApplication : Gtk.Application {
         get { return Args.hidden_startup || this.config.startup_notifications; }
     }
 
-    public Gtk.ActionGroup actions {
-        get; private set; default = new Gtk.ActionGroup("GearyActionGroup");
-    }
-
-    public Gtk.UIManager ui_manager {
-        get; private set; default = new Gtk.UIManager();
-    }
-
     private string bin;
     private File exec_dir;
     private bool exiting_fired = false;
@@ -260,14 +252,6 @@ public class GearyApplication : Gtk.Application {
         is_destroyed = true;
     }
 
-    // NOTE: This assert()'s if the Gtk.Action is not present in the default action group
-    public Gtk.Action get_action(string name) {
-        Gtk.Action? action = actions.get_action(name);
-        assert(action != null);
-        
-        return action;
-    }
-    
     public File get_user_data_directory() {
         return File.new_for_path(Environment.get_user_data_dir()).get_child("geary");
     }
@@ -351,30 +335,6 @@ public class GearyApplication : Gtk.Application {
     }
 
     /**
-     * Loads a GResource as a string.
-     *
-     * @deprecated Use {@link GioUtil.read_resource} instead.
-     */
-    [Deprecated]
-    public string read_resource(string name) throws Error {
-        return GioUtil.read_resource(name);
-    }
-
-    /**
-     * Loads a UI GResource into the UI manager.
-     */
-    [Deprecated]
-    public void load_ui_resource(string name) {
-        try {
-            this.ui_manager.add_ui_from_resource("/org/gnome/Geary/" + name);
-        } catch(GLib.Error error) {
-            critical("Unable to load \"%s\" for Gtk.UIManager: %s".printf(
-                name, error.message
-            ));
-        }
-    }
-
-    /**
      * Displays a URI on the current active window, if any.
      */
     public void show_uri(string uri) throws Error {
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index b263530..040dede 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -21,67 +21,40 @@ extern bool gcr_trust_remove_pinned_certificate(Gcr.Certificate cert, string pur
  */
 public class GearyController : Geary.BaseObject {
     // Named actions.
-    //
-    // NOTE: Some actions with accelerators need to also be added to ui/accelerators.ui
-    public const string ACTION_NEW_MESSAGE = "GearyNewMessage";
-    public const string ACTION_REPLY_TO_MESSAGE = "GearyReplyToMessage";
-    public const string ACTION_REPLY_ALL_MESSAGE = "GearyReplyAllMessage";
-    public const string ACTION_FORWARD_MESSAGE = "GearyForwardMessage";
-    public const string ACTION_ARCHIVE_CONVERSATION = "GearyArchiveConversation";
-    public const string ACTION_TRASH_CONVERSATION = "GearyTrashConversation";
-    public const string ACTION_DELETE_CONVERSATION = "GearyDeleteConversation";
-    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_ZOOM_IN = "GearyZoomIn";
-    public const string ACTION_ZOOM_OUT = "GearyZoomOut";
-    public const string ACTION_ZOOM_NORMAL = "GearyZoomNormal";
-    public const string ACTION_MARK_AS_MENU = "GearyMarkAsMenuButton";
-    public const string ACTION_MARK_AS_READ = "GearyMarkAsRead";
-    public const string ACTION_MARK_AS_UNREAD = "GearyMarkAsUnread";
-    public const string ACTION_MARK_AS_STARRED = "GearyMarkAsStarred";
-    public const string ACTION_MARK_AS_UNSTARRED = "GearyMarkAsUnStarred";
-    public const string ACTION_MARK_AS_SPAM = "GearyMarkAsSpam";
-    public const string ACTION_COPY_MENU = "GearyCopyMenuButton";
-    public const string ACTION_MOVE_MENU = "GearyMoveMenuButton";
-    public const string ACTION_SEARCH = "GearySearch";
-    public const string ACTION_CONVERSATION_LIST = "GearyConversationList";
-    public const string ACTION_TOGGLE_SEARCH = "GearyToggleSearch";
-    public const string ACTION_TOGGLE_FIND = "GearyToggleFind";
-
+    public const string ACTION_NEW_MESSAGE = "new-message";
+    public const string ACTION_REPLY_TO_MESSAGE = "reply-to-message";
+    public const string ACTION_REPLY_ALL_MESSAGE = "reply-all-message";
+    public const string ACTION_FORWARD_MESSAGE = "forward-message";
+    public const string ACTION_ARCHIVE_CONVERSATION = "archive-conv";
+    public const string ACTION_TRASH_CONVERSATION = "trash-conv";
+    public const string ACTION_DELETE_CONVERSATION = "delete-conv";
+    public const string ACTION_EMPTY_SPAM = "empty-spam";
+    public const string ACTION_EMPTY_TRASH = "empty-trash";
+    public const string ACTION_UNDO = "undo";
+    public const string ACTION_FIND_IN_CONVERSATION = "conv-find";
+    public const string ACTION_ZOOM = "zoom";
+    public const string ACTION_SHOW_MARK_MENU = "mark-message-menu";
+    public const string ACTION_MARK_AS_READ = "mark-message-read";
+    public const string ACTION_MARK_AS_UNREAD = "mark-message-unread";
+    public const string ACTION_MARK_AS_STARRED = "mark-message-starred";
+    public const string ACTION_MARK_AS_UNSTARRED = "mark-message-unstarred";
+    public const string ACTION_MARK_AS_SPAM = "mark-message-spam";
+    public const string ACTION_MARK_AS_NOT_SPAM = "mark-message-not-spam";
+    public const string ACTION_COPY_MENU = "show-copy-menu";
+    public const string ACTION_MOVE_MENU = "show-move-menu";
+    public const string ACTION_SEARCH = "search-conv";
+    public const string ACTION_CONVERSATION_LIST = "focus-conv-list";
+    public const string ACTION_TOGGLE_SEARCH = "toggle-search";
+    public const string ACTION_TOGGLE_FIND = "toggle-find";
+
+    // Properties
     public const string PROP_CURRENT_CONVERSATION ="current-conversations";
-    
+    public const string PROP_SELECTED_CONVERSATIONS ="selected-conversations";
+
     public const int MIN_CONVERSATION_COUNT = 50;
-    
-    private const string DELETE_CONVERSATION_LABEL = _("Delete conversation");
-    private const string DELETE_CONVERSATION_TOOLTIP_SINGLE = _("Delete conversation (Shift+Delete)");
-    private const string DELETE_CONVERSATION_TOOLTIP_MULTIPLE = _("Delete conversations (Shift+Delete)");
-    private const string DELETE_CONVERSATION_ICON_NAME = "edit-delete-symbolic";
-    
-    // This refers to the action ("move email to the trash"), not the Trash folder itself
-    private const string TRASH_CONVERSATION_TOOLTIP_SINGLE = _("Move conversation to Trash (Delete, 
Backspace)");
-    private const string TRASH_CONVERSATION_TOOLTIP_MULTIPLE = _("Move conversations to Trash (Delete, 
Backspace)");
-    private const string TRASH_CONVERSATION_ICON_NAME = "user-trash-symbolic";
-    
-    // This refers to the action ("archive an email"), not the Archive folder itself
-    private const string ARCHIVE_CONVERSATION_LABEL = _("_Archive");
-    private const string ARCHIVE_CONVERSATION_TOOLTIP_SINGLE = _("Archive conversation (A)");
-    private const string ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE = _("Archive conversations (A)");
-    private const string ARCHIVE_CONVERSATION_ICON_NAME = "mail-archive-symbolic";
-    
-    private const string MARK_AS_SPAM_LABEL = _("Mark as S_pam");
-    private const string MARK_AS_NOT_SPAM_LABEL = _("Mark as not S_pam");
-    
-    private const string MARK_MESSAGE_MENU_TOOLTIP_SINGLE = _("Mark conversation");
-    private const string MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE = _("Mark conversations");
-    private const string LABEL_MESSAGE_TOOLTIP_SINGLE = _("Add label to conversation");
-    private const string LABEL_MESSAGE_TOOLTIP_MULTIPLE = _("Add label to conversations");
-    private const string MOVE_MESSAGE_TOOLTIP_SINGLE = _("Move conversation");
-    private const string MOVE_MESSAGE_TOOLTIP_MULTIPLE = _("Move conversations");
-    
+
     private const int SELECT_FOLDER_TIMEOUT_USEC = 100 * 1000;
-    
+
     private const string PROP_ATTEMPT_OPEN_ACCOUNT = "attempt-open-account";
 
     public weak GearyApplication application { get; private set; } // circular ref
@@ -126,10 +99,39 @@ public class GearyController : Geary.BaseObject {
     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>();
-    
+
+    private const ActionEntry[] win_action_entries = {
+        {ACTION_NEW_MESSAGE,           on_new_message                  },
+        {ACTION_CONVERSATION_LIST,     on_conversation_list            },
+        {ACTION_FIND_IN_CONVERSATION,  on_find_in_conversation_action  },
+        {ACTION_SEARCH,                on_search_activated             },
+        {ACTION_EMPTY_SPAM,            on_empty_spam                   },
+        {ACTION_EMPTY_TRASH,           on_empty_trash                  },
+        {ACTION_UNDO,                  on_revoke                       },
+        // Message actions
+        {ACTION_REPLY_TO_MESSAGE,      on_reply_to_message_action   },
+        {ACTION_REPLY_ALL_MESSAGE,     on_reply_all_message_action  },
+        {ACTION_FORWARD_MESSAGE,       on_forward_message_action    },
+        {ACTION_ARCHIVE_CONVERSATION,  on_archive_conversation      },
+        {ACTION_TRASH_CONVERSATION,    on_trash_conversation        },
+        {ACTION_DELETE_CONVERSATION,   on_delete_conversation       },
+        {ACTION_COPY_MENU,             on_show_copy_menu            },
+        {ACTION_MOVE_MENU,             on_show_move_menu            },
+        // Message marking actions
+        {ACTION_SHOW_MARK_MENU,     on_show_mark_menu           },
+        {ACTION_MARK_AS_READ,       on_mark_as_read             },
+        {ACTION_MARK_AS_UNREAD,     on_mark_as_unread           },
+        {ACTION_MARK_AS_STARRED,    on_mark_as_starred          },
+        {ACTION_MARK_AS_UNSTARRED,  on_mark_as_unstarred        },
+        {ACTION_MARK_AS_SPAM,       on_mark_as_spam_toggle      },
+        {ACTION_MARK_AS_NOT_SPAM,   on_mark_as_spam_toggle      },
+        // Message viewer
+        {ACTION_ZOOM,  on_zoom,  "s"  },
+    };
+
     /**
      * Fired when the currently selected account has changed.
      */
@@ -176,10 +178,6 @@ public class GearyController : Geary.BaseObject {
 
         apply_app_menu_fix();
 
-        // Setup actions.
-        setup_actions();
-        this.application.load_ui_resource("accelerators.ui");
-
         // Listen for attempts to close the application.
         this.application.exiting.connect(on_application_exiting);
 
@@ -224,9 +222,11 @@ public class GearyController : Geary.BaseObject {
         main_window = new MainWindow(this.application);
         main_window.on_shift_key.connect(on_shift_key);
         main_window.notify["has-toplevel-focus"].connect(on_has_toplevel_focus);
-        
+
+        setup_actions();
+
         enable_message_buttons(false);
-        
+
         Geary.Engine.instance.account_available.connect(on_account_available);
         Geary.Engine.instance.account_unavailable.connect(on_account_unavailable);
         Geary.Engine.instance.untrusted_host.connect(on_untrusted_host);
@@ -414,11 +414,6 @@ public class GearyController : Geary.BaseObject {
         }
     }
 
-    private void add_accelerator(string accelerator, string action) {
-        GtkUtil.add_accelerator(this.application.ui_manager, this.application.actions,
-            accelerator, action);
-    }
-
     // Fix for clients having both:
     //   * disabled Gtk/ShellShowsAppMenu setting
     //   * no 'menu' setting in Gtk/DecorationLayout
@@ -428,13 +423,10 @@ public class GearyController : Geary.BaseObject {
 
         if (settings == null) {
             warning("Couldn't fetch Gtk default settings");
-            return ;
+            return;
         }
 
-        string? decoration_layout = settings.gtk_decoration_layout;
-
-        if (decoration_layout == null) decoration_layout = "";
-
+        string decoration_layout = settings.gtk_decoration_layout ?? "";
         if (!decoration_layout.contains("menu")) {
             string prefix = "menu:";
             if (decoration_layout.contains(":")) {
@@ -444,177 +436,35 @@ public class GearyController : Geary.BaseObject {
         }
     }
 
-    private Gtk.ActionEntry[] create_actions() {
-        Gtk.ActionEntry[] entries = new Gtk.ActionEntry[0];
-
-        Gtk.ActionEntry mark_menu = { ACTION_MARK_AS_MENU, null, TRANSLATABLE, null, _("Mark conversation"),
-            on_show_mark_menu };
-        mark_menu.label = _("_Mark as…");
-        mark_menu.tooltip = MARK_MESSAGE_MENU_TOOLTIP_SINGLE;
-        entries += mark_menu;
-
-        Gtk.ActionEntry mark_read = { ACTION_MARK_AS_READ, "mail-mark-read", TRANSLATABLE, "<Ctrl>I",
-            null, on_mark_as_read };
-        mark_read.label = _("Mark as _Read");
-        entries += mark_read;
-        add_accelerator("<Shift>I", ACTION_MARK_AS_READ);
-
-        Gtk.ActionEntry mark_unread = { ACTION_MARK_AS_UNREAD, "mail-mark-unread", TRANSLATABLE,
-            "<Ctrl>U", null, on_mark_as_unread };
-        mark_unread.label = _("Mark as _Unread");
-        entries += mark_unread;
-        add_accelerator("<Shift>U", ACTION_MARK_AS_UNREAD);
-        
-        Gtk.ActionEntry mark_starred = { ACTION_MARK_AS_STARRED, "star-symbolic", TRANSLATABLE, "S", null,
-            on_mark_as_starred };
-        mark_starred.label = _("_Star");
-        entries += mark_starred;
-
-        Gtk.ActionEntry mark_unstarred = { ACTION_MARK_AS_UNSTARRED, "non-starred", TRANSLATABLE, "D",
-            null, on_mark_as_unstarred };
-        mark_unstarred.label = _("U_nstar");
-        entries += mark_unstarred;
-        
-        Gtk.ActionEntry mark_spam = { ACTION_MARK_AS_SPAM, null, TRANSLATABLE, "<Ctrl>J", null,
-            on_mark_as_spam };
-        mark_spam.label = MARK_AS_SPAM_LABEL;
-        entries += mark_spam;
-        add_accelerator("exclam", ACTION_MARK_AS_SPAM); // Exclamation mark (!)
-        
-        Gtk.ActionEntry copy_menu = { ACTION_COPY_MENU, null, TRANSLATABLE, "L",
-            _("Add label"), null };
-        copy_menu.label = _("_Label");
-        entries += copy_menu;
-
-        Gtk.ActionEntry move_menu = { ACTION_MOVE_MENU, null, TRANSLATABLE, "M", _("Move conversation"), 
null };
-        move_menu.label = _("_Move");
-        entries += move_menu;
-
-        Gtk.ActionEntry new_message = { ACTION_NEW_MESSAGE, null, null, "<Ctrl>N", 
-            _("Compose new message (Ctrl+N, N)"), on_new_message };
-        entries += new_message;
-        add_accelerator("N", ACTION_NEW_MESSAGE);
-
-        Gtk.ActionEntry reply_to_message = { ACTION_REPLY_TO_MESSAGE, null, _("_Reply"), "<Ctrl>R",
-            _("Reply (Ctrl+R, R)"), on_reply_to_message_action };
-        entries += reply_to_message;
-        add_accelerator("R", ACTION_REPLY_TO_MESSAGE);
-        
-        Gtk.ActionEntry reply_all_message = { ACTION_REPLY_ALL_MESSAGE, null, _("R_eply All"),
-            "<Ctrl><Shift>R", _("Reply all (Ctrl+Shift+R, Shift+R)"), 
-            on_reply_all_message_action };
-        entries += reply_all_message;
-        add_accelerator("<Shift>R", ACTION_REPLY_ALL_MESSAGE);
-        
-        Gtk.ActionEntry forward_message = { ACTION_FORWARD_MESSAGE, null, _("_Forward"), "<Ctrl>L", 
-            _("Forward (Ctrl+L, F)"), on_forward_message_action };
-        entries += forward_message;
-        add_accelerator("F", ACTION_FORWARD_MESSAGE);
-        
-        Gtk.ActionEntry find_in_conversation = { ACTION_FIND_IN_CONVERSATION, null, null, "<Ctrl>F",
-            null, on_find_in_conversation_action };
-        entries += find_in_conversation;
-        add_accelerator("slash", ACTION_FIND_IN_CONVERSATION);
-
-        Gtk.ActionEntry archive_conversation = { ACTION_ARCHIVE_CONVERSATION, ARCHIVE_CONVERSATION_ICON_NAME,
-            ARCHIVE_CONVERSATION_LABEL, "A", null, on_archive_conversation };
-        archive_conversation.tooltip = ARCHIVE_CONVERSATION_TOOLTIP_SINGLE;
-        entries += archive_conversation;
-        
-        // although this action changes according to the account's capabilities, set to Delete
-        // until they're known so the "translatable" string doesn't first appear
-        Gtk.ActionEntry trash_conversation = { ACTION_TRASH_CONVERSATION, TRASH_CONVERSATION_ICON_NAME,
-            null, "Delete", null, on_trash_conversation };
-        trash_conversation.tooltip = TRASH_CONVERSATION_TOOLTIP_SINGLE;
-        entries += trash_conversation;
-        add_accelerator("BackSpace", ACTION_TRASH_CONVERSATION);
-
-        Gtk.ActionEntry delete_conversation = { ACTION_DELETE_CONVERSATION, DELETE_CONVERSATION_ICON_NAME,
-            null, "<Shift>Delete", null, on_delete_conversation };
-        delete_conversation.label = DELETE_CONVERSATION_LABEL;
-        delete_conversation.tooltip = DELETE_CONVERSATION_TOOLTIP_SINGLE;
-        entries += delete_conversation;
-        add_accelerator("<Shift>BackSpace", ACTION_DELETE_CONVERSATION);
-        
-        Gtk.ActionEntry empty_spam = { ACTION_EMPTY_SPAM, null, null, null, null, on_empty_spam };
-        empty_spam.label = _("Empty _Spam…");
-        entries += empty_spam;
-        
-        Gtk.ActionEntry empty_trash = { ACTION_EMPTY_TRASH, null, null, null, null, on_empty_trash };
-        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;
-        add_accelerator("equal", ACTION_ZOOM_IN);
-
-        Gtk.ActionEntry zoom_out = { ACTION_ZOOM_OUT, null, null, "<Ctrl>minus",
-            null, on_zoom_out };
-        entries += zoom_out;
-        add_accelerator("minus", ACTION_ZOOM_OUT);
-
-        Gtk.ActionEntry zoom_normal = { ACTION_ZOOM_NORMAL, null, null, "<Ctrl>0",
-            null, on_zoom_normal };
-        entries += zoom_normal;
-        add_accelerator("0", ACTION_ZOOM_NORMAL);
-
-        Gtk.ActionEntry search = {
-            ACTION_SEARCH, null, null, "<Ctrl>S", null,
-            () => { show_search_bar(); }
-        };
-        entries += search;
-
-        Gtk.ActionEntry conversation_list = { ACTION_CONVERSATION_LIST, null, null, "<Ctrl>B", null, 
on_conversation_list };
-        entries += conversation_list;
-
-        // No callback is connected, since we bind the toggle button to the search bar visibility
-        Gtk.ActionEntry toggle_search = { ACTION_TOGGLE_SEARCH, null, null, null,
-            _("Toggle search bar"), null };
-        entries += toggle_search;
-
-        // No callback is connected, since we bind the toggle button to the find bar visibility
-        Gtk.ActionEntry toggle_find = { ACTION_TOGGLE_FIND, null, null, null,
-            _("Toggle find bar"), null };
-        entries += toggle_find;
-
-        return entries;
-    }
-
-    private Gtk.ToggleActionEntry[] create_toggle_actions() {
-        Gtk.ToggleActionEntry[] entries = new Gtk.ToggleActionEntry[0];
-        
-        return entries;
-    }
-    
     private void setup_actions() {
-        const string[] important_actions = {
-            ACTION_NEW_MESSAGE,
-            ACTION_REPLY_TO_MESSAGE,
-            ACTION_REPLY_ALL_MESSAGE,
-            ACTION_FORWARD_MESSAGE,
-            ACTION_ARCHIVE_CONVERSATION,
-            ACTION_TRASH_CONVERSATION,
-            ACTION_DELETE_CONVERSATION,
-        };
-        Gtk.ActionGroup action_group = this.application.actions;
-
-        Gtk.ActionEntry[] action_entries = create_actions();
-        action_group.add_actions(action_entries, this);
-        foreach (Gtk.ActionEntry e in action_entries) {
-            Gtk.Action action = action_group.get_action(e.name);
-            assert(action != null);
-            
-            if (e.name in important_actions)
-                action.is_important = true;
-        }
-
-        Gtk.ToggleActionEntry[] toggle_action_entries = create_toggle_actions();
-        action_group.add_toggle_actions(toggle_action_entries, this);
-        this.application.ui_manager.insert_action_group(action_group, 0);
+        this.main_window.add_action_entries(win_action_entries, this);
+
+        add_window_accelerators(ACTION_MARK_AS_READ, { "<Ctrl>I", "<Shift>I" });
+        add_window_accelerators(ACTION_MARK_AS_UNREAD, { "<Ctrl>U", "<Shift>U" });
+        add_window_accelerators(ACTION_MARK_AS_STARRED, { "S" });
+        add_window_accelerators(ACTION_MARK_AS_UNSTARRED, { "D" });
+        add_window_accelerators(ACTION_MARK_AS_SPAM, { "<Ctrl>J", "exclam" }); // Exclamation mark (!)
+        add_window_accelerators(ACTION_MARK_AS_NOT_SPAM, { "<Ctrl>J", "exclam" });
+        add_window_accelerators(ACTION_COPY_MENU, { "L" });
+        add_window_accelerators(ACTION_MOVE_MENU, { "M" });
+        add_window_accelerators(ACTION_NEW_MESSAGE, { "<Ctrl>N", "N" });
+        add_window_accelerators(ACTION_REPLY_TO_MESSAGE, { "<Ctrl>R", "R" });
+        add_window_accelerators(ACTION_REPLY_ALL_MESSAGE, { "<Ctrl><Shift>R", "<Shift>R" });
+        add_window_accelerators(ACTION_FORWARD_MESSAGE, { "<Ctrl>L", "F" });
+        add_window_accelerators(ACTION_FIND_IN_CONVERSATION, { "<Ctrl>F", "slash" });
+        add_window_accelerators(ACTION_ARCHIVE_CONVERSATION, { "A" });
+        add_window_accelerators(ACTION_TRASH_CONVERSATION, { "Delete", "BackSpace" });
+        add_window_accelerators(ACTION_DELETE_CONVERSATION, { "<Shift>Delete", "<Shift>BackSpace" });
+        add_window_accelerators(ACTION_UNDO, { "<Ctrl>Z" });
+        add_window_accelerators(ACTION_ZOOM+("('in')"), { "<Ctrl>equal", "equal" });
+        add_window_accelerators(ACTION_ZOOM+("('out')"), { "<Ctrl>minus", "minus" });
+        add_window_accelerators(ACTION_ZOOM+("('normal')"), { "<Ctrl>0", "0" });
+        add_window_accelerators(ACTION_SEARCH, { "<Ctrl>S" });
+        add_window_accelerators(ACTION_CONVERSATION_LIST, { "<Ctrl>B" });
+    }
+
+    private void add_window_accelerators(string action, string[] accelerators, Variant? param = null) {
+        this.application.set_accels_for_action("win."+action, accelerators);
     }
 
     private void open_account(Geary.Account account) {
@@ -1330,11 +1180,9 @@ public class GearyController : Geary.BaseObject {
     // Update widgets and such to match capabilities of the current folder ... sensitivity is handled
     // by other utility methods
     private void update_ui() {
-        update_tooltips();
-        main_window.main_toolbar.update_trash_button(
-            current_folder_supports_trash() ||
-            !(current_folder is Geary.FolderSupport.Remove)
-        );
+        main_window.main_toolbar.selected_conversations = this.selected_conversations.size;
+        main_window.main_toolbar.show_trash_button = current_folder_supports_trash() ||
+                                                    !(current_folder is Geary.FolderSupport.Remove);
     }
 
     private void on_folder_selected(Geary.Folder? folder) {
@@ -1346,9 +1194,7 @@ public class GearyController : Geary.BaseObject {
             folder_selected(null);
         } else if (folder != this.current_folder) {
             this.main_window.conversation_viewer.show_loading();
-            this.application.get_action(
-                ACTION_FIND_IN_CONVERSATION
-            ).set_sensitive(false);
+            get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
             enable_message_buttons(false);
 
             // To prevent the user from selecting folders too quickly,
@@ -1531,7 +1377,7 @@ public class GearyController : Geary.BaseObject {
     
     private void on_indicator_activated_composer(uint32 timestamp) {
         on_indicator_activated_application(timestamp);
-        on_new_message();
+        on_new_message(null);
     }
     
     private void on_indicator_activated_inbox(Geary.Folder folder, uint32 timestamp) {
@@ -1554,9 +1400,7 @@ public class GearyController : Geary.BaseObject {
 
     private void on_conversations_selected(Gee.Set<Geary.App.Conversation> selected) {
         this.selected_conversations = selected;
-        this.application.get_action(
-            ACTION_FIND_IN_CONVERSATION
-            ).set_sensitive(false);
+        get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
         ConversationViewer viewer = this.main_window.conversation_viewer;
         if (this.current_folder != null && !viewer.is_composer_visible) {
             switch(selected.size) {
@@ -1575,9 +1419,7 @@ public class GearyController : Geary.BaseObject {
                         try {
                             viewer.load_conversation.end(ret);
                             enable_message_buttons(true);
-                            this.application.get_action(
-                                ACTION_FIND_IN_CONVERSATION
-                            ).set_sensitive(true);
+                            get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(true);
                         } catch (Error err) {
                             debug("Unable to load conversation: %s",
                                   err.message);
@@ -1754,8 +1596,9 @@ public class GearyController : Geary.BaseObject {
     private void on_shift_key(bool pressed) {
         if (main_window != null && main_window.main_toolbar != null
             && current_account != null && current_folder != null) {
-            main_window.main_toolbar.update_trash_button(
-                (!pressed && current_folder_supports_trash()) || !(current_folder is 
Geary.FolderSupport.Remove));
+            main_window.main_toolbar.show_trash_button =
+                (!pressed && current_folder_supports_trash()) ||
+                !(current_folder is Geary.FolderSupport.Remove);
         }
     }
 
@@ -1832,30 +1675,19 @@ public class GearyController : Geary.BaseObject {
                 unstarred_selected = true;
             }
         }
-        var actions = this.application.actions;
-        actions.get_action(ACTION_MARK_AS_READ).set_visible(unread_selected);
-        actions.get_action(ACTION_MARK_AS_UNREAD).set_visible(read_selected);
-        actions.get_action(ACTION_MARK_AS_STARRED).set_visible(unstarred_selected);
-        actions.get_action(ACTION_MARK_AS_UNSTARRED).set_visible(starred_selected);
-        
-        if (current_folder.special_folder_type != Geary.SpecialFolderType.DRAFTS &&
-            current_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX) {
-            if (current_folder.special_folder_type == Geary.SpecialFolderType.SPAM) {
-                // We're in the spam folder.
-                actions.get_action(ACTION_MARK_AS_SPAM).sensitive = true;
-                actions.get_action(ACTION_MARK_AS_SPAM).label = MARK_AS_NOT_SPAM_LABEL;
-            } else {
-                // We're not in the spam folder, but we are in a folder that allows mark-as-spam.
-                actions.get_action(ACTION_MARK_AS_SPAM).sensitive = true;
-                actions.get_action(ACTION_MARK_AS_SPAM).label = MARK_AS_SPAM_LABEL;
-            }
-        } else {
-            // We're in Drafts/Outbox, so gray-out the option.
-            actions.get_action(ACTION_MARK_AS_SPAM).sensitive = false;
-            actions.get_action(ACTION_MARK_AS_SPAM).label = MARK_AS_SPAM_LABEL;
-        }
+        get_window_action(ACTION_MARK_AS_READ).set_enabled(unread_selected);
+        get_window_action(ACTION_MARK_AS_UNREAD).set_enabled(read_selected);
+        get_window_action(ACTION_MARK_AS_STARRED).set_enabled(unstarred_selected);
+        get_window_action(ACTION_MARK_AS_UNSTARRED).set_enabled(starred_selected);
+
+        bool in_spam_folder = current_folder.special_folder_type == Geary.SpecialFolderType.SPAM;
+        get_window_action(ACTION_MARK_AS_NOT_SPAM).set_enabled(in_spam_folder);
+        // If we're in Drafts/Outbox, we also shouldn't set a message as SPAM.
+        get_window_action(ACTION_MARK_AS_SPAM).set_enabled(!in_spam_folder &&
+            current_folder.special_folder_type != Geary.SpecialFolderType.DRAFTS &&
+            current_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX);
     }
-    
+
     private void on_visible_conversations_changed(Gee.Set<Geary.App.Conversation> visible) {
         clear_new_messages("on_visible_conversations_changed", visible);
     }
@@ -1900,11 +1732,11 @@ public class GearyController : Geary.BaseObject {
         Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove) {
         mark_email(emails, flags_to_add, flags_to_remove);
     }
-    
-    private void on_mark_as_read() {
+
+    private void on_mark_as_read(SimpleAction action) {
         Geary.EmailFlags flags = new Geary.EmailFlags();
         flags.add(Geary.EmailFlags.UNREAD);
-        
+
         Gee.ArrayList<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
         mark_email(ids, null, flags);
 
@@ -1916,10 +1748,10 @@ public class GearyController : Geary.BaseObject {
         }
     }
 
-    private void on_mark_as_unread() {
+    private void on_mark_as_unread(SimpleAction action) {
         Geary.EmailFlags flags = new Geary.EmailFlags();
         flags.add(Geary.EmailFlags.UNREAD);
-        
+
         Gee.ArrayList<Geary.EmailIdentifier> ids = get_selected_email_ids(true);
         mark_email(ids, flags, null);
 
@@ -1931,19 +1763,27 @@ public class GearyController : Geary.BaseObject {
         }
     }
 
-    private void on_mark_as_starred() {
+    private void on_mark_as_starred(SimpleAction action) {
         Geary.EmailFlags flags = new Geary.EmailFlags();
         flags.add(Geary.EmailFlags.FLAGGED);
         mark_email(get_selected_email_ids(true), flags, null);
     }
 
-    private void on_mark_as_unstarred() {
+    private void on_mark_as_unstarred(SimpleAction action) {
         Geary.EmailFlags flags = new Geary.EmailFlags();
         flags.add(Geary.EmailFlags.FLAGGED);
         mark_email(get_selected_email_ids(false), null, flags);
     }
-    
-    private async void mark_as_spam_async(Cancellable? cancellable) {
+
+    private void on_show_move_menu(SimpleAction? action) {
+        this.main_window.main_toolbar.copy_message_button.clicked();
+    }
+
+    private void on_show_copy_menu(SimpleAction? action) {
+        this.main_window.main_toolbar.move_message_button.clicked();
+    }
+
+    private async void mark_as_spam_toggle_async(Cancellable? cancellable) {
         Geary.Folder? destination_folder = null;
         if (current_folder.special_folder_type != Geary.SpecialFolderType.SPAM) {
             // Move to spam folder.
@@ -1961,15 +1801,15 @@ public class GearyController : Geary.BaseObject {
                 debug("Error getting inbox folder: %s", e.message);
             }
         }
-        
+
         if (destination_folder != null)
             on_move_conversation(destination_folder);
     }
-    
-    private void on_mark_as_spam() {
-        mark_as_spam_async.begin(null);
+
+    private void on_mark_as_spam_toggle(SimpleAction action) {
+        mark_as_spam_toggle_async.begin(null);
     }
-    
+
     private void copy_email(Gee.Collection<Geary.EmailIdentifier> ids,
         Geary.FolderPath destination) {
         if (ids.size > 0) {
@@ -2284,9 +2124,7 @@ public class GearyController : Geary.BaseObject {
             if (widget.state == ComposerWidget.ComposerState.NEW ||
                 widget.state == ComposerWidget.ComposerState.PANED) {
                 main_window.conversation_viewer.do_compose(widget);
-                this.application.get_action(
-                    ACTION_FIND_IN_CONVERSATION
-                ).set_sensitive(false);
+                get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
             } else {
                 main_window.conversation_viewer.do_compose_embedded(
                     widget,
@@ -2401,7 +2239,7 @@ public class GearyController : Geary.BaseObject {
         }
     }
     
-    private void on_new_message() {
+    private void on_new_message(SimpleAction? action) {
         create_compose_widget(ComposerWidget.ComposeType.NEW_MESSAGE);
     }
 
@@ -2409,7 +2247,7 @@ public class GearyController : Geary.BaseObject {
         create_reply_forward_widget(ComposerWidget.ComposeType.REPLY, target_view);
     }
 
-    private void on_reply_to_message_action() {
+    private void on_reply_to_message_action(SimpleAction action) {
         create_reply_forward_widget(ComposerWidget.ComposeType.REPLY, null);
     }
 
@@ -2417,7 +2255,7 @@ public class GearyController : Geary.BaseObject {
         create_reply_forward_widget(ComposerWidget.ComposeType.REPLY_ALL, target_view);
     }
 
-    private void on_reply_all_message_action() {
+    private void on_reply_all_message_action(SimpleAction action) {
         create_reply_forward_widget(ComposerWidget.ComposeType.REPLY_ALL, null);
     }
 
@@ -2425,37 +2263,41 @@ public class GearyController : Geary.BaseObject {
         create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, target_view);
     }
 
-    private void on_forward_message_action() {
+    private void on_forward_message_action(SimpleAction action) {
         create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, null);
     }
 
-    private void on_find_in_conversation_action() {
+    private void on_find_in_conversation_action(SimpleAction action) {
         this.main_window.conversation_viewer.conversation_find_bar.set_search_mode(true);
     }
 
-    private void on_archive_conversation() {
+    private void on_search_activated(SimpleAction action) {
+        show_search_bar();
+    }
+
+    private void on_archive_conversation(SimpleAction action) {
         archive_or_delete_selection_async.begin(true, false, cancellable_folder,
             on_archive_or_delete_selection_finished);
     }
-    
-    private void on_trash_conversation() {
+
+    private void on_trash_conversation(SimpleAction action) {
         archive_or_delete_selection_async.begin(false, true, cancellable_folder,
             on_archive_or_delete_selection_finished);
     }
-    
-    private void on_delete_conversation() {
+
+    private void on_delete_conversation(SimpleAction action) {
         archive_or_delete_selection_async.begin(false, false, cancellable_folder,
             on_archive_or_delete_selection_finished);
     }
-    
-    private void on_empty_spam() {
+
+    private void on_empty_spam(SimpleAction action) {
         on_empty_trash_or_spam(Geary.SpecialFolderType.SPAM);
     }
-    
-    private void on_empty_trash() {
+
+    private void on_empty_trash(SimpleAction action) {
         on_empty_trash_or_spam(Geary.SpecialFolderType.TRASH);
     }
-    
+
     private void on_empty_trash_or_spam(Geary.SpecialFolderType special_folder_type) {
         // Account must be in place, must have the specified special folder type, and that folder
         // must support Empty in order for this command to proceed
@@ -2636,17 +2478,18 @@ public class GearyController : Geary.BaseObject {
             revokable.committed.connect(on_revokable_committed);
         }
 
-        Gtk.Action undo_action = this.application.get_action(ACTION_UNDO);
-        undo_action.tooltip = (revokable != null && description != null) ? description : _("Undo (Ctrl+Z)");
+        if (revokable != null && description != null)
+            this.main_window.main_toolbar.undo_tooltip = description;
+        else
+            this.main_window.main_toolbar.undo_tooltip = _("Undo (Ctrl+Z)");
 
         update_revokable_action();
     }
-    
+
     private void update_revokable_action() {
-        Gtk.Action undo_action = this.application.get_action(ACTION_UNDO);
-        undo_action.sensitive = revokable != null && revokable.valid && !revokable.in_process;
+        get_window_action(ACTION_UNDO).set_enabled(this.revokable != null && this.revokable.valid && 
!this.revokable.in_process);
     }
-    
+
     private void on_revokable_valid_changed() {
         // remove revokable if it goes invalid
         if (revokable != null && !revokable.valid)
@@ -2658,10 +2501,9 @@ public class GearyController : Geary.BaseObject {
             return;
 
         // use existing description
-        Gtk.Action undo_action = this.application.get_action(ACTION_UNDO);
-        save_revokable(committed_revokable, undo_action.tooltip);
+        save_revokable(committed_revokable, this.main_window.main_toolbar.undo_tooltip);
     }
-    
+
     private void on_revoke() {
         if (revokable != null && revokable.valid)
             revokable.revoke_async.begin(null, on_revoke_completed);
@@ -2681,27 +2523,16 @@ public class GearyController : Geary.BaseObject {
         }
     }
 
-    private void on_zoom_in() {
-        ConversationListBox? view =
-            main_window.conversation_viewer.current_list;
-        if (view != null) {
-            view.zoom_in();
-        }
-    }
-
-    private void on_zoom_out() {
-        ConversationListBox? view =
-            main_window.conversation_viewer.current_list;
-        if (view != null) {
-            view.zoom_out();
-        }
-    }
-
-    private void on_zoom_normal() {
-        ConversationListBox? view =
-            main_window.conversation_viewer.current_list;
-        if (view != null) {
-            view.zoom_reset();
+    private void on_zoom(SimpleAction action, Variant? parameter) {
+        ConversationListBox? view = main_window.conversation_viewer.current_list;
+        if (view != null && parameter != null) {
+            string zoom_action = parameter.get_string();
+            if (zoom_action == "in")
+                view.zoom_in();
+            else if (zoom_action == "out")
+                view.zoom_out();
+            else
+                view.zoom_reset();
         }
     }
 
@@ -2775,24 +2606,24 @@ public class GearyController : Geary.BaseObject {
         }
     }
 
+    private SimpleAction get_window_action(string action_name) {
+        return (SimpleAction) this.main_window.lookup_action(action_name);
+    }
+
     // Disables all single-message buttons and enables all multi-message buttons.
     public void enable_multiple_message_buttons() {
-        update_tooltips();
+        main_window.main_toolbar.selected_conversations = this.selected_conversations.size;
 
         // Single message only buttons.
-        this.application.actions.get_action(ACTION_REPLY_TO_MESSAGE).sensitive = false;
-        this.application.actions.get_action(ACTION_REPLY_ALL_MESSAGE).sensitive = false;
-        this.application.actions.get_action(ACTION_FORWARD_MESSAGE).sensitive = false;
+        get_window_action(ACTION_REPLY_TO_MESSAGE).set_enabled(false);
+        get_window_action(ACTION_REPLY_ALL_MESSAGE).set_enabled(false);
+        get_window_action(ACTION_FORWARD_MESSAGE).set_enabled(false);
 
         // Mutliple message buttons.
-        this.application.actions.get_action(ACTION_MOVE_MENU).sensitive =
-            (current_folder is Geary.FolderSupport.Move);
-        this.application.actions.get_action(ACTION_ARCHIVE_CONVERSATION).sensitive =
-            (current_folder is Geary.FolderSupport.Archive);
-        this.application.actions.get_action(ACTION_TRASH_CONVERSATION).sensitive =
-            current_folder_supports_trash();
-        this.application.actions.get_action(ACTION_DELETE_CONVERSATION).sensitive =
-            (current_folder is Geary.FolderSupport.Remove);
+        get_window_action(ACTION_MOVE_MENU).set_enabled(current_folder is Geary.FolderSupport.Move);
+        get_window_action(ACTION_ARCHIVE_CONVERSATION).set_enabled(current_folder is 
Geary.FolderSupport.Archive);
+        get_window_action(ACTION_TRASH_CONVERSATION).set_enabled(current_folder_supports_trash());
+        get_window_action(ACTION_DELETE_CONVERSATION).set_enabled(current_folder is 
Geary.FolderSupport.Remove);
 
         cancel_context_dependent_buttons();
         enable_context_dependent_buttons_async.begin(true, cancellable_context_dependent_buttons);
@@ -2800,29 +2631,25 @@ public class GearyController : Geary.BaseObject {
 
     // Enables or disables the message buttons on the toolbar.
     public void enable_message_buttons(bool sensitive) {
-        update_tooltips();
-        
+        main_window.main_toolbar.selected_conversations = this.selected_conversations.size;
+
         // No reply/forward in drafts folder.
         bool respond_sensitive = sensitive;
         if (current_folder != null && current_folder.special_folder_type == Geary.SpecialFolderType.DRAFTS)
             respond_sensitive = false;
 
-        this.application.actions.get_action(ACTION_REPLY_TO_MESSAGE).sensitive = respond_sensitive;
-        this.application.actions.get_action(ACTION_REPLY_ALL_MESSAGE).sensitive = respond_sensitive;
-        this.application.actions.get_action(ACTION_FORWARD_MESSAGE).sensitive = respond_sensitive;
-        this.application.actions.get_action(ACTION_MOVE_MENU).sensitive =
-            sensitive && (current_folder is Geary.FolderSupport.Move);
-        this.application.actions.get_action(ACTION_ARCHIVE_CONVERSATION).sensitive = sensitive
-            && (current_folder is Geary.FolderSupport.Archive);
-        this.application.actions.get_action(ACTION_TRASH_CONVERSATION).sensitive = sensitive
-            && current_folder_supports_trash();
-        this.application.actions.get_action(ACTION_DELETE_CONVERSATION).sensitive = sensitive
-            && (current_folder is Geary.FolderSupport.Remove);
+        get_window_action(ACTION_REPLY_TO_MESSAGE).set_enabled(respond_sensitive);
+        get_window_action(ACTION_REPLY_ALL_MESSAGE).set_enabled(respond_sensitive);
+        get_window_action(ACTION_FORWARD_MESSAGE).set_enabled(respond_sensitive);
+        get_window_action(ACTION_MOVE_MENU).set_enabled(sensitive && (current_folder is 
Geary.FolderSupport.Move));
+        get_window_action(ACTION_ARCHIVE_CONVERSATION).set_enabled(sensitive && (current_folder is 
Geary.FolderSupport.Archive));
+        get_window_action(ACTION_TRASH_CONVERSATION).set_enabled(sensitive && 
current_folder_supports_trash());
+        get_window_action(ACTION_DELETE_CONVERSATION).set_enabled(sensitive && (current_folder is 
Geary.FolderSupport.Remove));
 
         cancel_context_dependent_buttons();
         enable_context_dependent_buttons_async.begin(sensitive, cancellable_context_dependent_buttons);
     }
-    
+
     private async void enable_context_dependent_buttons_async(bool sensitive, Cancellable? cancellable) {
         Gee.MultiMap<Geary.EmailIdentifier, Type>? selected_operations = null;
         try {
@@ -2846,29 +2673,8 @@ public class GearyController : Geary.BaseObject {
         if (selected_operations != null)
             supported_operations.add_all(selected_operations.get_values());
 
-        this.application.actions.get_action(ACTION_MARK_AS_MENU).sensitive =
-            sensitive && (supported_operations.contains(typeof(Geary.FolderSupport.Mark)));
-        this.application.actions.get_action(ACTION_COPY_MENU).sensitive =
-            sensitive && (supported_operations.contains(typeof(Geary.FolderSupport.Copy)));
-    }
-
-    // Updates tooltip text depending on number of conversations selected.
-    private void update_tooltips() {
-        bool single = selected_conversations.size == 1;
-
-        this.application.actions.get_action(ACTION_MARK_AS_MENU).tooltip = single ?
-            MARK_MESSAGE_MENU_TOOLTIP_SINGLE : MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE;
-        this.application.actions.get_action(ACTION_COPY_MENU).tooltip = single ?
-            LABEL_MESSAGE_TOOLTIP_SINGLE : LABEL_MESSAGE_TOOLTIP_MULTIPLE;
-        this.application.actions.get_action(ACTION_MOVE_MENU).tooltip = single ?
-            MOVE_MESSAGE_TOOLTIP_SINGLE : MOVE_MESSAGE_TOOLTIP_MULTIPLE;
-
-        this.application.actions.get_action(ACTION_ARCHIVE_CONVERSATION).tooltip = single ?
-            ARCHIVE_CONVERSATION_TOOLTIP_SINGLE : ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE;
-        this.application.actions.get_action(ACTION_TRASH_CONVERSATION).tooltip = single ?
-            TRASH_CONVERSATION_TOOLTIP_SINGLE : TRASH_CONVERSATION_TOOLTIP_MULTIPLE;
-        this.application.actions.get_action(ACTION_DELETE_CONVERSATION).tooltip = single ?
-            DELETE_CONVERSATION_TOOLTIP_SINGLE : DELETE_CONVERSATION_TOOLTIP_MULTIPLE;
+        get_window_action(ACTION_SHOW_MARK_MENU).set_enabled(sensitive && (typeof(Geary.FolderSupport.Mark) 
in supported_operations));
+        get_window_action(ACTION_COPY_MENU).set_enabled(sensitive && 
(supported_operations.contains(typeof(Geary.FolderSupport.Copy))));
     }
 
     // Returns a list of composer windows for an account, or null if none.
diff --git a/src/client/components/folder-popover.vala b/src/client/components/folder-popover.vala
index 9b377c8..99326ab 100644
--- a/src/client/components/folder-popover.vala
+++ b/src/client/components/folder-popover.vala
@@ -79,6 +79,8 @@ public class FolderPopover : Gtk.Popover {
         label.set_halign(Gtk.Align.START);
         row.add(label);
 
+        row.show_all();
+
         return row;
     }
 
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 3c35113..67fc5d5 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -1,4 +1,4 @@
-/* Copyright 2016 Software Freedom Conservancy Inc.
+/* Copyright 2017 Software Freedom Conservancy Inc.
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
@@ -7,24 +7,35 @@
 // Draws the main toolbar.
 [GtkTemplate (ui = "/org/gnome/Geary/main-toolbar.ui")]
 public class MainToolbar : Gtk.Box {
-    private Gtk.ActionGroup action_group;
-    public FolderPopover copy_folder_menu { get; private set; default = new FolderPopover(); }
-    public FolderPopover move_folder_menu { get; private set; default = new FolderPopover(); }
+    // How wide the left pane should be. Auto-synced with our settings
+    public int left_pane_width { get; set; }
+    // Used to form the title of the folder header
     public string account { get; set; }
     public string folder { get; set; }
+    // Close button settings
     public bool show_close_button { get; set; default = false; }
     public bool show_close_button_left { get; private set; default = true; }
     public bool show_close_button_right { get; private set; default = true; }
+    // Search and find bar
     public bool search_open { get; set; default = false; }
     public bool find_open { get; set; default = false; }
-    public int left_pane_width { get; set; }
+    // Copy and Move popovers
+    public FolderPopover copy_folder_menu { get; private set; default = new FolderPopover(); }
+    public FolderPopover move_folder_menu { get; private set; default = new FolderPopover(); }
+    // How many conversations are selected right now. Should automatically be updated.
+    public int selected_conversations { get; set; }
+    // Whether to show the trash or the delete button
+    public bool show_trash_button { get; set; default = true; }
+    // The tooltip of the Undo-button
+    public string undo_tooltip {
+        owned get { return this.undo_button.tooltip_text; }
+        set { this.undo_button.tooltip_text = value; }
+    }
 
     // Folder header elements
     [GtkChild]
     private Gtk.HeaderBar folder_header;
     [GtkChild]
-    private Gtk.Button compose_new_message_button;
-    [GtkChild]
     private Gtk.MenuButton empty_menu_button;
     [GtkChild]
     private Gtk.ToggleButton search_conversations_button;
@@ -34,90 +45,85 @@ public class MainToolbar : Gtk.Box {
     [GtkChild]
     private Gtk.HeaderBar conversation_header;
     [GtkChild]
-    private Gtk.Button reply_sender_button;
-    [GtkChild]
-    private Gtk.Button reply_all_button;
-    [GtkChild]
-    private Gtk.Button forward_button;
-    [GtkChild]
     private Gtk.MenuButton mark_message_button;
     [GtkChild]
-    private Gtk.MenuButton copy_message_button;
+    public Gtk.MenuButton copy_message_button;
     [GtkChild]
-    private Gtk.MenuButton move_message_button;
+    public Gtk.MenuButton move_message_button;
     [GtkChild]
     private Gtk.Button archive_button;
     [GtkChild]
     private Gtk.Button trash_delete_button;
     [GtkChild]
-    private Gtk.Button undo_button;
-    [GtkChild]
     private Gtk.ToggleButton find_button;
 
-    public MainToolbar(Configuration config) {
-        this.action_group = GearyApplication.instance.actions;
+    // Other
+    [GtkChild]
+    private Gtk.Button undo_button;
 
+    // Load these at construction time
+    private Gtk.Image trash_image = new Gtk.Image.from_icon_name("user-trash-symbolic", Gtk.IconSize.MENU);
+    private Gtk.Image delete_image = new Gtk.Image.from_icon_name("edit-delete-symbolic", Gtk.IconSize.MENU);
+
+    // Tooltips
+    private const string DELETE_CONVERSATION_TOOLTIP_SINGLE = _("Delete conversation (Shift+Delete)");
+    private const string DELETE_CONVERSATION_TOOLTIP_MULTIPLE = _("Delete conversations (Shift+Delete)");
+    private const string TRASH_CONVERSATION_TOOLTIP_SINGLE = _("Move conversation to Trash (Delete, 
Backspace)");
+    private const string TRASH_CONVERSATION_TOOLTIP_MULTIPLE = _("Move conversations to Trash (Delete, 
Backspace)");
+    private const string ARCHIVE_CONVERSATION_TOOLTIP_SINGLE = _("Archive conversation (A)");
+    private const string ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE = _("Archive conversations (A)");
+    private const string MARK_MESSAGE_MENU_TOOLTIP_SINGLE = _("Mark conversation");
+    private const string MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE = _("Mark conversations");
+    private const string LABEL_MESSAGE_TOOLTIP_SINGLE = _("Add label to conversation");
+    private const string LABEL_MESSAGE_TOOLTIP_MULTIPLE = _("Add label to conversations");
+    private const string MOVE_MESSAGE_TOOLTIP_SINGLE = _("Move conversation");
+    private const string MOVE_MESSAGE_TOOLTIP_MULTIPLE = _("Move conversations");
+
+    public MainToolbar(Configuration config) {
         // Instead of putting a separator between the two headerbars, as other applications do,
         // we put a separator at the right end of the left headerbar.  This greatly improves
         // the appearance under the Ambiance theme (see bug #746171).  To get this separator to
         // line up with the handle of the pane, we need to extend the width of the left-hand
         // headerbar a bit.  Six pixels is right both for Adwaita and Ambiance.
-        GearyApplication.instance.config.bind(Configuration.MESSAGES_PANE_POSITION_KEY,
-            this, "left-pane-width", SettingsBindFlags.GET);
-        this.bind_property("left-pane-width", folder_header, "width-request",
+        config.bind(Configuration.MESSAGES_PANE_POSITION_KEY, this, "left-pane-width",
+            SettingsBindFlags.GET);
+        this.bind_property("left-pane-width", this.folder_header, "width-request",
             BindingFlags.SYNC_CREATE, (binding, source_value, ref target_value) => {
                 target_value = left_pane_width + 6;
                 return true;
             });
 
         if (config.desktop_environment != Configuration.DesktopEnvironment.UNITY) {
-            this.bind_property("account", folder_header, "title", BindingFlags.SYNC_CREATE);
-            this.bind_property("folder", folder_header, "subtitle", BindingFlags.SYNC_CREATE);
+            this.bind_property("account", this.folder_header, "title", BindingFlags.SYNC_CREATE);
+            this.bind_property("folder", this.folder_header, "subtitle", BindingFlags.SYNC_CREATE);
         }
-        this.bind_property("show-close-button-left", folder_header, "show-close-button",
+        this.bind_property("show-close-button-left", this.folder_header, "show-close-button",
             BindingFlags.SYNC_CREATE);
-        this.bind_property("show-close-button-right", conversation_header, "show-close-button",
+        this.bind_property("show-close-button-right", this.conversation_header, "show-close-button",
             BindingFlags.SYNC_CREATE);
 
         // Assemble the empty/mark menus
-        GearyApplication.instance.load_ui_resource("toolbar_empty_menu.ui");
-        Gtk.Menu empty_menu = (Gtk.Menu) 
GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarEmptyMenu");
-        GearyApplication.instance.load_ui_resource("toolbar_mark_menu.ui");
-        Gtk.Menu mark_menu = (Gtk.Menu) 
GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMarkMenu");
+        Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui");
+        MenuModel empty_menu = (MenuModel) builder.get_object("empty_menu");
+        MenuModel mark_menu = (MenuModel) builder.get_object("mark_message_menu");
 
         // Setup folder header elements
-        setup_button(compose_new_message_button, GearyController.ACTION_NEW_MESSAGE);
-        empty_menu_button.popup = empty_menu;
-
-        setup_button(search_conversations_button, GearyController.ACTION_TOGGLE_SEARCH);
-        this.bind_property("search-open", search_conversations_button, "active",
+        this.empty_menu_button.popover = new Gtk.Popover.from_model(null, empty_menu);
+        this.bind_property("search-open", this.search_conversations_button, "active",
             BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
 
         // Setup conversation header elements
-        setup_button(reply_sender_button, GearyController.ACTION_REPLY_TO_MESSAGE);
-        setup_button(reply_all_button, GearyController.ACTION_REPLY_ALL_MESSAGE);
-        setup_button(forward_button, GearyController.ACTION_FORWARD_MESSAGE);
-
-        setup_menu_button(mark_message_button, mark_menu, GearyController.ACTION_MARK_AS_MENU);
-        setup_popover_button(copy_message_button, copy_folder_menu, GearyController.ACTION_COPY_MENU);
-        setup_popover_button(move_message_button, move_folder_menu, GearyController.ACTION_MOVE_MENU);
+        this.notify["selected-conversations"].connect(() => update_conversation_buttons());
+        this.notify["show-trash-button"].connect(() => update_conversation_buttons());
+        this.mark_message_button.popover = new Gtk.Popover.from_model(null, mark_menu);
+        this.copy_message_button.popover = copy_folder_menu;
+        this.move_message_button.popover = move_folder_menu;
 
-        setup_button(archive_button, GearyController.ACTION_ARCHIVE_CONVERSATION, true);
-        setup_button(trash_delete_button, GearyController.ACTION_TRASH_CONVERSATION);
-        setup_button(undo_button, GearyController.ACTION_UNDO);
-
-        setup_button(find_button, GearyController.ACTION_TOGGLE_FIND);
-        this.bind_property("find-open", find_button, "active",
+        this.bind_property("find-open", this.find_button, "active",
             BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
 
         Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons);
-        realize.connect(set_window_buttons);
-    }
-
-    public void update_trash_button(bool is_trash) {
-        string action_name = (is_trash ? GearyController.ACTION_TRASH_CONVERSATION
-            : GearyController.ACTION_DELETE_CONVERSATION);
-        setup_button(trash_delete_button, action_name, false);
+        this.realize.connect(set_window_buttons);
     }
 
     public void set_conversation_header(Gtk.HeaderBar header) {
@@ -152,71 +158,33 @@ public class MainToolbar : Gtk.Box {
         conversation_header.decoration_layout = ":" + buttons[1];
     }
 
-    private void setup_button(Gtk.Button b, string action_name, bool show_label = false) {
-        Gtk.Action related_action = action_group.get_action(action_name);
-        b.focus_on_click = false;
-        b.use_underline = true;
-        b.tooltip_text = related_action.tooltip;
-        related_action.notify["tooltip"].connect(() => { b.tooltip_text = related_action.tooltip; });
-        b.related_action = related_action;
-
-        // Load icon by name with this fallback order: specified icon name, the action's icon name,
-        // the action's stock ID ... although stock IDs are being deprecated, that's how we specify
-        // the icon in the GtkActionEntry (also being deprecated) and GTK+ 3.14 doesn't support that
-        // any longer
-        string? icon_to_load = b.related_action.icon_name;
-        if (icon_to_load == null)
-            icon_to_load = b.related_action.stock_id;
-
-        // set pixel size to force GTK+ to load our images from our installed directory, not the theme
-        // directory
-        if (icon_to_load != null) {
-            Gtk.Image image = new Gtk.Image.from_icon_name(icon_to_load, Gtk.IconSize.MENU);
-            image.set_pixel_size(16);
-            b.image = image;
-        }
-
-        b.always_show_image = true;
-
-        if (show_label)
-            b.label = related_action.label;
-        else
-            b.label = null;
-    }
-
-    /**
-     * Given an icon, menu, and action, creates a button that triggers the menu and the action.
-     */
-    private void setup_menu_button(Gtk.MenuButton b, Gtk.Menu menu, string action_name) {
-        setup_button(b, action_name);
-        menu.foreach(GtkUtil.show_menuitem_accel_labels);
-        b.popup = menu;
-
-        if (b.related_action != null) {
-            b.related_action.activate.connect(() => {
-                    b.clicked();
-                });
-            // Null out the action since by connecting it to clicked
-            // above, invoking would cause an infinite loop otherwise.
-            b.related_action = null;
-        }
-    }
-
-    /**
-     * Given an icon, popover, and action, creates a button that triggers the popover and the action.
-     */
-    private void setup_popover_button(Gtk.MenuButton b, Gtk.Popover popover, string action_name) {
-        setup_button(b, action_name);
-        b.popover = popover;
-        b.clicked.connect(() => popover.show_all());
-
-        if (b.related_action != null) {
-            b.related_action.activate.connect(() => {
-                    b.clicked();
-                });
-            // Null out the action since by connecting it to clicked
-            // above, invoking would cause an infinite loop otherwise.
-            b.related_action = null;
+    // Updates tooltip text depending on number of conversations selected.
+    private void update_conversation_buttons() {
+        this.mark_message_button.tooltip_text = ngettext(MARK_MESSAGE_MENU_TOOLTIP_SINGLE,
+                                                         MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE,
+                                                         this.selected_conversations);
+        this.copy_message_button.tooltip_text = ngettext(LABEL_MESSAGE_TOOLTIP_SINGLE,
+                                                         LABEL_MESSAGE_TOOLTIP_MULTIPLE,
+                                                         this.selected_conversations);
+        this.move_message_button.tooltip_text = ngettext(MOVE_MESSAGE_TOOLTIP_SINGLE,
+                                                         MOVE_MESSAGE_TOOLTIP_MULTIPLE,
+                                                         this.selected_conversations);
+        this.archive_button.tooltip_text = ngettext(ARCHIVE_CONVERSATION_TOOLTIP_SINGLE,
+                                                    ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE,
+                                                    this.selected_conversations);
+
+        if (this.show_trash_button) {
+            this.trash_delete_button.action_name = "win."+GearyController.ACTION_TRASH_CONVERSATION;
+            this.trash_delete_button.image = trash_image;
+            this.trash_delete_button.tooltip_text = ngettext(TRASH_CONVERSATION_TOOLTIP_SINGLE,
+                                                             TRASH_CONVERSATION_TOOLTIP_MULTIPLE,
+                                                             this.selected_conversations);
+        } else {
+            this.trash_delete_button.action_name = "win."+GearyController.ACTION_DELETE_CONVERSATION;
+            this.trash_delete_button.image = delete_image;
+            this.trash_delete_button.tooltip_text = ngettext(DELETE_CONVERSATION_TOOLTIP_SINGLE,
+                                                             DELETE_CONVERSATION_TOOLTIP_MULTIPLE,
+                                                             this.selected_conversations);
         }
     }
 }
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index d017107..13fedf7 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -29,7 +29,7 @@ public class MainWindow : Gtk.ApplicationWindow {
     public FolderList.Tree folder_list { get; private set; default = new FolderList.Tree(); }
     public MainToolbar main_toolbar { get; private set; }
     public SearchBar search_bar { get; private set; default = new SearchBar(); }
-    public ConversationListView conversation_list_view  { get; private set; default = new 
ConversationListView(); }
+    public ConversationListView conversation_list_view  { get; private set; }
     public ConversationViewer conversation_viewer { get; private set; default = new ConversationViewer(); }
     public StatusBar status_bar { get; private set; default = new StatusBar(); }
     private MonitoredSpinner spinner = new MonitoredSpinner();
@@ -61,8 +61,6 @@ public class MainWindow : Gtk.ApplicationWindow {
         load_config(application.config);
         restore_saved_window_state();
 
-        add_accel_group(application.ui_manager.get_accel_group());
-
         application.controller.notify[GearyController.PROP_CURRENT_CONVERSATION]
             .connect(on_conversation_monitor_changed);
         application.controller.folder_selected.connect(on_folder_selected);
@@ -163,6 +161,8 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
 
     private void setup_layout(Configuration config) {
+        // ConversationListView
+        this.conversation_list_view = new ConversationListView(this);
         // Toolbar
         this.main_toolbar = new MainToolbar(config);
         this.main_toolbar.bind_property("search-open", this.search_bar, "search-mode-enabled",
diff --git a/src/client/composer/composer-container.vala b/src/client/composer/composer-container.vala
index da82724..ba831e6 100644
--- a/src/client/composer/composer-container.vala
+++ b/src/client/composer/composer-container.vala
@@ -12,12 +12,6 @@ public interface ComposerContainer {
     // The ComposerWidget-child.
     internal abstract ComposerWidget composer { get; set; }
 
-    // Workaround to retrieve all Gtk.Actions with conflicting accelerators
-    protected const string[] conflicting_actions = {
-        GearyController.ACTION_MARK_AS_UNREAD,
-        GearyController.ACTION_FORWARD_MESSAGE
-    };
-
     // We use old_accelerators to keep track of the accelerators we temporarily disabled.
     protected abstract Gee.MultiMap<string, string>? old_accelerators { get; set; }
 
@@ -80,10 +74,6 @@ public interface ComposerContainer {
             }
         }
 
-        // Very stupid workaround while we still use Gtk.Actions in the GearyController
-        foreach (string conflicting_action in conflicting_actions)
-            app.actions.get_action(conflicting_action).disconnect_accelerator();
-
         // Now add our actions to the window and their accelerators
         foreach (string action in ComposerWidget.action_accelerators.get_keys()) {
             this.top_window.add_action(composer.get_action(action));
@@ -99,10 +89,6 @@ public interface ComposerContainer {
         foreach (string action in ComposerWidget.action_accelerators.get_keys())
             GearyApplication.instance.set_accels_for_action("win." + action, {});
 
-        // Very stupid workaround while we still use Gtk.Actions in the GearyController
-        foreach (string conflicting_action in conflicting_actions)
-            GearyApplication.instance.actions.get_action(conflicting_action).connect_accelerator();
-
         foreach (string action in old_accelerators.get_keys())
             foreach (string accelerator in this.old_accelerators[action])
                 restore_conflicting_accelerator(action, accelerator);
diff --git a/src/client/conversation-list/conversation-list-view.vala 
b/src/client/conversation-list/conversation-list-view.vala
index bad2744..abacb34 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -6,9 +6,12 @@
 
 public class ConversationListView : Gtk.TreeView {
     const int LOAD_MORE_HEIGHT = 100;
-    
+
+    // Used to be able to refer to the action names of the MainWindow
+    private MainWindow main_window;
+
     private bool enable_load_more = true;
-    
+
     // Used to avoid repeated calls to load_more(). Contains the last "upper" bound of the
     // scroll adjustment seen at the call to load_more().
     private double last_upper = -1.0;
@@ -16,7 +19,6 @@ public class ConversationListView : Gtk.TreeView {
     private Geary.App.ConversationMonitor? conversation_monitor;
     private Gee.Set<Geary.App.Conversation>? current_visible_conversations = null;
     private Geary.Scheduler.Scheduled? scheduled_update_visible_conversations = null;
-    private Gtk.Menu? context_menu = null;
     private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
     private Geary.IdleManager selection_update;
     private bool suppress_selection = false;
@@ -36,9 +38,10 @@ public class ConversationListView : Gtk.TreeView {
     public signal void visible_conversations_changed(Gee.Set<Geary.App.Conversation> visible);
 
 
-    public ConversationListView() {
+    public ConversationListView(MainWindow parent) {
         set_show_expanders(false);
         set_headers_visible(false);
+        this.main_window = parent;
 
         append_column(create_column(ConversationListStore.Column.CONVERSATION_DATA,
             new ConversationListCellRenderer(), ConversationListStore.Column.CONVERSATION_DATA.to_string(),
@@ -281,47 +284,36 @@ public class ConversationListView : Gtk.TreeView {
         
         if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) {
             Geary.App.Conversation conversation = get_model().get_conversation_at_path(path);
-            
-            string?[] action_names = {};
-            action_names += GearyController.ACTION_DELETE_CONVERSATION;
-            
+
+            Menu context_menu_model = new Menu();
+            context_menu_model.append(_("Delete conversation"), 
"win."+GearyController.ACTION_DELETE_CONVERSATION);
+
             if (conversation.is_unread())
-                action_names += GearyController.ACTION_MARK_AS_READ;
-            
+                context_menu_model.append(_("Mark as _Read"), "win."+GearyController.ACTION_MARK_AS_READ);
+
             if (conversation.has_any_read_message())
-                action_names += GearyController.ACTION_MARK_AS_UNREAD;
-            
+                context_menu_model.append(_("Mark as _Unread"), 
"win."+GearyController.ACTION_MARK_AS_UNREAD);
+
             if (conversation.is_flagged())
-                action_names += GearyController.ACTION_MARK_AS_UNSTARRED;
+                context_menu_model.append(_("U_nstar"), "win."+GearyController.ACTION_MARK_AS_UNSTARRED);
             else
-                action_names += GearyController.ACTION_MARK_AS_STARRED;
-            
-            // treat null as separator
-            action_names += null;
-            action_names += GearyController.ACTION_REPLY_TO_MESSAGE;
-            action_names += GearyController.ACTION_REPLY_ALL_MESSAGE;
-            action_names += GearyController.ACTION_FORWARD_MESSAGE;
-            
-            context_menu = new Gtk.Menu();
-            foreach (string? action_name in action_names) {
-                if (action_name == null) {
-                    context_menu.add(new Gtk.SeparatorMenuItem());
-                    
-                    continue;
-                }
-                
-                Gtk.Action? menu_action = GearyApplication.instance.actions.get_action(action_name);
-                if (menu_action != null)
-                    context_menu.add(menu_action.create_menu_item());
-            }
-            
+                context_menu_model.append(_("_Star"), "win."+GearyController.ACTION_MARK_AS_STARRED);
+
+            Menu actions_section = new Menu();
+            actions_section.append(_("_Reply"), "win."+GearyController.ACTION_REPLY_TO_MESSAGE);
+            actions_section.append(_("R_eply All"), "win."+GearyController.ACTION_REPLY_ALL_MESSAGE);
+            actions_section.append(_("_Forward"), "win."+GearyController.ACTION_FORWARD_MESSAGE);
+            context_menu_model.append_section(null, actions_section);
+
+            Gtk.Menu context_menu = new Gtk.Menu.from_model(context_menu_model);
+            context_menu.insert_action_group("win", this.main_window);
             context_menu.show_all();
             context_menu.popup(null, null, null, event.button, event.time);
-            
+
             // When the conversation under the mouse is selected, stop event propagation
             return get_selection().path_is_selected(path);
         }
-        
+
         return false;
     }
 
diff --git a/src/client/util/util-gtk.vala b/src/client/util/util-gtk.vala
index 84fcabe..489d217 100644
--- a/src/client/util/util-gtk.vala
+++ b/src/client/util/util-gtk.vala
@@ -35,25 +35,6 @@ public void add_proxy_menu(Gtk.ToolItem tool_item, string label, Gtk.Menu proxy_
     });
 }
 
-public void add_accelerator(Gtk.UIManager ui_manager, Gtk.ActionGroup action_group,
-    string accelerator, string action) {
-    // Parse the accelerator.
-    uint key = 0;
-    Gdk.ModifierType modifiers = 0;
-    Gtk.accelerator_parse(accelerator, out key, out modifiers);
-    if (key == 0) {
-        debug("Failed to parse accelerator '%s'", accelerator);
-        return;
-    }
-    
-    // Connect the accelerator to the action.
-    ui_manager.get_accel_group().connect(key, modifiers, Gtk.AccelFlags.VISIBLE,
-        (group, obj, key, modifiers) => {
-            action_group.get_action(action).activate();
-            return true;
-        });
-}
-
 public void show_menuitem_accel_labels(Gtk.Widget widget) {
     Gtk.MenuItem? item = widget as Gtk.MenuItem;
     if (item == null) {
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index dc41091..05a74e7 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -1,6 +1,5 @@
 
 set(RESOURCE_LIST
-  STRIPBLANKS "accelerators.ui"
   STRIPBLANKS "account_cannot_remove.glade"
   STRIPBLANKS "account_list.glade"
   STRIPBLANKS "account_spinner.glade"
@@ -29,12 +28,11 @@ set(RESOURCE_LIST
   STRIPBLANKS "gtk/menus.ui"
   STRIPBLANKS "login.glade"
   STRIPBLANKS "main-toolbar.ui"
+  STRIPBLANKS "main-toolbar-menus.ui"
   STRIPBLANKS "main-window.ui"
   STRIPBLANKS "password-dialog.glade"
   STRIPBLANKS "preferences-dialog.ui"
   STRIPBLANKS "remove_confirm.glade"
-  STRIPBLANKS "toolbar_empty_menu.ui"
-  STRIPBLANKS "toolbar_mark_menu.ui"
   STRIPBLANKS "upgrade_dialog.glade"
   "geary.css"
 )
diff --git a/ui/main-toolbar-menus.ui b/ui/main-toolbar-menus.ui
new file mode 100644
index 0000000..dea30cc
--- /dev/null
+++ b/ui/main-toolbar-menus.ui
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="empty_menu">
+    <item>
+      <attribute name="label" translatable="yes">Empty _Spam…</attribute>
+      <attribute name="action">win.empty-spam</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Empty _Trash…</attribute>
+      <attribute name="action">win.empty-trash</attribute>
+    </item>
+  </menu>
+
+  <menu id="mark_message_menu">
+    <item>
+      <attribute name="label" translatable="yes">Mark as _Read</attribute>
+      <attribute name="action">win.mark-message-read</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Mark as _Unread</attribute>
+      <attribute name="action">win.mark-message-unread</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">_Star</attribute>
+      <attribute name="action">win.mark-message-starred</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">U_nstar</attribute>
+      <attribute name="action">win.mark-message-unstarred</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Mark as S_pam</attribute>
+      <attribute name="action">win.mark-message-spam</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="yes">Mark as not S_pam</attribute>
+      <attribute name="action">win.mark-message-not-spam</attribute>
+    </item>
+  </menu>
+</interface>
diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui
index 074710c..48cd8a0 100644
--- a/ui/main-toolbar.ui
+++ b/ui/main-toolbar.ui
@@ -20,6 +20,7 @@
             <property name="can_focus">True</property>
             <property name="focus_on_click">False</property>
             <property name="always_show_image">True</property>
+            <property name="action_name">win.new-message</property>
             <child>
               <object class="GtkImage" id="compose_new_message_image">
                 <property name="icon_name">text-editor-symbolic</property>
@@ -43,6 +44,7 @@
             <property name="can_focus">True</property>
             <property name="focus_on_click">False</property>
             <property name="always_show_image">True</property>
+            <property name="tooltip_text" translatable="yes">Toggle search bar</property>
             <child>
               <object class="GtkImage" id="search_conversations_image">
                 <property name="icon_name">preferences-system-search-symbolic</property>
@@ -94,6 +96,8 @@
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
                 <property name="always_show_image">True</property>
+                <property name="tooltip_text" translatable="yes">Reply</property>
+                <property name="action_name">win.reply-to-message</property>
                 <child>
                   <object class="GtkImage" id="reply_sender_image">
                     <property name="icon_name">mail-reply-sender-symbolic</property>
@@ -107,6 +111,8 @@
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
                 <property name="always_show_image">True</property>
+                <property name="tooltip_text" translatable="yes">Reply All</property>
+                <property name="action_name">win.reply-all-message</property>
                 <child>
                   <object class="GtkImage" id="reply_all_image">
                     <property name="icon_name">mail-reply-all-symbolic</property>
@@ -120,6 +126,8 @@
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
                 <property name="always_show_image">True</property>
+                <property name="tooltip_text" translatable="yes">Forward</property>
+                <property name="action_name">win.forward-message</property>
                 <child>
                   <object class="GtkImage" id="forward_image">
                     <property name="icon_name">mail-forward-symbolic</property>
@@ -143,6 +151,7 @@
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
                 <property name="always_show_image">True</property>
+                <property name="action_name">win.mark-message-menu</property>
                 <child>
                   <object class="GtkImage" id="mark_message_image">
                     <property name="icon_name">marker-symbolic</property>
@@ -151,7 +160,7 @@
               </object>
             </child>
             <child>
-              <object class="GtkMenuButton" id="copy_message_button">
+              <object class="GtkMenuButton" id="move_message_button">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
@@ -164,7 +173,7 @@
               </object>
             </child>
             <child>
-              <object class="GtkMenuButton" id="move_message_button">
+              <object class="GtkMenuButton" id="copy_message_button">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
@@ -184,6 +193,7 @@
             <property name="can_focus">True</property>
             <property name="focus_on_click">False</property>
             <property name="always_show_image">True</property>
+            <property name="tooltip_text" translatable="yes">Toggle find bar</property>
             <child>
               <object class="GtkImage" id="find_image">
                 <property name="icon_name">preferences-system-search-symbolic</property>
@@ -200,9 +210,10 @@
             <property name="can_focus">True</property>
             <property name="focus_on_click">False</property>
             <property name="always_show_image">True</property>
+            <property name="action_name">win.undo</property>
             <child>
               <object class="GtkImage" id="undo_image">
-                <property name="icon_name">preferences-system-search-symbolic</property>
+                <property name="icon_name">edit-undo-symbolic</property>
               </object>
             </child>
           </object>
@@ -224,6 +235,12 @@
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
                 <property name="always_show_image">True</property>
+                <property name="action_name">win.archive-conv</property>
+                <child>
+                  <object class="GtkImage" id="archive_image">
+                    <property name="icon_name">mail-archive-symbolic</property>
+                  </object>
+                </child>
               </object>
             </child>
             <child>
@@ -232,6 +249,12 @@
                 <property name="can_focus">True</property>
                 <property name="focus_on_click">False</property>
                 <property name="always_show_image">True</property>
+                <property name="action_name">win.trash-conv</property>
+                <child>
+                  <object class="GtkImage" id="trash_delete_image">
+                    <property name="icon_name">user-trash-symbolic</property>
+                  </object>
+                </child>
               </object>
             </child>
           </object>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]