Commit 5594abd9 authored by Charles Lindsay's avatar Charles Lindsay

Allow operations on email in any folder; fix #6496

This is squashed commit (sorry -- we'll get better about maintaining a
clean history in collaborative branches in the future!) of a massive
amount of work from Jim and myself.

* EmailIdentifiers for normal (i.e. not outbox, etc.) emails are the
  same regardless of which folder they came from
* New EmailStore interface to manipulate messages that reside in any
  folder, without having to care what folders are open
* Relevant places that manipulate emails (e.g. the toolbar) have been
  updated to use the new EmailStore interface
* Conversation and ImplConversation have been smooshed together
* Many, many more items and bugfixes related to the above points
parent 48b72968
......@@ -23,7 +23,6 @@ engine/api/geary-contact.vala
engine/api/geary-contact-flags.vala
engine/api/geary-contact-importance.vala
engine/api/geary-contact-store.vala
engine/api/geary-conversation.vala
engine/api/geary-credentials.vala
engine/api/geary-credentials-mediator.vala
engine/api/geary-email-flags.vala
......@@ -50,7 +49,9 @@ engine/api/geary-search-folder.vala
engine/api/geary-service-provider.vala
engine/api/geary-special-folder-type.vala
engine/app/app-conversation.vala
engine/app/app-conversation-monitor.vala
engine/app/app-email-store.vala
engine/app/conversation-monitor/app-append-operation.vala
engine/app/conversation-monitor/app-conversation-operation-queue.vala
......@@ -58,13 +59,18 @@ engine/app/conversation-monitor/app-conversation-operation.vala
engine/app/conversation-monitor/app-conversation-set.vala
engine/app/conversation-monitor/app-external-append-operation.vala
engine/app/conversation-monitor/app-fill-window-operation.vala
engine/app/conversation-monitor/app-impl-conversation.vala
engine/app/conversation-monitor/app-local-load-operation.vala
engine/app/conversation-monitor/app-local-search-operation.vala
engine/app/conversation-monitor/app-remove-operation.vala
engine/app/conversation-monitor/app-reseed-operation.vala
engine/app/conversation-monitor/app-terminate-operation.vala
engine/app/email-store/app-async-folder-operation.vala
engine/app/email-store/app-copy-operation.vala
engine/app/email-store/app-fetch-operation.vala
engine/app/email-store/app-list-operation.vala
engine/app/email-store/app-mark-operation.vala
engine/common/common-message-data.vala
engine/db/db.vala
......@@ -84,7 +90,6 @@ engine/imap/imap.vala
engine/imap/imap-error.vala
engine/imap/api/imap-account.vala
engine/imap/api/imap-email-flags.vala
engine/imap/api/imap-email-identifier.vala
engine/imap/api/imap-email-properties.vala
engine/imap/api/imap-folder-properties.vala
engine/imap/api/imap-folder.vala
......@@ -191,6 +196,7 @@ engine/imap-engine/imap-engine-replay-queue.vala
engine/imap-engine/imap-engine-send-replay-operation.vala
engine/imap-engine/gmail/imap-engine-gmail-account.vala
engine/imap-engine/gmail/imap-engine-gmail-folder.vala
engine/imap-engine/gmail/imap-engine-gmail-search-folder.vala
engine/imap-engine/other/imap-engine-other-account.vala
engine/imap-engine/other/imap-engine-other-folder.vala
engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
......@@ -204,6 +210,7 @@ engine/imap-engine/replay-ops/imap-engine-move-email.vala
engine/imap-engine/replay-ops/imap-engine-replay-append.vala
engine/imap-engine/replay-ops/imap-engine-replay-disconnect.vala
engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
engine/imap-engine/yahoo/imap-engine-yahoo-folder.vala
......
......@@ -189,7 +189,7 @@ public class ComposerWindow : Gtk.Window {
private Gtk.UIManager ui;
public ComposerWindow(Geary.Account account, ComposeType compose_type,
Geary.Email? referred = null) {
Geary.Email? referred = null, bool is_referred_draft = false) {
this.account = account;
this.compose_type = compose_type;
......@@ -327,7 +327,7 @@ public class ComposerWindow : Gtk.Window {
try {
Geary.Folder? draft_folder = account.get_special_folder(Geary.SpecialFolderType.DRAFTS);
if (draft_folder != null && referred.id.folder_path.equal_to(draft_folder.path))
if (draft_folder != null && is_referred_draft)
draft_id = referred.id;
} catch (Error e) {
debug("Error looking up special folder: %s", e.message);
......
This diff is collapsed.
......@@ -18,7 +18,7 @@ public class ConversationListStore : Gtk.ListStore {
public static Type[] get_types() {
return {
typeof (FormattedConversationData), // CONVERSATION_DATA
typeof (Geary.Conversation) // CONVERSATION_OBJECT
typeof (Geary.App.Conversation) // CONVERSATION_OBJECT
};
}
......@@ -41,6 +41,7 @@ public class ConversationListStore : Gtk.ListStore {
private Geary.App.ConversationMonitor conversation_monitor;
private Geary.Folder? current_folder = null;
private Geary.App.EmailStore? email_store = null;
private Cancellable? cancellable_folder = null;
private bool loading_local_only = true;
private int conversations_added_counter = 0;
......@@ -97,6 +98,7 @@ public class ConversationListStore : Gtk.ListStore {
public void set_current_folder(Geary.Folder? current_folder, Cancellable? cancellable_folder) {
this.current_folder = current_folder;
this.cancellable_folder = cancellable_folder;
email_store = (current_folder == null ? null : new Geary.App.EmailStore(current_folder.account));
}
public Geary.EmailIdentifier? get_lowest_email_id() {
......@@ -106,7 +108,7 @@ public class ConversationListStore : Gtk.ListStore {
Geary.EmailIdentifier? lowest_id = null;
do {
Geary.Conversation? conversation = get_conversation_at_iter(iter);
Geary.App.Conversation? conversation = get_conversation_at_iter(iter);
if (conversation == null)
continue;
......@@ -114,14 +116,14 @@ public class ConversationListStore : Gtk.ListStore {
if (conversation_lowest == null)
continue;
if (lowest_id == null || conversation_lowest.compare_to(lowest_id) < 0)
if (lowest_id == null || conversation_lowest.natural_sort_comparator(lowest_id) < 0)
lowest_id = conversation_lowest;
} while (iter_next(ref iter));
return lowest_id;
}
public Geary.Conversation? get_conversation_at_path(Gtk.TreePath path) {
public Geary.App.Conversation? get_conversation_at_path(Gtk.TreePath path) {
Gtk.TreeIter iter;
if (!get_iter(out iter, path))
return null;
......@@ -129,7 +131,7 @@ public class ConversationListStore : Gtk.ListStore {
return get_conversation_at_iter(iter);
}
public Gtk.TreePath? get_path_for_conversation(Geary.Conversation conversation) {
public Gtk.TreePath? get_path_for_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter))
return null;
......@@ -169,23 +171,17 @@ public class ConversationListStore : Gtk.ListStore {
if (current_folder == null || !GearyApplication.instance.config.display_preview)
return;
Gee.Collection<Geary.EmailIdentifier> folder_emails_needing_previews;
Gee.Collection<Geary.EmailIdentifier> account_emails_needing_previews;
get_emails_needing_previews(out folder_emails_needing_previews, out account_emails_needing_previews);
Gee.Set<Geary.EmailIdentifier> needing_previews = get_emails_needing_previews();
Gee.ArrayList<Geary.Email> emails = new Gee.ArrayList<Geary.Email>();
if (folder_emails_needing_previews.size > 0)
emails.add_all(yield do_get_folder_previews_async(conversation_monitor,
folder_emails_needing_previews));
if (account_emails_needing_previews.size > 0)
emails.add_all(yield do_get_account_previews_async(conversation_monitor,
account_emails_needing_previews));
if (needing_previews.size > 0)
emails.add_all(yield do_get_previews_async(needing_previews));
if (emails.size < 1)
return;
debug("Displaying %d previews for %s...", emails.size, current_folder.to_string());
foreach (Geary.Email email in emails) {
Geary.Conversation? conversation = conversation_monitor.get_conversation_for_email(email.id);
Geary.App.Conversation? conversation = conversation_monitor.get_conversation_for_email(email.id);
if (conversation != null)
set_preview_for_conversation(conversation, email);
else
......@@ -194,17 +190,16 @@ public class ConversationListStore : Gtk.ListStore {
debug("Displayed %d previews for %s", emails.size, current_folder.to_string());
}
private async Gee.List<Geary.Email> do_get_folder_previews_async(
Geary.App.ConversationMonitor conversation_monitor,
private async Gee.Collection<Geary.Email> do_get_previews_async(
Gee.Collection<Geary.EmailIdentifier> emails_needing_previews) {
Geary.Folder.ListFlags flags = (loading_local_only) ? Geary.Folder.ListFlags.LOCAL_ONLY
: Geary.Folder.ListFlags.NONE;
Gee.List<Geary.Email>? emails = null;
Gee.Collection<Geary.Email>? emails = null;
try {
debug("Loading %d previews for %s...", emails_needing_previews.size, current_folder.to_string());
emails = yield current_folder.list_email_by_sparse_id_async(emails_needing_previews,
debug("Loading %d previews...", emails_needing_previews.size);
emails = yield email_store.list_email_by_sparse_id_async(emails_needing_previews,
ConversationListStore.WITH_PREVIEW_FIELDS, flags, cancellable_folder);
debug("Loaded %d previews for %s...", emails_needing_previews.size, current_folder.to_string());
debug("Loaded %d previews...", emails_needing_previews.size);
} catch (Error err) {
// Ignore NOT_FOUND, as that's entirely possible when waiting for the remote to open
if (!(err is Geary.EngineError.NOT_FOUND))
......@@ -214,40 +209,19 @@ public class ConversationListStore : Gtk.ListStore {
return emails ?? new Gee.ArrayList<Geary.Email>();
}
private async Gee.List<Geary.Email> do_get_account_previews_async(
Geary.App.ConversationMonitor conversation_monitor,
Gee.Collection<Geary.EmailIdentifier> emails_needing_previews) {
debug("Loading %d previews from %s...", emails_needing_previews.size,
current_folder.account.to_string());
Gee.List<Geary.Email> emails = new Gee.ArrayList<Geary.Email>();
foreach (Geary.EmailIdentifier id in emails_needing_previews) {
try {
emails.add(yield current_folder.account.local_fetch_email_async(id,
ConversationListStore.WITH_PREVIEW_FIELDS, cancellable_folder));
} catch (Error err) {
debug("Unable to fetch preview for %s: %s", id.to_string(), err.message);
}
}
debug("Loaded %d previews from %s...", emails_needing_previews.size,
current_folder.account.to_string());
private Gee.Set<Geary.EmailIdentifier> get_emails_needing_previews() {
Gee.Set<Geary.EmailIdentifier> needing = new Gee.HashSet<Geary.EmailIdentifier>();
return emails;
}
private void get_emails_needing_previews(out Gee.Collection<Geary.EmailIdentifier> folder_emails,
out Gee.Collection<Geary.EmailIdentifier> account_emails) {
// sort the conversations so the previews are fetched from the newest to the oldest, matching
// the user experience
Gee.TreeSet<Geary.Conversation> sorted_conversations = new Geary.Collection.FixedTreeSet<Geary.Conversation>(
Gee.TreeSet<Geary.App.Conversation> sorted_conversations
= new Geary.Collection.FixedTreeSet<Geary.App.Conversation>(
compare_conversation_descending);
sorted_conversations.add_all(conversation_monitor.get_conversations());
folder_emails = new Gee.HashSet<Geary.EmailIdentifier>();
account_emails = new Gee.HashSet<Geary.EmailIdentifier>();
foreach (Geary.Conversation conversation in sorted_conversations) {
foreach (Geary.App.Conversation conversation in sorted_conversations) {
// find oldest unread message for the preview
Geary.Email? need_preview = null;
foreach (Geary.Email email in conversation.get_emails(Geary.Conversation.Ordering.DATE_ASCENDING)) {
foreach (Geary.Email email in conversation.get_emails(Geary.App.Conversation.Ordering.DATE_ASCENDING)) {
if (email.email_flags.is_unread()) {
need_preview = email;
......@@ -255,9 +229,9 @@ public class ConversationListStore : Gtk.ListStore {
}
}
// if all are read, use newest message
// if all are read, use newest in-folder message
if (need_preview == null)
need_preview = conversation.get_latest_email();
need_preview = conversation.get_latest_email(true);
if (need_preview == null)
continue;
......@@ -271,14 +245,13 @@ public class ConversationListStore : Gtk.ListStore {
continue;
}
if (need_preview.id.folder_path == null)
account_emails.add(need_preview.id);
else
folder_emails.add(need_preview.id);
needing.add(need_preview.id);
}
return needing;
}
private Geary.Email? get_preview_for_conversation(Geary.Conversation conversation) {
private Geary.Email? get_preview_for_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
debug("Unable to find preview for conversation");
......@@ -290,7 +263,7 @@ public class ConversationListStore : Gtk.ListStore {
return message_data == null ? null : message_data.preview;
}
private void set_preview_for_conversation(Geary.Conversation conversation, Geary.Email preview) {
private void set_preview_for_conversation(Geary.App.Conversation conversation, Geary.Email preview) {
Gtk.TreeIter iter;
if (get_iter_for_conversation(conversation, out iter))
set_row(iter, conversation, preview);
......@@ -298,7 +271,7 @@ public class ConversationListStore : Gtk.ListStore {
debug("Unable to find preview for conversation");
}
private void set_row(Gtk.TreeIter iter, Geary.Conversation conversation, Geary.Email preview) {
private void set_row(Gtk.TreeIter iter, Geary.App.Conversation conversation, Geary.Email preview) {
FormattedConversationData conversation_data = new FormattedConversationData(conversation,
preview, current_folder, account_owner_email);
set(iter,
......@@ -306,7 +279,7 @@ public class ConversationListStore : Gtk.ListStore {
Column.CONVERSATION_OBJECT, conversation);
}
private void refresh_conversation(Geary.Conversation conversation) {
private void refresh_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
// Unknown conversation, attempt to append it.
......@@ -339,7 +312,7 @@ public class ConversationListStore : Gtk.ListStore {
}
}
private void refresh_flags(Geary.Conversation conversation) {
private void refresh_flags(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (!get_iter_for_conversation(conversation, out iter)) {
// Unknown conversation, attempt to append it.
......@@ -359,7 +332,7 @@ public class ConversationListStore : Gtk.ListStore {
row_changed(path, iter);
}
private bool get_iter_for_conversation(Geary.Conversation conversation, out Gtk.TreeIter iter) {
private bool get_iter_for_conversation(Geary.App.Conversation conversation, out Gtk.TreeIter iter) {
if (!get_iter_first(out iter))
return false;
......@@ -371,12 +344,12 @@ public class ConversationListStore : Gtk.ListStore {
return false;
}
private bool has_conversation(Geary.Conversation conversation) {
private bool has_conversation(Geary.App.Conversation conversation) {
return get_iter_for_conversation(conversation, null);
}
private Geary.Conversation? get_conversation_at_iter(Gtk.TreeIter iter) {
Geary.Conversation? conversation;
private Geary.App.Conversation? get_conversation_at_iter(Gtk.TreeIter iter) {
Geary.App.Conversation? conversation;
get(iter, Column.CONVERSATION_OBJECT, out conversation);
return conversation;
......@@ -389,13 +362,13 @@ public class ConversationListStore : Gtk.ListStore {
return message_data;
}
private void remove_conversation(Geary.Conversation conversation) {
private void remove_conversation(Geary.App.Conversation conversation) {
Gtk.TreeIter iter;
if (get_iter_for_conversation(conversation, out iter))
remove(iter);
}
private bool add_conversation(Geary.Conversation conversation) {
private bool add_conversation(Geary.App.Conversation conversation) {
Geary.Email? last_email = conversation.get_latest_email();
if (last_email == null) {
debug("Cannot add conversation: last email is null");
......@@ -421,7 +394,7 @@ public class ConversationListStore : Gtk.ListStore {
loading_local_only = false;
}
private void on_conversations_added(Gee.Collection<Geary.Conversation> conversations) {
private void on_conversations_added(Gee.Collection<Geary.App.Conversation> conversations) {
// this handler is used to initialize the display, so it's possible for an empty list to
// be passed in (the ConversationMonitor signal should never do this)
if (conversations.size == 0)
......@@ -431,7 +404,7 @@ public class ConversationListStore : Gtk.ListStore {
debug("Adding %d conversations.", conversations.size);
int added = 0;
foreach (Geary.Conversation conversation in conversations) {
foreach (Geary.App.Conversation conversation in conversations) {
if (add_conversation(conversation))
added++;
}
......@@ -446,11 +419,11 @@ public class ConversationListStore : Gtk.ListStore {
conversations_added_finished();
}
private void on_conversation_removed(Geary.Conversation conversation) {
private void on_conversation_removed(Geary.App.Conversation conversation) {
remove_conversation(conversation);
}
private void on_conversation_appended(Geary.Conversation conversation) {
private void on_conversation_appended(Geary.App.Conversation conversation) {
if (has_conversation(conversation)) {
refresh_conversation(conversation);
} else {
......@@ -458,7 +431,7 @@ public class ConversationListStore : Gtk.ListStore {
}
}
private void on_conversation_trimmed(Geary.Conversation conversation) {
private void on_conversation_trimmed(Geary.App.Conversation conversation) {
refresh_conversation(conversation);
}
......@@ -466,7 +439,7 @@ public class ConversationListStore : Gtk.ListStore {
refresh_previews_async.begin(conversation_monitor);
}
private void on_email_flags_changed(Geary.Conversation conversation) {
private void on_email_flags_changed(Geary.App.Conversation conversation) {
refresh_flags(conversation);
// refresh previews because the oldest unread message is displayed as the preview, and if
......@@ -476,7 +449,7 @@ public class ConversationListStore : Gtk.ListStore {
}
private int sort_by_date(Gtk.TreeModel model, Gtk.TreeIter aiter, Gtk.TreeIter biter) {
Geary.Conversation a, b;
Geary.App.Conversation a, b;
get(aiter, Column.CONVERSATION_OBJECT, out a);
get(biter, Column.CONVERSATION_OBJECT, out b);
......
......@@ -4,7 +4,7 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public int compare_conversation_ascending(Geary.Conversation a, Geary.Conversation b) {
public int compare_conversation_ascending(Geary.App.Conversation a, Geary.App.Conversation b) {
Geary.Email? a_latest = a.get_latest_email(true);
Geary.Email? b_latest = b.get_latest_email(true);
......@@ -18,20 +18,7 @@ public int compare_conversation_ascending(Geary.Conversation a, Geary.Conversati
return a_latest.properties.date_received.compare(b_latest.properties.date_received);
}
public int compare_conversation_descending(Geary.Conversation a, Geary.Conversation b) {
public int compare_conversation_descending(Geary.App.Conversation a, Geary.App.Conversation b) {
return compare_conversation_ascending(b, a);
}
public async Geary.Email fetch_full_message_async(Geary.Email email, Geary.Folder folder,
Geary.Email.Field required_fields, Cancellable? cancellable) throws Error {
Geary.Email full_email;
if (email.id.folder_path == null) {
full_email = yield folder.account.local_fetch_email_async(
email.id, required_fields, cancellable);
} else {
full_email = yield folder.fetch_email_async(email.id,
required_fields, Geary.Folder.ListFlags.NONE, cancellable);
}
return full_email;
}
......@@ -13,27 +13,26 @@ public class ConversationListView : Gtk.TreeView {
// scroll adjustment seen at the call to load_more().
private double last_upper = -1.0;
private bool reset_adjustment = false;
private Gee.Set<Geary.Conversation> selected = new Gee.HashSet<Geary.Conversation>();
private Gee.Set<Geary.App.Conversation> selected = new Gee.HashSet<Geary.App.Conversation>();
private ConversationListStore conversation_list_store;
private Geary.App.ConversationMonitor? conversation_monitor;
private Gee.Set<Geary.Conversation>? current_visible_conversations = null;
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;
// Signal for conversations that have been single clicked or otherwise selected.
public signal void conversations_selected(Gee.Set<Geary.Conversation> selected);
public signal void conversations_selected(Gee.Set<Geary.App.Conversation> selected);
// Signal for when a conversation has been double-clicked, or selected and enter is pressed.
public signal void conversation_activated(Geary.Conversation activated);
public signal void conversation_activated(Geary.App.Conversation activated);
public virtual signal void load_more() {
enable_load_more = false;
}
public signal void mark_conversation(Geary.Conversation conversation,
public signal void mark_conversation(Geary.App.Conversation conversation,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, bool only_mark_preview);
public signal void visible_conversations_changed(Gee.Set<Geary.Conversation> visible);
public signal void visible_conversations_changed(Gee.Set<Geary.App.Conversation> visible);
public ConversationListView(ConversationListStore conversation_list_store) {
this.conversation_list_store = conversation_list_store;
......@@ -101,7 +100,7 @@ public class ConversationListView : Gtk.TreeView {
select_first_conversation();
}
private void on_conversation_removed(Geary.Conversation conversation) {
private void on_conversation_removed(Geary.App.Conversation conversation) {
if (!GearyApplication.instance.config.autoselect)
unselect_all();
}
......@@ -149,7 +148,7 @@ public class ConversationListView : Gtk.TreeView {
(event.state & Gdk.ModifierType.CONTROL_MASK) == 0 &&
event.type == Gdk.EventType.BUTTON_PRESS && cell_x < 25 && cell_y < 25) {
Geary.Conversation conversation = conversation_list_store.get_conversation_at_path(path);
Geary.App.Conversation conversation = conversation_list_store.get_conversation_at_path(path);
Geary.EmailFlags flags = new Geary.EmailFlags();
flags.add(Geary.EmailFlags.FLAGGED);
if (conversation.is_flagged()) {
......@@ -161,7 +160,7 @@ public class ConversationListView : Gtk.TreeView {
}
if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) {
Geary.Conversation conversation = conversation_list_store.get_conversation_at_path(path);
Geary.App.Conversation conversation = conversation_list_store.get_conversation_at_path(path);
string?[] action_names = {};
action_names += GearyController.ACTION_DELETE_MESSAGE;
......@@ -272,22 +271,22 @@ public class ConversationListView : Gtk.TreeView {
}
// Conversations are selected, so collect them and signal if different
Gee.HashSet<Geary.Conversation> new_selected = new Gee.HashSet<Geary.Conversation>();
Gee.HashSet<Geary.App.Conversation> new_selected = new Gee.HashSet<Geary.App.Conversation>();
foreach (Gtk.TreePath path in paths) {
Geary.Conversation? conversation = conversation_list_store.get_conversation_at_path(path);
Geary.App.Conversation? conversation = conversation_list_store.get_conversation_at_path(path);
if (conversation != null)
new_selected.add(conversation);
}
// only notify if different than what was previously reported
if (!Geary.Collection.are_sets_equal<Geary.Conversation>(selected, new_selected)) {
if (!Geary.Collection.are_sets_equal<Geary.App.Conversation>(selected, new_selected)) {
selected = new_selected;
conversations_selected(selected.read_only_view);
}
}
public Gee.Set<Geary.Conversation> get_visible_conversations() {
Gee.HashSet<Geary.Conversation> visible_conversations = new Gee.HashSet<Geary.Conversation>();
public Gee.Set<Geary.App.Conversation> get_visible_conversations() {
Gee.HashSet<Geary.App.Conversation> visible_conversations = new Gee.HashSet<Geary.App.Conversation>();
Gtk.TreePath start_path;
Gtk.TreePath end_path;
......@@ -295,7 +294,7 @@ public class ConversationListView : Gtk.TreeView {
return visible_conversations;
while (start_path.compare(end_path) <= 0) {
Geary.Conversation? conversation = conversation_list_store.get_conversation_at_path(start_path);
Geary.App.Conversation? conversation = conversation_list_store.get_conversation_at_path(start_path);
if (conversation != null)
visible_conversations.add(conversation);
......@@ -307,9 +306,10 @@ public class ConversationListView : Gtk.TreeView {
// Always returns false, so it can be used as a one-time SourceFunc
private bool update_visible_conversations() {
Gee.Set<Geary.Conversation> visible_conversations = get_visible_conversations();
Gee.Set<Geary.App.Conversation> visible_conversations = get_visible_conversations();
if (current_visible_conversations != null
&& Geary.Collection.are_sets_equal<Geary.Conversation>(current_visible_conversations, visible_conversations)) {
&& Geary.Collection.are_sets_equal<Geary.App.Conversation>(
current_visible_conversations, visible_conversations)) {
return false;
}
......@@ -331,7 +331,7 @@ public class ConversationListView : Gtk.TreeView {
}
}
public void select_conversation(Geary.Conversation conversation) {
public void select_conversation(Geary.App.Conversation conversation) {
Gtk.TreePath path = conversation_list_store.get_path_for_conversation(conversation);
if (path != null)
set_cursor(path, null, false);
......@@ -361,7 +361,7 @@ public class ConversationListView : Gtk.TreeView {
}
private void on_row_activated(Gtk.TreePath path) {
Geary.Conversation? c = conversation_list_store.get_conversation_at_path(path);
Geary.App.Conversation? c = conversation_list_store.get_conversation_at_path(path);
if (c != null)
conversation_activated(c);
}
......
......@@ -23,8 +23,6 @@ public class ConversationViewer : Gtk.Box {
private const string MESSAGE_CONTAINER_ID = "message_container";
private const string SELECTION_COUNTER_ID = "multiple_messages";
private const string SPINNER_ID = "spinner";
private const Geary.Email.Field FULL_FETCH_FIELDS = ConversationViewer.REQUIRED_FIELDS |
Geary.ComposedEmail.REQUIRED_REPLY_FIELDS;
private enum SearchState {
// Search/find states.
......@@ -84,8 +82,9 @@ public class ConversationViewer : Gtk.Box {
// Fired when the user clicks "forward" in the message menu.
public signal void forward_message(Geary.Email message);
// Fired when the user marks a message.
public signal void mark_message(Geary.Email message, Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove);
// Fired when the user mark messages.
public signal void mark_messages(Gee.Collection<Geary.EmailIdentifier> emails,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove);
// Fired when the user opens an attachment.
public signal void open_attachment(Geary.Attachment attachment);
......@@ -104,7 +103,7 @@ public class ConversationViewer : Gtk.Box {
public ConversationWebView web_view { get; private set; }
// Current conversation, or null if none.
public Geary.Conversation? current_conversation = null;
public Geary.App.Conversation? current_conversation = null;
// Label for displaying overlay messages.
private Gtk.Label message_overlay_label;
......@@ -123,6 +122,7 @@ public class ConversationViewer : Gtk.Box {
private Gtk.Menu? attachment_menu = null;
private weak Geary.Folder? current_folder = null;
private weak Geary.SearchFolder? search_folder = null;
private Geary.App.EmailStore? email_store = null;
private Geary.AccountInformation? current_account_information = null;
private ConversationFindBar conversation_find_bar;
private Cancellable cancellable_fetch = new Cancellable();
......@@ -235,6 +235,7 @@ public class ConversationViewer : Gtk.Box {
private void on_folder_selected(Geary.Folder? folder) {
current_folder = folder;
email_store = (current_folder == null ? null : new Geary.App.EmailStore(current_folder.account));
fsm.issue(SearchEvent.RESET);
if (folder == null) {
......@@ -250,7 +251,7 @@ public class ConversationViewer : Gtk.Box {
}
}
private void on_conversations_selected(Gee.Set<Geary.Conversation>? conversations,
private void on_conversations_selected(Gee.Set<Geary.App.Conversation>? conversations,
Geary.Folder? current_folder) {
cancel_load();
if (current_conversation != null) {
......@@ -300,22 +301,21 @@ public class ConversationViewer : Gtk.Box {
}
}
private async void select_conversation_async(Geary.Conversation conversation,
private async void select_conversation_async(Geary.App.Conversation conversation,
Geary.Folder current_folder) throws Error {
Gee.Collection<Geary.Email> messages = conversation.get_emails(Geary.Conversation.Ordering.DATE_ASCENDING);
// Load this once, so if it's cancelled, we cancel the WHOLE load.
Cancellable cancellable = cancellable_fetch;
// Fetch full messages.
Gee.Collection<Geary.Email> messages_to_add = new Gee.HashSet<Geary.Email>();
foreach (Geary.Email email in messages)
messages_to_add.add(yield fetch_full_message_async(email, current_folder, FULL_FETCH_FIELDS,
cancellable));
Gee.Collection<Geary.Email>? messages_to_add
= yield list_full_messages_async(conversation.get_emails(
Geary.App.Conversation.Ordering.DATE_ASCENDING), cancellable);
// Add messages.
foreach (Geary.Email email in messages_to_add)
add_message(email);
if (messages_to_add != null) {
foreach (Geary.Email email in messages_to_add)
add_message(email, conversation.is_in_current_folder(email.id));
}
if (current_folder is Geary.SearchFolder) {
yield highlight_search_terms();
......@@ -329,7 +329,6 @@ public class ConversationViewer : Gtk.Box {