Closes #7119 Single folder properties
This commit is contained in:
parent
dc26b19aac
commit
5806f1eae4
11 changed files with 142 additions and 50 deletions
|
|
@ -14,6 +14,7 @@ engine/abstract/geary-abstract-folder.vala
|
|||
|
||||
engine/api/geary-account.vala
|
||||
engine/api/geary-account-information.vala
|
||||
engine/api/geary-aggregated-folder-properties.vala
|
||||
engine/api/geary-attachment.vala
|
||||
engine/api/geary-base-object.vala
|
||||
engine/api/geary-composed-email.vala
|
||||
|
|
@ -230,6 +231,7 @@ engine/util/util-imap-utf7.vala
|
|||
engine/util/util-inet.vala
|
||||
engine/util/util-memory.vala
|
||||
engine/util/util-numeric.vala
|
||||
engine/util/util-object.vala
|
||||
engine/util/util-reference-semantics.vala
|
||||
engine/util/util-scheduler.vala
|
||||
engine/util/util-single-item.vala
|
||||
|
|
|
|||
|
|
@ -9,24 +9,30 @@ public class FolderList.FolderEntry : Geary.BaseObject, Sidebar.Entry, Sidebar.I
|
|||
Sidebar.SelectableEntry, Sidebar.EmphasizableEntry {
|
||||
public Geary.Folder folder { get; private set; }
|
||||
private bool has_new;
|
||||
private int unread_count;
|
||||
|
||||
public FolderEntry(Geary.Folder folder) {
|
||||
this.folder = folder;
|
||||
has_new = false;
|
||||
unread_count = 0;
|
||||
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_UNDREAD].connect(
|
||||
on_email_unread_count_changed);
|
||||
}
|
||||
|
||||
~FolderEntry() {
|
||||
folder.properties.notify[Geary.FolderProperties.PROP_NAME_EMAIL_UNDREAD].disconnect(
|
||||
on_email_unread_count_changed);
|
||||
}
|
||||
|
||||
public virtual string get_sidebar_name() {
|
||||
return (unread_count == 0 ? folder.get_display_name() :
|
||||
return (folder.properties.email_unread == 0 ? folder.get_display_name() :
|
||||
/// This string gets the folder name and the unread messages count,
|
||||
/// e.g. All Mail (5).
|
||||
_("%s (%d)").printf(folder.get_display_name(), unread_count));
|
||||
_("%s (%d)").printf(folder.get_display_name(), folder.properties.email_unread));
|
||||
}
|
||||
|
||||
public string? get_sidebar_tooltip() {
|
||||
return (unread_count == 0 ? null :
|
||||
ngettext("%d unread message", "%d unread messages", unread_count).printf(unread_count));
|
||||
return (folder.properties.email_unread == 0 ? null :
|
||||
ngettext("%d unread message", "%d unread messages", folder.properties.email_unread).
|
||||
printf(folder.properties.email_unread));
|
||||
}
|
||||
|
||||
public Icon? get_sidebar_icon() {
|
||||
|
|
@ -82,15 +88,6 @@ public class FolderList.FolderEntry : Geary.BaseObject, Sidebar.Entry, Sidebar.I
|
|||
is_emphasized_changed(has_new);
|
||||
}
|
||||
|
||||
public void set_unread_count(int unread_count) {
|
||||
if (this.unread_count == unread_count)
|
||||
return;
|
||||
|
||||
this.unread_count = unread_count;
|
||||
sidebar_name_changed(get_sidebar_name());
|
||||
sidebar_tooltip_changed(get_sidebar_tooltip());
|
||||
}
|
||||
|
||||
public bool internal_drop_received(Gdk.DragContext context, Gtk.SelectionData data) {
|
||||
// Copy or move?
|
||||
Gdk.ModifierType mask;
|
||||
|
|
@ -105,4 +102,9 @@ public class FolderList.FolderEntry : Geary.BaseObject, Sidebar.Entry, Sidebar.I
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void on_email_unread_count_changed() {
|
||||
sidebar_name_changed(get_sidebar_name());
|
||||
sidebar_tooltip_changed(get_sidebar_tooltip());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ public class FolderList.InboxFolderEntry : FolderList.FolderEntry {
|
|||
}
|
||||
|
||||
public override string get_sidebar_name() {
|
||||
return folder.account.information.nickname;
|
||||
return (folder.properties.email_unread == 0 ? folder.account.information.nickname :
|
||||
/// This string gets the account nickname and the unread messages count,
|
||||
/// e.g. Work (5).
|
||||
_("%s (%d)").printf(folder.account.information.nickname, folder.properties.email_unread));
|
||||
}
|
||||
|
||||
public Geary.AccountInformation get_account_information() {
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ public abstract class Geary.AbstractFolder : BaseObject, Geary.Folder {
|
|||
|
||||
public abstract Geary.Account account { get; }
|
||||
|
||||
public abstract Geary.FolderPath get_path();
|
||||
public abstract Geary.FolderProperties properties { get; }
|
||||
|
||||
public abstract Geary.FolderProperties get_properties();
|
||||
public abstract Geary.FolderPath get_path();
|
||||
|
||||
public abstract Geary.SpecialFolderType get_special_folder_type();
|
||||
|
||||
|
|
|
|||
54
src/engine/api/geary-aggregated-folder-properties.vala
Normal file
54
src/engine/api/geary-aggregated-folder-properties.vala
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* Copyright 2013 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Aggregates multiple FolderProperties into one. This way a Geary.Folder can
|
||||
* present one stable FolderProperties object that the client can register
|
||||
* change listeners on, etc. despite most Geary.Folders having both a local
|
||||
* and remote version of FolderProperties.
|
||||
*
|
||||
* The class relies on GObject bindings and the fact that FolderProperties
|
||||
* contains only propertiess.
|
||||
*/
|
||||
private class Geary.AggregatedFolderProperties : Geary.FolderProperties {
|
||||
// Map of child FolderProperties to their bindings.
|
||||
private Gee.Map<FolderProperties, Gee.List<Binding>> child_bindings
|
||||
= new Gee.HashMap<FolderProperties, Gee.List<Binding>>();
|
||||
|
||||
/**
|
||||
* Creates an aggregate FolderProperties.
|
||||
*/
|
||||
public AggregatedFolderProperties() {
|
||||
// Set defaults.
|
||||
base(0, 0, Trillian.UNKNOWN, Trillian.UNKNOWN, Trillian.UNKNOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child FolderProperties. The child's property values will overwrite
|
||||
* this class's property values.
|
||||
*/
|
||||
public void add(FolderProperties child) {
|
||||
// Create a binding for all properties.
|
||||
Gee.List<Binding>? bindings = Geary.ObjectUtils.mirror_properties(child, this);
|
||||
assert(bindings != null);
|
||||
child_bindings.set(child, bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child FolderProperties.
|
||||
*/
|
||||
public bool remove(FolderProperties child) {
|
||||
Gee.List<Binding> bindings;
|
||||
if (child_bindings.unset(child, out bindings)) {
|
||||
Geary.ObjectUtils.unmirror_properties(bindings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5,6 +5,12 @@
|
|||
*/
|
||||
|
||||
public abstract class Geary.FolderProperties : BaseObject {
|
||||
public const string PROP_NAME_EMAIL_TOTAL = "email-total";
|
||||
public const string PROP_NAME_EMAIL_UNDREAD = "email-unread";
|
||||
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";
|
||||
|
||||
/**
|
||||
* The total count of email in the Folder.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -106,6 +106,8 @@ public interface Geary.Folder : BaseObject {
|
|||
|
||||
public abstract Geary.Account account { get; }
|
||||
|
||||
public abstract Geary.FolderProperties properties { get; }
|
||||
|
||||
/**
|
||||
* Fired when the folder is successfully opened by a caller.
|
||||
*
|
||||
|
|
@ -233,19 +235,6 @@ public interface Geary.Folder : BaseObject {
|
|||
|
||||
public abstract Geary.FolderPath get_path();
|
||||
|
||||
/**
|
||||
* Returns a FolderProperties that represents, if fully open, accurate values for this Folder,
|
||||
* and if not, values that represent the last time the Folder was opened or examined by the
|
||||
* Engine.
|
||||
*
|
||||
* The returned object is not guaranteed to be long-lived. If the Folder's state changes, it's
|
||||
* possible a new FolderProperties will be set in its place. Instead of monitoring the fields
|
||||
* of the FolderProperties for changes, use Account.folders_contents_changed() to be notified
|
||||
* of changes and use the (potentially new) FolderProperties returned by this method at that
|
||||
* point.
|
||||
*/
|
||||
public abstract Geary.FolderProperties get_properties();
|
||||
|
||||
/**
|
||||
* Returns the special folder type of the folder.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -45,10 +45,12 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractFolder, Geary.FolderSupport
|
|||
private Geary.Smtp.ClientSession smtp;
|
||||
private int open_count = 0;
|
||||
private Nonblocking.Mailbox<OutboxRow> outbox_queue = new Nonblocking.Mailbox<OutboxRow>();
|
||||
private SmtpOutboxFolderProperties properties = new SmtpOutboxFolderProperties(0, 0);
|
||||
private SmtpOutboxFolderProperties _properties = new SmtpOutboxFolderProperties(0, 0);
|
||||
|
||||
public override Account account { get { return _account; } }
|
||||
|
||||
public override FolderProperties properties { get { return _properties; } }
|
||||
|
||||
// Requires the Database from the get-go because it runs a background task that access it
|
||||
// whether open or not
|
||||
public SmtpOutboxFolder(ImapDB.Database db, Account account) {
|
||||
|
|
@ -91,7 +93,7 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractFolder, Geary.FolderSupport
|
|||
|
||||
if (list.size > 0) {
|
||||
// set properties now (can't do yield in ctor)
|
||||
properties.set_total(list.size);
|
||||
_properties.set_total(list.size);
|
||||
|
||||
debug("Priming outbox postman with %d stored messages", list.size);
|
||||
foreach (OutboxRow row in list)
|
||||
|
|
@ -178,7 +180,7 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractFolder, Geary.FolderSupport
|
|||
|
||||
// update properties
|
||||
try {
|
||||
properties.set_total(yield get_email_count_async(null));
|
||||
_properties.set_total(yield get_email_count_async(null));
|
||||
} catch (Error err) {
|
||||
debug("Outbox postman: Unable to fetch updated email count for properties: %s",
|
||||
err.message);
|
||||
|
|
@ -198,10 +200,6 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractFolder, Geary.FolderSupport
|
|||
return path;
|
||||
}
|
||||
|
||||
public override Geary.FolderProperties get_properties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public override Geary.SpecialFolderType get_special_folder_type() {
|
||||
return Geary.SpecialFolderType.OUTBOX;
|
||||
}
|
||||
|
|
@ -286,7 +284,7 @@ private class Geary.SmtpOutboxFolder : Geary.AbstractFolder, Geary.FolderSupport
|
|||
assert(row != null);
|
||||
|
||||
// update properties
|
||||
properties.set_total(yield get_email_count_async(cancellable));
|
||||
_properties.set_total(yield get_email_count_async(cancellable));
|
||||
|
||||
// immediately add to outbox queue for delivery
|
||||
outbox_queue.send(row);
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
|
|||
debug("Oldest local email in %s not old enough (%s vs. %s), synchronizing...",
|
||||
folder.to_string(), oldest_local.to_string(), epoch.to_string());
|
||||
}
|
||||
} else if (folder.get_properties().email_total == 0) {
|
||||
} else if (folder.properties.email_total == 0) {
|
||||
// no local messages, no remote messages -- this is as good as having everything up
|
||||
// to the epoch
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
Geary.Email.Field.PROPERTIES | ImapDB.Folder.REQUIRED_FOR_DUPLICATE_DETECTION;
|
||||
|
||||
public override Account account { get { return _account; } }
|
||||
public override FolderProperties properties { get { return _properties; } }
|
||||
internal ImapDB.Folder local_folder { get; protected set; }
|
||||
internal Imap.Folder? remote_folder { get; protected set; default = null; }
|
||||
internal EmailPrefetcher email_prefetcher { get; private set; }
|
||||
internal EmailFlagWatcher email_flag_watcher;
|
||||
|
||||
private weak GenericAccount _account;
|
||||
private Geary.AggregatedFolderProperties _properties = new Geary.AggregatedFolderProperties();
|
||||
private Imap.Account remote;
|
||||
private ImapDB.Account local;
|
||||
private SpecialFolderType special_folder_type;
|
||||
|
|
@ -37,6 +39,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
this.local = local;
|
||||
this.local_folder = local_folder;
|
||||
this.special_folder_type = special_folder_type;
|
||||
_properties.add(local_folder.get_properties());
|
||||
|
||||
email_flag_watcher = new EmailFlagWatcher(this);
|
||||
email_flag_watcher.email_flags_changed.connect(on_email_flags_changed);
|
||||
|
|
@ -53,16 +56,6 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
return local_folder.get_path();
|
||||
}
|
||||
|
||||
public override Geary.FolderProperties get_properties() {
|
||||
// Get properties in order of authoritativeness:
|
||||
// - From open remote folder
|
||||
// - Fetch from local store
|
||||
if (remote_folder != null && get_open_state() == OpenState.BOTH)
|
||||
return remote_folder.properties;
|
||||
|
||||
return local_folder.get_properties();
|
||||
}
|
||||
|
||||
public override Geary.SpecialFolderType get_special_folder_type() {
|
||||
return special_folder_type;
|
||||
}
|
||||
|
|
@ -581,6 +574,8 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
return;
|
||||
}
|
||||
|
||||
_properties.add(remote_folder.properties);
|
||||
|
||||
// notify any subscribers with similar information
|
||||
notify_opened(
|
||||
(remote_folder != null) ? Geary.Folder.OpenState.BOTH : Geary.Folder.OpenState.LOCAL,
|
||||
|
|
@ -591,6 +586,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
|
|||
if (open_count == 0 || --open_count > 0)
|
||||
return;
|
||||
|
||||
_properties.remove(remote_folder.properties);
|
||||
yield close_internal_async(CloseReason.LOCAL_CLOSE, CloseReason.REMOTE_CLOSE, cancellable);
|
||||
}
|
||||
|
||||
|
|
|
|||
42
src/engine/util/util-object.vala
Normal file
42
src/engine/util/util-object.vala
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* Copyright 2013 Yorba Foundation
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
namespace Geary.ObjectUtils {
|
||||
|
||||
/**
|
||||
* Creates a set of property bindings from source to dest with the given binding flags.
|
||||
*/
|
||||
public Gee.List<Binding>? mirror_properties(Object source, Object dest, BindingFlags
|
||||
flags = GLib.BindingFlags.DEFAULT | GLib.BindingFlags.SYNC_CREATE) {
|
||||
// Make sets of both object's properties.
|
||||
Gee.HashSet<ParamSpec> source_properties = new Gee.HashSet<ParamSpec>();
|
||||
source_properties.add_all_array(source.get_class().list_properties());
|
||||
Gee.HashSet<ParamSpec> dest_properties = new Gee.HashSet<ParamSpec>();
|
||||
dest_properties.add_all_array(dest.get_class().list_properties());
|
||||
|
||||
// Remove properties from source_properties that are not in both sets.
|
||||
source_properties.retain_all(dest_properties);
|
||||
|
||||
// Create all bindings.
|
||||
Gee.List<Binding> bindings = new Gee.ArrayList<Binding>();
|
||||
foreach(ParamSpec ps in source_properties)
|
||||
bindings.add(source.bind_property(ps.name, dest, ps.name, flags));
|
||||
|
||||
return bindings.size > 0 ? bindings : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a property mirror created by mirror_properties
|
||||
*/
|
||||
public void unmirror_properties(Gee.List<Binding> bindings) {
|
||||
foreach(Binding b in bindings)
|
||||
b.unref();
|
||||
|
||||
bindings.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue