[geary] Add separate trash/archive/delete buttons



commit 968743ac35be73307adf95bbb593509f60e6f922
Author: Charles Lindsay <chaz yorba org>
Date:   Tue Jan 7 15:04:49 2014 -0800

    Add separate trash/archive/delete buttons
    
    Archive shows up as its own button for any account that supports
    archiving (currently only Gmail).  Trash or delete shows up as one
    button, depending on what the folder supports and whether you've got the
    shift key held down.  Future work will extend supporting archive to
    other account types and getting the trash special folder recognized in
    more accounts.
    
    Closes: bgo #714212

 src/client/application/geary-controller.vala       |  184 +++++++++++++------
 src/client/components/main-toolbar.vala            |   37 ++++-
 src/client/components/main-window.vala             |   28 +++-
 src/client/components/pill-toolbar.vala            |    2 +-
 src/engine/abstract/geary-abstract-account.vala    |    5 +-
 src/engine/api/geary-account.vala                  |    8 +
 .../gmail/imap-engine-gmail-account.vala           |    2 +-
 .../imap-engine/imap-engine-generic-account.vala   |    6 +-
 .../other/imap-engine-other-account.vala           |    2 +-
 .../outlook/imap-engine-outlook-account.vala       |    2 +-
 .../yahoo/imap-engine-yahoo-account.vala           |    2 +-
 11 files changed, 205 insertions(+), 73 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index da59363..2953d5b 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -15,6 +15,8 @@ public class GearyController : Geary.BaseObject {
     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_MESSAGE = "GearyArchiveMessage";
+    public const string ACTION_TRASH_MESSAGE = "GearyTrashMessage";
     public const string ACTION_DELETE_MESSAGE = "GearyDeleteMessage";
     public const string ACTION_FIND_IN_CONVERSATION = "GearyFindInConversation";
     public const string ACTION_FIND_NEXT_IN_CONVERSATION = "GearyFindNextInConversation";
@@ -40,13 +42,18 @@ public class GearyController : Geary.BaseObject {
     public const int MIN_CONVERSATION_COUNT = 50;
     
     private const string DELETE_MESSAGE_LABEL = _("_Delete");
-    private const string DELETE_MESSAGE_TOOLTIP_SINGLE = _("Delete conversation (Delete, Backspace, A)");
-    private const string DELETE_MESSAGE_TOOLTIP_MULTIPLE = _("Delete conversations (Delete, Backspace, A)");
-    private const string DELETE_MESSAGE_ICON_NAME = "user-trash-symbolic";
+    private const string DELETE_MESSAGE_TOOLTIP_SINGLE = _("Delete conversation (Shift+Delete)");
+    private const string DELETE_MESSAGE_TOOLTIP_MULTIPLE = _("Delete conversations (Shift+Delete)");
+    private const string DELETE_MESSAGE_ICON_NAME = "edit-delete-symbolic";
+    
+    private const string TRASH_MESSAGE_LABEL = _("_Trash");
+    private const string TRASH_MESSAGE_TOOLTIP_SINGLE = _("Move conversation to trash (Delete, Backspace)");
+    private const string TRASH_MESSAGE_TOOLTIP_MULTIPLE = _("Move conversations to trash (Delete, 
Backspace)");
+    private const string TRASH_MESSAGE_ICON_NAME = "user-trash-symbolic";
     
     private const string ARCHIVE_MESSAGE_LABEL = _("_Archive");
-    private const string ARCHIVE_MESSAGE_TOOLTIP_SINGLE = _("Archive conversation (Delete, Backspace, A)");
-    private const string ARCHIVE_MESSAGE_TOOLTIP_MULTIPLE = _("Archive conversations (Delete, Backspace, 
A)");
+    private const string ARCHIVE_MESSAGE_TOOLTIP_SINGLE = _("Archive conversation (A)");
+    private const string ARCHIVE_MESSAGE_TOOLTIP_MULTIPLE = _("Archive conversations (A)");
     private const string ARCHIVE_MESSAGE_ICON_NAME = "archive-symbolic";
     
     private const string MARK_AS_SPAM_LABEL = _("Mark as S_pam");
@@ -156,6 +163,7 @@ public class GearyController : Geary.BaseObject {
         
         // Create the main window (must be done after creating actions.)
         main_window = new MainWindow(GearyApplication.instance);
+        main_window.on_shift_key.connect(on_shift_key);
         main_window.notify["has-toplevel-focus"].connect(on_has_toplevel_focus);
         
         enable_message_buttons(false);
@@ -338,14 +346,24 @@ public class GearyController : Geary.BaseObject {
             null, null, "<Shift><Ctrl>G", null, on_find_previous_in_conversation_action };
         entries += find_previous_in_conversation;
         
-        // although this action changes according to Geary.Folder capabilities, set to Archive
+        Gtk.ActionEntry archive_message = { ACTION_ARCHIVE_MESSAGE, ARCHIVE_MESSAGE_ICON_NAME,
+            ARCHIVE_MESSAGE_LABEL, "A", null, on_archive_message };
+        archive_message.tooltip = ARCHIVE_MESSAGE_TOOLTIP_SINGLE;
+        entries += archive_message;
+        
+        // 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 delete_message = { ACTION_DELETE_MESSAGE, ARCHIVE_MESSAGE_ICON_NAME,
-            ARCHIVE_MESSAGE_LABEL, "A", null, on_delete_message };
-        delete_message.tooltip = ARCHIVE_MESSAGE_TOOLTIP_SINGLE;
+        Gtk.ActionEntry trash_message = { ACTION_TRASH_MESSAGE, TRASH_MESSAGE_ICON_NAME,
+            TRASH_MESSAGE_LABEL, "Delete", null, on_trash_message };
+        trash_message.tooltip = TRASH_MESSAGE_TOOLTIP_SINGLE;
+        entries += trash_message;
+        add_accelerator("BackSpace", ACTION_TRASH_MESSAGE);
+        
+        Gtk.ActionEntry delete_message = { ACTION_DELETE_MESSAGE, DELETE_MESSAGE_ICON_NAME,
+            DELETE_MESSAGE_LABEL, "<Shift>Delete", null, on_delete_message };
+        delete_message.tooltip = DELETE_MESSAGE_TOOLTIP_SINGLE;
         entries += delete_message;
-        add_accelerator("Delete", ACTION_DELETE_MESSAGE);
-        add_accelerator("BackSpace", ACTION_DELETE_MESSAGE);
+        add_accelerator("<Shift>BackSpace", ACTION_DELETE_MESSAGE);
 
         Gtk.ActionEntry zoom_in = { ACTION_ZOOM_IN, null, null, "<Ctrl>equal",
             null, on_zoom_in };
@@ -387,6 +405,8 @@ public class GearyController : Geary.BaseObject {
             ACTION_REPLY_TO_MESSAGE,
             ACTION_REPLY_ALL_MESSAGE,
             ACTION_FORWARD_MESSAGE,
+            ACTION_ARCHIVE_MESSAGE,
+            ACTION_TRASH_MESSAGE,
             ACTION_DELETE_MESSAGE,
         };
         const string[] exported_actions = {
@@ -908,16 +928,9 @@ public class GearyController : Geary.BaseObject {
     // by other utility methods
     private void update_ui() {
         update_tooltips();
-        Gtk.Action delete_message = GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE);
-        if (current_folder is Geary.FolderSupport.Archive) {
-            delete_message.label = ARCHIVE_MESSAGE_LABEL;
-            delete_message.icon_name = ARCHIVE_MESSAGE_ICON_NAME;
-        } else {
-            // even if not Geary.FolderSupportsrRemove, use delete icons and label, although they
-            // may be insensitive the entire time
-            delete_message.label = DELETE_MESSAGE_LABEL;
-            delete_message.icon_name = DELETE_MESSAGE_ICON_NAME;
-        }
+        main_window.main_toolbar.update_trash_buttons(
+            current_folder_supports_trash() || !(current_folder is Geary.FolderSupport.Remove),
+            current_account.can_support_archive);
     }
     
     private void on_folder_selected(Geary.Folder? folder) {
@@ -1291,6 +1304,12 @@ public class GearyController : Geary.BaseObject {
         }
     }
     
+    private void on_shift_key(bool pressed) {
+        main_window.main_toolbar.update_trash_buttons(
+            (!pressed && current_folder_supports_trash()) || !(current_folder is Geary.FolderSupport.Remove),
+            current_account.can_support_archive);
+    }
+    
     // this signal does not necessarily indicate that the application previously didn't have
     // focus and now it does
     private void on_has_toplevel_focus() {
@@ -1833,54 +1852,96 @@ public class GearyController : Geary.BaseObject {
         main_window.conversation_viewer.find(false);
     }
     
-    // This method is used for both removing and archive a message; currently Geary only supports
-    // one or the other in a folder
+    private void on_archive_message() {
+        archive_or_delete_selection_async.begin(true, false, cancellable_folder,
+            on_archive_or_delete_selection_finished);
+    }
+    
+    private void on_trash_message() {
+        archive_or_delete_selection_async.begin(false, true, cancellable_folder,
+            on_archive_or_delete_selection_finished);
+    }
+    
     private void on_delete_message() {
-        // Prevent deletes of the same conversation from repeating.
+        archive_or_delete_selection_async.begin(false, false, cancellable_folder,
+            on_archive_or_delete_selection_finished);
+    }
+    
+    private bool current_folder_supports_trash(out Geary.FolderSupport.Move? move = null,
+        out Geary.FolderPath? trash_path = null) {
+        try {
+            if (current_folder != null && current_folder.special_folder_type != Geary.SpecialFolderType.TRASH
+                && !current_folder.properties.is_local_only && current_account != null) {
+                Geary.FolderSupport.Move? supports_move = current_folder as Geary.FolderSupport.Move;
+                Geary.Folder? trash_folder = 
current_account.get_special_folder(Geary.SpecialFolderType.TRASH);
+                if (supports_move != null && trash_folder != null) {
+                    move = supports_move;
+                    trash_path = trash_folder.path;
+                    return true;
+                }
+            }
+        } catch (Error e) {
+            debug("Error finding trash folder: %s", e.message);
+        }
+        
+        move = null;
+        trash_path = null;
+        return false;
+    }
+    
+    private async void archive_or_delete_selection_async(bool archive, bool trash,
+        Cancellable? cancellable) throws Error {
         if (main_window.conversation_viewer.current_conversation != null
             && main_window.conversation_viewer.current_conversation == last_deleted_conversation) {
-            debug("not archiving/deleting, viewed conversation is last deleted conversation");
-            
+            debug("Not archiving/trashing/deleting; viewed conversation is last deleted conversation");
             return;
         }
         
-        // There should always be at least one conversation selected here, otherwise the archive
-        // button is disabled, but better safe than segfaulted.
         last_deleted_conversation = selected_conversations.size > 0
-            ? Geary.Collection.get_first<Geary.App.Conversation>(selected_conversations) : null;
+            ? Geary.traverse<Geary.App.Conversation>(selected_conversations).first() : null;
         
-        // If the user clicked the toolbar button, we want to move focus back to the message list.
+        // Return focus to the conversation list from the clicked toolbar button.
         main_window.conversation_list_view.grab_focus();
         
-        delete_messages.begin(get_selected_email_ids(false), cancellable_folder, 
on_delete_messages_completed);
-    }
-    
-    // This method is used for both removing and archive a message; currently Geary only supports
-    // one or the other in a folder.  This will try archiving first, then remove.
-    private async void delete_messages(Gee.List<Geary.EmailIdentifier> ids, Cancellable? cancellable)
-        throws Error {
-        Geary.FolderSupport.Archive? supports_archive = current_folder as Geary.FolderSupport.Archive;
-        if (supports_archive != null) {
-            yield supports_archive.archive_email_async(ids, cancellable);
+        Gee.List<Geary.EmailIdentifier> ids = get_selected_email_ids(false);
+        if (archive) {
+            debug("Archiving selected messages");
             
+            Geary.FolderSupport.Archive? supports_archive = current_folder as Geary.FolderSupport.Archive;
+            if (supports_archive == null)
+                debug("Folder %s doesn't support archive", current_folder.to_string());
+            else
+                yield supports_archive.archive_email_async(ids, cancellable);
             return;
         }
         
-        Geary.FolderSupport.Remove? supports_remove = current_folder as Geary.FolderSupport.Remove;
-        if (supports_remove != null) {
-            yield supports_remove.remove_email_async(ids, cancellable);
+        if (trash) {
+            debug("Trashing selected messages");
             
+            Geary.FolderPath? trash_path;
+            Geary.FolderSupport.Move? supports_move;
+            if (!current_folder_supports_trash(out supports_move, out trash_path))
+                debug("Folder %s doesn't support move or account %s doesn't have a trash folder",
+                    current_folder.to_string(), current_account.to_string());
+            else
+                yield supports_move.move_email_async(ids, trash_path, cancellable);
             return;
         }
         
-        debug("Folder %s supports neither remove nor archive", current_folder.to_string());
+        debug("Deleting selected messages");
+        
+        Geary.FolderSupport.Remove? supports_remove = current_folder as Geary.FolderSupport.Remove;
+        if (supports_remove == null)
+            debug("Folder %s doesn't support remove", current_folder.to_string());
+        else
+            yield supports_remove.remove_email_async(ids, cancellable);
     }
-
-    private void on_delete_messages_completed(Object? source, AsyncResult result) {
+    
+    private void on_archive_or_delete_selection_finished(Object? source, AsyncResult result) {
         try {
-            delete_messages.end(result);
-        } catch (Error err) {
-            debug("Error, unable to delete messages: %s", err.message);
+            archive_or_delete_selection_async.end(result);
+        } catch (Error e) {
+            debug("Unable to archive/trash/delete messages: %s", e.message);
         }
     }
     
@@ -1924,8 +1985,12 @@ public class GearyController : Geary.BaseObject {
         // Mutliple message buttons.
         GearyApplication.instance.actions.get_action(ACTION_MOVE_MENU).sensitive =
             (current_folder is Geary.FolderSupport.Move);
+        GearyApplication.instance.actions.get_action(ACTION_ARCHIVE_MESSAGE).sensitive =
+            (current_folder is Geary.FolderSupport.Archive);
+        GearyApplication.instance.actions.get_action(ACTION_TRASH_MESSAGE).sensitive =
+            current_folder_supports_trash();
         GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE).sensitive =
-            (current_folder is Geary.FolderSupport.Remove) || (current_folder is 
Geary.FolderSupport.Archive);
+            (current_folder is Geary.FolderSupport.Remove);
         
         cancel_context_dependent_buttons();
         enable_context_dependent_buttons_async.begin(true, cancellable_context_dependent_buttons);
@@ -1945,8 +2010,12 @@ public class GearyController : Geary.BaseObject {
         GearyApplication.instance.actions.get_action(ACTION_FORWARD_MESSAGE).sensitive = respond_sensitive;
         GearyApplication.instance.actions.get_action(ACTION_MOVE_MENU).sensitive =
             sensitive && (current_folder is Geary.FolderSupport.Move);
+        GearyApplication.instance.actions.get_action(ACTION_ARCHIVE_MESSAGE).sensitive = sensitive
+            && (current_folder is Geary.FolderSupport.Archive);
+        GearyApplication.instance.actions.get_action(ACTION_TRASH_MESSAGE).sensitive = sensitive
+            && current_folder_supports_trash();
         GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE).sensitive = sensitive
-            && ((current_folder is Geary.FolderSupport.Remove) || (current_folder is 
Geary.FolderSupport.Archive));
+            && (current_folder is Geary.FolderSupport.Remove);
         
         cancel_context_dependent_buttons();
         enable_context_dependent_buttons_async.begin(sensitive, cancellable_context_dependent_buttons);
@@ -1992,13 +2061,12 @@ public class GearyController : Geary.BaseObject {
         GearyApplication.instance.actions.get_action(ACTION_MOVE_MENU).tooltip = single ?
             MOVE_MESSAGE_TOOLTIP_SINGLE : MOVE_MESSAGE_TOOLTIP_MULTIPLE;
         
-        if (current_folder is Geary.FolderSupport.Archive) {
-            GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE).tooltip = single ?
-                ARCHIVE_MESSAGE_TOOLTIP_SINGLE : ARCHIVE_MESSAGE_TOOLTIP_MULTIPLE;
-        } else {
-            GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE).tooltip = single ?
-                DELETE_MESSAGE_TOOLTIP_SINGLE : DELETE_MESSAGE_TOOLTIP_MULTIPLE;
-        }
+        GearyApplication.instance.actions.get_action(ACTION_ARCHIVE_MESSAGE).tooltip = single ?
+            ARCHIVE_MESSAGE_TOOLTIP_SINGLE : ARCHIVE_MESSAGE_TOOLTIP_MULTIPLE;
+        GearyApplication.instance.actions.get_action(ACTION_TRASH_MESSAGE).tooltip = single ?
+            TRASH_MESSAGE_TOOLTIP_SINGLE : TRASH_MESSAGE_TOOLTIP_MULTIPLE;
+        GearyApplication.instance.actions.get_action(ACTION_DELETE_MESSAGE).tooltip = single ?
+            DELETE_MESSAGE_TOOLTIP_SINGLE : DELETE_MESSAGE_TOOLTIP_MULTIPLE;
     }
     
     public void compose_mailto(string mailto) {
diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala
index 20b10fe..0e71ca0 100644
--- a/src/client/components/main-toolbar.vala
+++ b/src/client/components/main-toolbar.vala
@@ -13,7 +13,10 @@ public class MainToolbar : PillToolbar {
     public FolderMenu copy_folder_menu { get; private set; default = new FolderMenu(); }
     public FolderMenu move_folder_menu { get; private set; default = new FolderMenu(); }
     public string search_text { get { return search_entry.text; } }
+    public bool search_entry_has_focus { get { return search_entry.has_focus; } }
     
+    private Gtk.Button archive_button;
+    private Gtk.Button trash_buttons[2];
     private Gtk.ToolItem search_container = new Gtk.ToolItem();
     private Gtk.SearchEntry search_entry = new Gtk.SearchEntry();
     private Geary.ProgressMonitor? search_upgrade_progress_monitor = null;
@@ -61,11 +64,17 @@ public class MainToolbar : PillToolbar {
         insert.add(create_menu_button("folder-symbolic", move_folder_menu, 
GearyController.ACTION_MOVE_MENU));
         add(create_pill_buttons(insert));
         
-        // Archive/delete button.
-        // For this button, the controller sets the tooltip and icon depending on the context.
+        // The toolbar looks bad when you hide one of a pair of pill buttons.
+        // Unfortunately, this means we have to have one pair for archive/trash
+        // and one single button for just trash, for when the archive button is
+        // hidden.
         insert.clear();
-        insert.add(create_toolbar_button("", GearyController.ACTION_DELETE_MESSAGE, true));
+        insert.add(archive_button = create_toolbar_button(null, GearyController.ACTION_ARCHIVE_MESSAGE, 
true));
+        insert.add(trash_buttons[0] = create_toolbar_button(null, GearyController.ACTION_TRASH_MESSAGE, 
true));
         add(create_pill_buttons(insert));
+        insert.clear();
+        insert.add(trash_buttons[1] = create_toolbar_button(null, GearyController.ACTION_TRASH_MESSAGE, 
true));
+        add(create_pill_buttons(insert, false));
         
         // Spacer.
         add(create_spacer());
@@ -101,6 +110,28 @@ public class MainToolbar : PillToolbar {
         set_search_placeholder_text(DEFAULT_SEARCH_TEXT);
     }
     
+    private void show_archive_button(bool show) {
+        if (show) {
+            archive_button.show();
+            trash_buttons[0].show();
+            trash_buttons[1].hide();
+        } else {
+            archive_button.hide();
+            trash_buttons[0].hide();
+            trash_buttons[1].show();
+        }
+    }
+    
+    /// Updates the trash button as trash or delete, and shows or hides the archive button.
+    public void update_trash_buttons(bool trash, bool archive) {
+        string action_name = (trash ? GearyController.ACTION_TRASH_MESSAGE
+            : GearyController.ACTION_DELETE_MESSAGE);
+        foreach (Gtk.Button b in trash_buttons)
+            setup_button(b, null, action_name, true);
+        
+        show_archive_button(archive);
+    }
+    
     public void set_search_text(string text) {
         search_entry.text = text;
     }
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index bc93d71..6cbac74 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -9,6 +9,9 @@ public class MainWindow : Gtk.ApplicationWindow {
     private const int FOLDER_LIST_WIDTH = 100;
     private const int STATUS_BAR_HEIGHT = 18;
     
+    /// Fired when the shift key is pressed or released.
+    public signal void on_shift_key(bool pressed);
+    
     public FolderList.Tree folder_list { get; private set; default = new FolderList.Tree(); }
     public ConversationListStore conversation_list_store { get; private set; default = new 
ConversationListStore(); }
     public MainToolbar main_toolbar { get; private set; }
@@ -35,6 +38,8 @@ public class MainWindow : Gtk.ApplicationWindow {
         
         conversation_list_view = new ConversationListView(conversation_list_store);
         
+        add_events(Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK);
+        
         // This code both loads AND saves the pane positions with live
         // updating. This is more resilient against crashes because
         // the value in dconf changes *immediately*, and stays saved
@@ -57,6 +62,7 @@ public class MainWindow : Gtk.ApplicationWindow {
         
         delete_event.connect(on_delete_event);
         key_press_event.connect(on_key_press_event);
+        key_release_event.connect(on_key_release_event);
         GearyApplication.instance.controller.notify[GearyController.PROP_CURRENT_CONVERSATION].
             connect(on_conversation_monitor_changed);
         Geary.Engine.instance.account_available.connect(on_account_available);
@@ -66,12 +72,15 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
     
     public override void show_all() {
-        set_default_size(GearyApplication.instance.config.window_width, 
+        set_default_size(GearyApplication.instance.config.window_width,
             GearyApplication.instance.config.window_height);
         if (GearyApplication.instance.config.window_maximize)
             maximize();
         
         base.show_all();
+        
+        // Some buttons need to be hidden, so we have to do this after we show everything.
+        main_toolbar.update_trash_buttons(true, true);
     }
     
     private bool on_delete_event() {
@@ -149,8 +158,6 @@ public class MainWindow : Gtk.ApplicationWindow {
         main_layout.pack_end(folder_paned, true, true, 0);
         
         add(main_layout);
-        
-        this.key_press_event.connect(on_key_press_event);
     }
     
     // Returns true when there's a conversation list scrollbar visible, i.e. the list is tall
@@ -161,11 +168,26 @@ public class MainWindow : Gtk.ApplicationWindow {
     }
     
     private bool on_key_press_event(Gdk.EventKey event) {
+        if ((event.keyval == Gdk.Key.Shift_L || event.keyval == Gdk.Key.Shift_R)
+            && (event.state & Gdk.ModifierType.SHIFT_MASK) == 0 && !main_toolbar.search_entry_has_focus)
+            on_shift_key(true);
+        
         // Check whether the focused widget wants to handle it, if not let the accelerators kick in
         // via the default handling
         return propagate_key_event(event);
     }
     
+    private bool on_key_release_event(Gdk.EventKey event) {
+        // FIXME: it's possible the user will press two shift keys.  We want
+        // the shift key to report as released when they release ALL of them.
+        // There doesn't seem to be an easy way to do this in Gdk.
+        if ((event.keyval == Gdk.Key.Shift_L || event.keyval == Gdk.Key.Shift_R)
+            && !main_toolbar.search_entry_has_focus)
+            on_shift_key(false);
+        
+        return propagate_key_event(event);
+    }
+    
     private void on_conversation_monitor_changed() {
         Geary.App.ConversationMonitor? conversation_monitor =
             GearyApplication.instance.controller.current_conversations;
diff --git a/src/client/components/pill-toolbar.vala b/src/client/components/pill-toolbar.vala
index 4d5ec66..6cb41b0 100644
--- a/src/client/components/pill-toolbar.vala
+++ b/src/client/components/pill-toolbar.vala
@@ -14,7 +14,7 @@ public class PillToolbar : Gtk.Toolbar {
         action_group = toolbar_action_group;
     }
     
-    private void setup_button(Gtk.Button b, string? icon_name, string action_name,
+    protected void setup_button(Gtk.Button b, string? icon_name, string action_name,
         bool show_label = false) {
         b.related_action = action_group.get_action(action_name);
         b.tooltip_text = b.related_action.tooltip;
diff --git a/src/engine/abstract/geary-abstract-account.vala b/src/engine/abstract/geary-abstract-account.vala
index 4ee8115..920a268 100644
--- a/src/engine/abstract/geary-abstract-account.vala
+++ b/src/engine/abstract/geary-abstract-account.vala
@@ -11,11 +11,14 @@ public abstract class Geary.AbstractAccount : BaseObject, Geary.Account {
     public Geary.ProgressMonitor opening_monitor { get; protected set; }
     public Geary.ProgressMonitor sending_monitor { get; protected set; }
     
+    public virtual bool can_support_archive { get; protected set; }
+    
     private string name;
     
-    public AbstractAccount(string name, AccountInformation information) {
+    public AbstractAccount(string name, AccountInformation information, bool can_support_archive) {
         this.name = name;
         this.information = information;
+        this.can_support_archive = can_support_archive;
     }
     
     protected virtual void notify_folders_available_unavailable(Gee.List<Geary.Folder>? available,
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index f2dc933..3c83947 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -21,6 +21,14 @@ public interface Geary.Account : BaseObject {
     public abstract Geary.ProgressMonitor opening_monitor { get; protected set; }
     public abstract Geary.ProgressMonitor sending_monitor { get; protected set; }
     
+    /**
+     * HACK: for now, only certain account types support folders with
+     * FolderSupport.Archive.  It's useful to know whether an account supports
+     * archive because the button is hidden for accounts that will never
+     * support it.
+     */
+    public abstract bool can_support_archive { get; protected set; }
+    
     public signal void opened();
     
     public signal void closed();
diff --git a/src/engine/imap-engine/gmail/imap-engine-gmail-account.vala 
b/src/engine/imap-engine/gmail/imap-engine-gmail-account.vala
index 93a7bce..bf1e159 100644
--- a/src/engine/imap-engine/gmail/imap-engine-gmail-account.vala
+++ b/src/engine/imap-engine/gmail/imap-engine-gmail-account.vala
@@ -38,7 +38,7 @@ private class Geary.ImapEngine.GmailAccount : Geary.ImapEngine.GenericAccount {
     
     public GmailAccount(string name, Geary.AccountInformation account_information,
         Imap.Account remote, ImapDB.Account local) {
-        base (name, account_information, remote, local);
+        base (name, account_information, true, remote, local);
         
         if (path_type_map == null) {
             path_type_map = new Gee.HashMap<Geary.FolderPath, Geary.SpecialFolderType>();
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index d26aef2..2fce9e5 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -21,9 +21,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
     private Cancellable refresh_cancellable = new Cancellable();
     private bool awaiting_credentials = false;
     
-    public GenericAccount(string name, Geary.AccountInformation information, Imap.Account remote,
-        ImapDB.Account local) {
-        base (name, information);
+    public GenericAccount(string name, Geary.AccountInformation information, bool can_support_archive,
+        Imap.Account remote, ImapDB.Account local) {
+        base (name, information, can_support_archive);
         
         this.remote = remote;
         this.local = local;
diff --git a/src/engine/imap-engine/other/imap-engine-other-account.vala 
b/src/engine/imap-engine/other/imap-engine-other-account.vala
index 2be84e1..52d1f25 100644
--- a/src/engine/imap-engine/other/imap-engine-other-account.vala
+++ b/src/engine/imap-engine/other/imap-engine-other-account.vala
@@ -7,7 +7,7 @@
 private class Geary.ImapEngine.OtherAccount : Geary.ImapEngine.GenericAccount {
     public OtherAccount(string name, AccountInformation account_information,
         Imap.Account remote, ImapDB.Account local) {
-        base (name, account_information, remote, local);
+        base (name, account_information, false, remote, local);
     }
     
     protected override GenericFolder new_folder(Geary.FolderPath path, Imap.Account remote_account,
diff --git a/src/engine/imap-engine/outlook/imap-engine-outlook-account.vala 
b/src/engine/imap-engine/outlook/imap-engine-outlook-account.vala
index 5ae1baa..4007f10 100644
--- a/src/engine/imap-engine/outlook/imap-engine-outlook-account.vala
+++ b/src/engine/imap-engine/outlook/imap-engine-outlook-account.vala
@@ -33,7 +33,7 @@ private class Geary.ImapEngine.OutlookAccount : Geary.ImapEngine.GenericAccount
     
     public OutlookAccount(string name, AccountInformation account_information, Imap.Account remote,
         ImapDB.Account local) {
-        base (name, account_information, remote, local);
+        base (name, account_information, false, remote, local);
     }
     
     protected override GenericFolder new_folder(Geary.FolderPath path, Imap.Account remote_account,
diff --git a/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala 
b/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
index 655103a..6b2b86e 100644
--- a/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
+++ b/src/engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
@@ -35,7 +35,7 @@ private class Geary.ImapEngine.YahooAccount : Geary.ImapEngine.GenericAccount {
     
     public YahooAccount(string name, AccountInformation account_information,
         Imap.Account remote, ImapDB.Account local) {
-        base (name, account_information, remote, local);
+        base (name, account_information, false, remote, local);
         
         if (special_map == null) {
             special_map = new Gee.HashMap<Geary.FolderPath, Geary.SpecialFolderType>();


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