diff --git a/src/client/ui/folder-menu.vala b/src/client/ui/folder-menu.vala index 8dd5b2aa..fc2a4a2a 100644 --- a/src/client/ui/folder-menu.vala +++ b/src/client/ui/folder-menu.vala @@ -17,6 +17,16 @@ public class FolderMenu : Gtk.Menu { } public void add_folder(Geary.Folder folder) { + // don't allow multiples and don't allow folders that can't be opened (that means they + // support almost no operations and have no content) + if (folder_list.contains(folder) || folder.properties.is_openable.is_impossible()) + return; + + // also don't allow local-only or virtual folders, which also have a limited set of + // operations + if (folder.properties.is_local_only || folder.properties.is_virtual) + return; + folder_list.add(folder); folder_list.sort(folder_sort); diff --git a/src/engine/api/geary-aggregated-folder-properties.vala b/src/engine/api/geary-aggregated-folder-properties.vala index 543b3557..2f3e471a 100644 --- a/src/engine/api/geary-aggregated-folder-properties.vala +++ b/src/engine/api/geary-aggregated-folder-properties.vala @@ -21,9 +21,9 @@ private class Geary.AggregatedFolderProperties : Geary.FolderProperties { /** * Creates an aggregate FolderProperties. */ - public AggregatedFolderProperties() { + public AggregatedFolderProperties(bool is_local_only, bool is_virtual) { // Set defaults. - base(0, 0, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN); + base(0, 0, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN, is_local_only, is_virtual); } /** diff --git a/src/engine/api/geary-folder-properties.vala b/src/engine/api/geary-folder-properties.vala index a6a4f023..4034f1a0 100644 --- a/src/engine/api/geary-folder-properties.vala +++ b/src/engine/api/geary-folder-properties.vala @@ -10,6 +10,8 @@ public abstract class Geary.FolderProperties : BaseObject { public const string PROP_NAME_HAS_CHILDREN = "has-children"; public const string PROP_NAME_SUPPORTS_CHILDREN = "supports-children"; public const string PROP_NAME_IS_OPENABLE = "is-openable"; + public const string PROP_NAME_IS_LOCAL_ONLY = "is-local-only"; + public const string PROP_NAME_IS_VIRTUAL = "is-virtual"; /** * The total count of email in the Folder. @@ -38,13 +40,34 @@ public abstract class Geary.FolderProperties : BaseObject { */ public Trillian is_openable { get; protected set; } + /** + * Returns true if the {@link Folder} is local-only, that is, has no remote folder backing + * it. + * + * Note that this doesn't mean there's no network aspect to the Folder. For example, an Outbox + * may present itself as a Folder but the network backing (SMTP) has nothing that resembles + * a Folder interface. + */ + public bool is_local_only { get; private set; } + + /** + * Returns true if the {@link Folder} is virtual, that is, it is either generated by some + * external criteria and/or is aggregating the content of other Folders. + * + * In general, virtual folders cannot be the destination Folder for operations like move and + * copy. + */ + public bool is_virtual { get; private set; } + protected FolderProperties(int email_total, int email_unread, Trillian has_children, - Trillian supports_children, Trillian is_openable) { + Trillian supports_children, Trillian is_openable, bool is_local_only, bool is_virtual) { this.email_total = email_total; this.email_unread = email_unread; this.has_children = has_children; this.supports_children = supports_children; this.is_openable = is_openable; + this.is_local_only = is_local_only; + this.is_virtual = is_virtual; } } diff --git a/src/engine/api/geary-search-folder.vala b/src/engine/api/geary-search-folder.vala index 11187e0e..f2db844e 100644 --- a/src/engine/api/geary-search-folder.vala +++ b/src/engine/api/geary-search-folder.vala @@ -14,7 +14,7 @@ public class Geary.SearchFolderRoot : Geary.FolderRoot { public class Geary.SearchFolderProperties : Geary.FolderProperties { public SearchFolderProperties(int total, int unread) { - base(total, unread, Trillian.FALSE, Trillian.FALSE, Trillian.TRUE); + base(total, unread, Trillian.FALSE, Trillian.FALSE, Trillian.TRUE, true, true); } public void set_total(int total) { diff --git a/src/engine/imap-db/outbox/smtp-outbox-folder-properties.vala b/src/engine/imap-db/outbox/smtp-outbox-folder-properties.vala index 5e665b2f..8b137a6b 100644 --- a/src/engine/imap-db/outbox/smtp-outbox-folder-properties.vala +++ b/src/engine/imap-db/outbox/smtp-outbox-folder-properties.vala @@ -6,7 +6,7 @@ private class Geary.SmtpOutboxFolderProperties : Geary.FolderProperties { public SmtpOutboxFolderProperties(int total, int unread) { - base (total, unread, Trillian.FALSE, Trillian.FALSE, Trillian.TRUE); + base (total, unread, Trillian.FALSE, Trillian.FALSE, Trillian.TRUE, true, false); } public void set_total(int total) { diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala b/src/engine/imap-engine/imap-engine-generic-folder.vala index 7d538166..da53dc3d 100644 --- a/src/engine/imap-engine/imap-engine-generic-folder.vala +++ b/src/engine/imap-engine/imap-engine-generic-folder.vala @@ -31,7 +31,8 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde internal EmailFlagWatcher email_flag_watcher; private weak GenericAccount _account; - private Geary.AggregatedFolderProperties _properties = new Geary.AggregatedFolderProperties(); + private Geary.AggregatedFolderProperties _properties = new Geary.AggregatedFolderProperties( + false, false); private Imap.Account remote; private ImapDB.Account local; private Folder.OpenFlags open_flags = OpenFlags.NONE; diff --git a/src/engine/imap/api/imap-folder-properties.vala b/src/engine/imap/api/imap-folder-properties.vala index 0673017d..ab99e56b 100644 --- a/src/engine/imap/api/imap-folder-properties.vala +++ b/src/engine/imap/api/imap-folder-properties.vala @@ -66,7 +66,8 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties { UID? uid_next, MailboxAttributes attrs) { // give the base class a zero email_unread, as the notion of "unknown" doesn't exist in // its contract - base (messages, email_unread, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN); + base (messages, email_unread, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN, false, + false); select_examine_messages = messages; status_messages = -1; @@ -80,7 +81,8 @@ public class Geary.Imap.FolderProperties : Geary.FolderProperties { } public FolderProperties.status(StatusData status, MailboxAttributes attrs) { - base (status.messages, status.unseen, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN); + base (status.messages, status.unseen, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN, + false, false); select_examine_messages = -1; status_messages = status.messages; diff --git a/src/engine/util/util-object.vala b/src/engine/util/util-object.vala index 66b7584f..540222a9 100644 --- a/src/engine/util/util-object.vala +++ b/src/engine/util/util-object.vala @@ -24,8 +24,10 @@ public Gee.List? mirror_properties(Object source, Object dest, BindingF // Create all bindings. Gee.List bindings = new Gee.ArrayList(); - foreach(ParamSpec ps in source_properties) - bindings.add(source.bind_property(ps.name, dest, ps.name, flags)); + foreach(ParamSpec ps in source_properties) { + if ((ps.flags & ParamFlags.WRITABLE) != 0) + bindings.add(source.bind_property(ps.name, dest, ps.name, flags)); + } return bindings.size > 0 ? bindings : null; }