Refactored main window and geary application to create new controller object. Closes #4308

This commit is contained in:
Eric Gregory 2011-11-22 18:55:07 -08:00
parent 68d554950b
commit 1f3d3da3ca
5 changed files with 535 additions and 509 deletions

View file

@ -17,17 +17,11 @@ public class GearyApplication : YorbaApplication {
public const string WEBSITE = "http://www.yorba.org";
public static string WEBSITE_LABEL = _("Visit the Yorba web site");
// Named actions.
public const string ACTION_DONATE = "GearyDonate";
public const string ACTION_ABOUT = "GearyAbout";
public const string ACTION_QUIT = "GearyQuit";
public const string ACTION_NEW_MESSAGE = "GearyNewMessage";
public const string ACTION_DEBUG_PRINT = "GearyDebugPrint";
public const string PREFIX = _PREFIX;
public const string[] AUTHORS = {
"Jim Nelson <jim@yorba.org>",
"Eric Gregory <eric@yorba.org>",
null
};
@ -68,7 +62,7 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
private static GearyApplication _instance = null;
private MainWindow? main_window = null;
private GearyController? controller = null;
private Geary.EngineAccount? account = null;
private File exec_dir;
@ -81,18 +75,14 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
public override void activate() {
// If Geary is already running, show the main window and return.
if (main_window != null) {
main_window.present();
if (controller != null && controller.main_window != null) {
controller.main_window.present();
return;
}
exec_dir = (File.new_for_path(Environment.find_program_in_path(args[0]))).get_parent();
// Start Geary.
actions.add_actions(create_actions(), this);
ui_manager.insert_action_group(actions, 0);
load_ui_file("accelerators.ui");
config = new Configuration(GearyApplication.instance.get_install_dir() != null,
GearyApplication.instance.get_exec_dir().get_child("build/src/client").get_path());
@ -131,15 +121,15 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
error("Unable to open mail database for %s: %s", cred.user, err.message);
}
main_window = new MainWindow();
main_window.show_all();
main_window.start(account);
controller = new GearyController();
controller.start(account);
return;
}
public override void exiting(bool panicked) {
if (main_window != null)
main_window.destroy();
if (controller.main_window != null)
controller.main_window.destroy();
}
public File get_user_data_directory() {
@ -194,103 +184,8 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
}
}
private Gtk.ActionEntry[] create_actions() {
Gtk.ActionEntry[] entries = new Gtk.ActionEntry[0];
Gtk.ActionEntry donate = { ACTION_DONATE, null, TRANSLATABLE, null, null, on_donate };
donate.label = _("_Donate");
entries += donate;
Gtk.ActionEntry about = { ACTION_ABOUT, Gtk.Stock.ABOUT, TRANSLATABLE, null, null, on_about };
about.label = _("_About");
entries += about;
Gtk.ActionEntry quit = { ACTION_QUIT, Gtk.Stock.QUIT, TRANSLATABLE, "<Ctrl>Q", null, on_quit };
quit.label = _("_Quit");
entries += quit;
Gtk.ActionEntry new_message = { ACTION_NEW_MESSAGE, Gtk.Stock.NEW, TRANSLATABLE, "<Ctrl>N",
null, on_new_message };
new_message.label = _("_New Message");
entries += new_message;
Gtk.ActionEntry secret_debug = { ACTION_DEBUG_PRINT, null, null, "<Ctrl><Alt>P",
null, on_debug_print };
entries += secret_debug;
return entries;
}
public void on_quit() {
GearyApplication.instance.exit();
}
public void on_about() {
Gtk.show_about_dialog(main_window,
"program-name", GearyApplication.NAME,
"comments", GearyApplication.DESCRIPTION,
"authors", GearyApplication.AUTHORS,
"copyright", GearyApplication.COPYRIGHT,
"license", GearyApplication.LICENSE,
"version", GearyApplication.VERSION,
"website", GearyApplication.WEBSITE,
"website-label", GearyApplication.WEBSITE_LABEL
);
}
public void on_donate() {
try {
Gtk.show_uri(main_window.get_screen(), "http://yorba.org/donate/", Gdk.CURRENT_TIME);
} catch (Error err) {
debug("Unable to open URL. %s", err.message);
}
}
private void on_new_message() {
ComposerWindow w = new ComposerWindow();
w.set_position(Gtk.WindowPosition.CENTER);
w.send.connect(on_send);
w.show_all();
}
private void on_send(ComposerWindow cw) {
string username;
try {
// TODO: Multiple accounts.
username = Geary.Engine.get_usernames(GearyApplication.instance.get_user_data_directory())
.get(0);
} catch (Error e) {
error("Unable to get username. Error: %s", e.message);
}
Geary.ComposedEmail email = new Geary.ComposedEmail(new DateTime.now_local(),
new Geary.RFC822.MailboxAddresses.from_rfc822_string(username));
if (!Geary.String.is_empty(cw.to))
email.to = new Geary.RFC822.MailboxAddresses.from_rfc822_string(cw.to);
if (!Geary.String.is_empty(cw.cc))
email.cc = new Geary.RFC822.MailboxAddresses.from_rfc822_string(cw.cc);
if (!Geary.String.is_empty(cw.bcc))
email.bcc = new Geary.RFC822.MailboxAddresses.from_rfc822_string(cw.bcc);
if (!Geary.String.is_empty(cw.subject))
email.subject = new Geary.RFC822.Subject(cw.subject);
email.body = new Geary.RFC822.Text(new Geary.Memory.StringBuffer(cw.message));
account.send_email_async.begin(email);
cw.destroy();
}
private void on_debug_print() {
main_window.debug_print_selected();
}
public Gtk.Window get_main_window() {
return main_window;
return controller.main_window;
}
}

View file

@ -0,0 +1,517 @@
/* Copyright 2011 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.
*/
// Primary controller object for Geary.
public class GearyController {
private class FetchPreviewOperation : Geary.NonblockingBatchOperation {
public MainWindow owner;
public Geary.Folder folder;
public Geary.EmailIdentifier email_id;
public Geary.Conversation conversation;
public FetchPreviewOperation(MainWindow owner, Geary.Folder folder,
Geary.EmailIdentifier email_id, Geary.Conversation conversation) {
this.owner = owner;
this.folder = folder;
this.email_id = email_id;
this.conversation = conversation;
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
Geary.Email? preview = yield folder.fetch_email_async(email_id,
MessageListStore.WITH_PREVIEW_FIELDS, cancellable);
if (preview != null)
owner.message_list_store.set_preview_for_conversation(conversation, preview);
return null;
}
}
private class ListFoldersOperation : Geary.NonblockingBatchOperation {
public Geary.Account account;
public Geary.FolderPath path;
public ListFoldersOperation(Geary.Account account, Geary.FolderPath path) {
this.account = account;
this.path = path;
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
return yield account.list_folders_async(path, cancellable);
}
}
private class FetchSpecialFolderOperation : Geary.NonblockingBatchOperation {
public Geary.Account account;
public Geary.SpecialFolder special_folder;
public FetchSpecialFolderOperation(Geary.Account account, Geary.SpecialFolder special_folder) {
this.account = account;
this.special_folder = special_folder;
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
return yield account.fetch_folder_async(special_folder.path);
}
}
// Named actions.
public const string ACTION_DONATE = "GearyDonate";
public const string ACTION_ABOUT = "GearyAbout";
public const string ACTION_QUIT = "GearyQuit";
public const string ACTION_NEW_MESSAGE = "GearyNewMessage";
public const string ACTION_DEBUG_PRINT = "GearyDebugPrint";
private const int FETCH_EMAIL_CHUNK_COUNT = 50;
public MainWindow main_window { get; private set; }
public bool enable_load_more { get; set; default = true; }
private Cancellable cancellable_folder = new Cancellable();
private Cancellable cancellable_message = new Cancellable();
private Geary.EngineAccount? account = null;
private Geary.Folder? current_folder = null;
private Geary.Conversations? current_conversations = null;
private bool second_list_pass_required = false;
public GearyController() {
// Setup actions.
GearyApplication.instance.actions.add_actions(create_actions(), this);
GearyApplication.instance.ui_manager.insert_action_group(
GearyApplication.instance.actions, 0);
GearyApplication.instance.load_ui_file("accelerators.ui");
// Create the main window (must be done after creating actions.)
main_window = new MainWindow();
main_window.message_list_view.conversation_selected.connect(on_conversation_selected);
main_window.message_list_view.load_more.connect(on_load_more);
main_window.folder_list_view.folder_selected.connect(on_folder_selected);
}
~GearyController() {
if (account != null)
account.folders_added_removed.disconnect(on_folders_added_removed);
}
private Gtk.ActionEntry[] create_actions() {
Gtk.ActionEntry[] entries = new Gtk.ActionEntry[0];
Gtk.ActionEntry donate = { ACTION_DONATE, null, TRANSLATABLE, null, null, on_donate };
donate.label = _("_Donate");
entries += donate;
Gtk.ActionEntry about = { ACTION_ABOUT, Gtk.Stock.ABOUT, TRANSLATABLE, null, null, on_about };
about.label = _("_About");
entries += about;
Gtk.ActionEntry quit = { ACTION_QUIT, Gtk.Stock.QUIT, TRANSLATABLE, "<Ctrl>Q", null, on_quit };
quit.label = _("_Quit");
entries += quit;
Gtk.ActionEntry new_message = { ACTION_NEW_MESSAGE, Gtk.Stock.NEW, TRANSLATABLE, "<Ctrl>N",
null, on_new_message };
new_message.label = _("_New Message");
entries += new_message;
Gtk.ActionEntry secret_debug = { ACTION_DEBUG_PRINT, null, null, "<Ctrl><Alt>P",
null, debug_print_selected };
entries += secret_debug;
return entries;
}
public void start(Geary.EngineAccount account) {
this.account = account;
account.folders_added_removed.connect(on_folders_added_removed);
main_window.folder_list_store.set_user_folders_root_name(account.get_user_folders_label());
main_window.show_all();
do_start.begin();
}
private async void do_start() {
try {
// add all the special folders, which are assumed to always exist
Geary.SpecialFolderMap? special_folders = account.get_special_folder_map();
if (special_folders != null) {
Geary.NonblockingBatch batch = new Geary.NonblockingBatch();
foreach (Geary.SpecialFolder special_folder in special_folders.get_all())
batch.add(new FetchSpecialFolderOperation(account, special_folder));
debug("Listing special folders");
yield batch.execute_all_async();
debug("Completed list of special folders");
foreach (int id in batch.get_ids()) {
FetchSpecialFolderOperation op = (FetchSpecialFolderOperation)
batch.get_operation(id);
try {
Geary.Folder folder = (Geary.Folder) batch.get_result(id);
main_window.folder_list_store.add_special_folder(op.special_folder, folder);
} catch (Error inner_error) {
message("Unable to fetch special folder %s: %s",
op.special_folder.path.to_string(), inner_error.message);
}
}
// If inbox is specified, select that
Geary.SpecialFolder? inbox = special_folders.get_folder(Geary.SpecialFolderType.INBOX);
if (inbox != null)
main_window.folder_list_view.select_path(inbox.path);
}
// pull down the root-level user folders
Gee.Collection<Geary.Folder> folders = yield account.list_folders_async(null);
if (folders != null)
on_folders_added_removed(folders, null);
else
debug("no folders");
} catch (Error err) {
message("%s", err.message);
}
}
private void on_folder_selected(Geary.Folder? folder) {
if (folder == null) {
debug("no folder selected");
main_window.message_list_store.clear();
return;
}
debug("Folder %s selected", folder.to_string());
do_select_folder.begin(folder, on_select_folder_completed);
}
private async void do_select_folder(Geary.Folder folder) throws Error {
cancel_folder();
main_window.message_list_store.clear();
if (current_folder != null) {
yield current_folder.close_async();
}
current_folder = folder;
yield current_folder.open_async(true, cancellable_folder);
current_conversations = new Geary.Conversations(current_folder,
MessageListStore.REQUIRED_FIELDS);
current_conversations.monitor_new_messages(cancellable_folder);
current_conversations.scan_started.connect(on_scan_started);
current_conversations.scan_error.connect(on_scan_error);
current_conversations.scan_completed.connect(on_scan_completed);
current_conversations.conversations_added.connect(on_conversations_added);
current_conversations.conversation_appended.connect(on_conversation_appended);
current_conversations.conversation_trimmed.connect(on_conversation_trimmed);
current_conversations.conversation_removed.connect(on_conversation_removed);
current_conversations.updated_placeholders.connect(on_updated_placeholders);
// Do a quick-list of the messages (which should return what's in the local store) if
// supported by the Folder, followed by a complete list if needed
second_list_pass_required =
current_folder.get_supported_list_flags().is_all_set(Geary.Folder.ListFlags.FAST);
// Load all conversations from the DB.
current_conversations.lazy_load(-1, -1, Geary.Folder.ListFlags.FAST, cancellable_folder);
}
public void on_scan_started(Geary.EmailIdentifier? id, int low, int count) {
debug("on scan started. id = %s low = %d count = %d", id != null ? id.to_string() : "(null)",
low, count);
main_window.message_list_view.enable_load_more = false;
}
public void on_scan_error(Error err) {
debug("Scan error: %s", err.message);
}
public void on_scan_completed() {
debug("on scan completed");
do_fetch_previews.begin(cancellable_folder);
main_window.message_list_view.enable_load_more = true;
}
public void on_conversations_added(Gee.Collection<Geary.Conversation> conversations) {
debug("on conversation added");
foreach (Geary.Conversation c in conversations) {
if (!main_window.message_list_store.has_conversation(c))
main_window.message_list_store.append_conversation(c);
}
}
public void on_conversation_appended(Geary.Conversation conversation,
Gee.Collection<Geary.Email> email) {
main_window.message_list_store.update_conversation(conversation);
}
public void on_conversation_trimmed(Geary.Conversation conversation, Geary.Email email) {
main_window.message_list_store.update_conversation(conversation);
}
public void on_conversation_removed(Geary.Conversation conversation) {
main_window.message_list_store.remove_conversation(conversation);
}
public void on_updated_placeholders(Geary.Conversation conversation,
Gee.Collection<Geary.Email> email) {
main_window.message_list_store.update_conversation(conversation);
}
private void on_load_more() {
debug("on_load_more");
main_window.message_list_view.enable_load_more = false;
Geary.EmailIdentifier? low_id = main_window.message_list_store.get_email_id_lowest();
if (low_id == null)
return;
current_conversations.load_by_id_async.begin(low_id, - FETCH_EMAIL_CHUNK_COUNT,
Geary.Folder.ListFlags.EXCLUDING_ID, cancellable_folder, on_load_more_completed);
}
private void on_load_more_completed(Object? source, AsyncResult result) {
debug("on load more completed");
try {
current_conversations.load_by_id_async.end(result);
} catch (Error err) {
debug("Error, unable to load conversations: %s", err.message);
}
}
private async void do_fetch_previews(Cancellable? cancellable) throws Error {
Geary.NonblockingBatch batch = new Geary.NonblockingBatch();
int count = main_window.message_list_store.get_count();
for (int ctr = 0; ctr < count; ctr++) {
Geary.Conversation? conversation;
Geary.Email? email = main_window.message_list_store.get_newest_message_at_index(
ctr, out conversation);
if (email != null)
batch.add(new FetchPreviewOperation(main_window, current_folder, email.id,
conversation));
}
debug("Fetching %d previews", count);
yield batch.execute_all_async(cancellable);
debug("Completed fetching %d previews", count);
batch.throw_first_exception();
// with all the previews fetched, now go back and do a full list (if required)
if (second_list_pass_required) {
second_list_pass_required = false;
debug("Doing second list pass now");
current_conversations.lazy_load(-1, FETCH_EMAIL_CHUNK_COUNT, Geary.Folder.ListFlags.NONE,
cancellable);
}
}
private void on_select_folder_completed(Object? source, AsyncResult result) {
try {
do_select_folder.end(result);
} catch (Error err) {
debug("Unable to select folder: %s", err.message);
}
}
private void on_conversation_selected(Geary.Conversation? conversation) {
cancel_message();
main_window.message_viewer.clear();
if (conversation != null)
do_select_message.begin(conversation, cancellable_message, on_select_message_completed);
}
private async void do_select_message(Geary.Conversation conversation, Cancellable?
cancellable = null) throws Error {
if (current_folder == null) {
debug("Conversation selected with no folder selected");
return;
}
foreach (Geary.Email email in conversation.get_pool_sorted(compare_email)) {
Geary.Email full_email = yield current_folder.fetch_email_async(email.id,
MessageViewer.REQUIRED_FIELDS, cancellable);
if (cancellable.is_cancelled())
break;
main_window.message_viewer.add_message(full_email);
}
}
private void on_select_message_completed(Object? source, AsyncResult result) {
try {
do_select_message.end(result);
} catch (Error err) {
if (!(err is IOError.CANCELLED))
debug("Unable to select message: %s", err.message);
}
}
private void on_folders_added_removed(Gee.Collection<Geary.Folder>? added,
Gee.Collection<Geary.Folder>? removed) {
if (added != null && added.size > 0) {
Gee.Set<Geary.FolderPath>? ignored_paths = account.get_ignored_paths();
Gee.ArrayList<Geary.Folder> skipped = new Gee.ArrayList<Geary.Folder>();
foreach (Geary.Folder folder in added) {
if (ignored_paths != null && ignored_paths.contains(folder.get_path()))
skipped.add(folder);
else
main_window.folder_list_store.add_user_folder(folder);
}
Gee.Collection<Geary.Folder> remaining = added;
if (skipped.size > 0) {
remaining = new Gee.ArrayList<Geary.Folder>();
remaining.add_all(added);
remaining.remove_all(skipped);
}
search_folders_for_children.begin(remaining);
}
}
private async void search_folders_for_children(Gee.Collection<Geary.Folder> folders) {
Geary.NonblockingBatch batch = new Geary.NonblockingBatch();
foreach (Geary.Folder folder in folders)
batch.add(new ListFoldersOperation(account, folder.get_path()));
debug("Listing folder children");
try {
yield batch.execute_all_async();
} catch (Error err) {
debug("Unable to execute batch: %s", err.message);
return;
}
debug("Completed listing folder children");
Gee.ArrayList<Geary.Folder> accumulator = new Gee.ArrayList<Geary.Folder>();
foreach (int id in batch.get_ids()) {
ListFoldersOperation op = (ListFoldersOperation) batch.get_operation(id);
try {
Gee.Collection<Geary.Folder> children = (Gee.Collection<Geary.Folder>)
batch.get_result(id);
accumulator.add_all(children);
} catch (Error err2) {
debug("Unable to list children of %s: %s", op.path.to_string(), err2.message);
}
}
if (accumulator.size > 0)
on_folders_added_removed(accumulator, null);
}
public void debug_print_selected() {
if (main_window.message_viewer.messages.size == 0) {
debug("Nothing to print");
return;
}
debug("---------------------------");
foreach (Geary.Email e in main_window.message_viewer.messages) {
debug("Message: %s", e.id.to_string());
if (e.header != null)
debug("\n%s", e.header.buffer.to_utf8());
else
debug("No message data.");
debug("---------------------------");
}
}
private void cancel_folder() {
Cancellable old_cancellable = cancellable_folder;
cancellable_folder = new Cancellable();
cancel_message();
old_cancellable.cancel();
}
private void cancel_message() {
Cancellable old_cancellable = cancellable_message;
cancellable_message = new Cancellable();
old_cancellable.cancel();
}
public void on_quit() {
GearyApplication.instance.exit();
}
public void on_about() {
Gtk.show_about_dialog(main_window,
"program-name", GearyApplication.NAME,
"comments", GearyApplication.DESCRIPTION,
"authors", GearyApplication.AUTHORS,
"copyright", GearyApplication.COPYRIGHT,
"license", GearyApplication.LICENSE,
"version", GearyApplication.VERSION,
"website", GearyApplication.WEBSITE,
"website-label", GearyApplication.WEBSITE_LABEL
);
}
public void on_donate() {
try {
Gtk.show_uri(main_window.get_screen(), "http://yorba.org/donate/", Gdk.CURRENT_TIME);
} catch (Error err) {
debug("Unable to open URL. %s", err.message);
}
}
private void on_new_message() {
ComposerWindow w = new ComposerWindow();
w.set_position(Gtk.WindowPosition.CENTER);
w.send.connect(on_send);
w.show_all();
}
private void on_send(ComposerWindow cw) {
string username;
try {
// TODO: Multiple accounts.
username = Geary.Engine.get_usernames(GearyApplication.instance.get_user_data_directory())
.get(0);
} catch (Error e) {
error("Unable to get username. Error: %s", e.message);
}
Geary.ComposedEmail email = new Geary.ComposedEmail(new DateTime.now_local(),
new Geary.RFC822.MailboxAddresses.from_rfc822_string(username));
if (!Geary.String.is_empty(cw.to))
email.to = new Geary.RFC822.MailboxAddresses.from_rfc822_string(cw.to);
if (!Geary.String.is_empty(cw.cc))
email.cc = new Geary.RFC822.MailboxAddresses.from_rfc822_string(cw.cc);
if (!Geary.String.is_empty(cw.bcc))
email.bcc = new Geary.RFC822.MailboxAddresses.from_rfc822_string(cw.bcc);
if (!Geary.String.is_empty(cw.subject))
email.subject = new Geary.RFC822.Subject(cw.subject);
email.body = new Geary.RFC822.Text(new Geary.Memory.StringBuffer(cw.message));
account.send_email_async.begin(email);
cw.destroy();
}
}

View file

@ -19,7 +19,7 @@ public class MainToolbar : Gtk.VBox {
Gtk.ToolButton new_message = builder.get_object("new_button") as Gtk.ToolButton;
new_message.set_related_action(GearyApplication.instance.actions.get_action(
GearyApplication.ACTION_NEW_MESSAGE));
GearyController.ACTION_NEW_MESSAGE));
menu_button = builder.get_object("menu_button") as Gtk.ToolButton;
menu_button.clicked.connect(on_show_menu);

View file

@ -6,149 +6,31 @@
public class MainWindow : Gtk.Window {
private const int MESSAGE_LIST_WIDTH = 250;
private const int FETCH_EMAIL_CHUNK_COUNT = 50;
private class FetchPreviewOperation : Geary.NonblockingBatchOperation {
public MainWindow owner;
public Geary.Folder folder;
public Geary.EmailIdentifier email_id;
public Geary.Conversation conversation;
public FetchPreviewOperation(MainWindow owner, Geary.Folder folder,
Geary.EmailIdentifier email_id, Geary.Conversation conversation) {
this.owner = owner;
this.folder = folder;
this.email_id = email_id;
this.conversation = conversation;
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
Geary.Email? preview = yield folder.fetch_email_async(email_id,
MessageListStore.WITH_PREVIEW_FIELDS, cancellable);
if (preview != null)
owner.message_list_store.set_preview_for_conversation(conversation, preview);
return null;
}
}
public FolderListStore folder_list_store { get; private set; default = new FolderListStore(); }
public MessageListStore message_list_store { get; private set; default = new MessageListStore(); }
public MainToolbar main_toolbar { get; private set; }
public MessageListView message_list_view { get; private set; }
public FolderListView folder_list_view { get; private set; }
public MessageViewer message_viewer { get; private set; default = new MessageViewer(); }
private class ListFoldersOperation : Geary.NonblockingBatchOperation {
public Geary.Account account;
public Geary.FolderPath path;
public ListFoldersOperation(Geary.Account account, Geary.FolderPath path) {
this.account = account;
this.path = path;
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
return yield account.list_folders_async(path, cancellable);
}
}
private class FetchSpecialFolderOperation : Geary.NonblockingBatchOperation {
public Geary.Account account;
public Geary.SpecialFolder special_folder;
public FetchSpecialFolderOperation(Geary.Account account, Geary.SpecialFolder special_folder) {
this.account = account;
this.special_folder = special_folder;
}
public override async Object? execute_async(Cancellable? cancellable) throws Error {
return yield account.fetch_folder_async(special_folder.path);
}
}
private MainToolbar main_toolbar;
private MessageListStore message_list_store = new MessageListStore();
private MessageListView message_list_view;
private FolderListStore folder_list_store = new FolderListStore();
private FolderListView folder_list_view;
private MessageViewer message_viewer = new MessageViewer();
private Geary.EngineAccount? account = null;
private Geary.Folder? current_folder = null;
private Geary.Conversations? current_conversations = null;
private bool second_list_pass_required = false;
private int window_width;
private int window_height;
private bool window_maximized;
private Gtk.HPaned folder_paned = new Gtk.HPaned();
private Gtk.HPaned messages_paned = new Gtk.HPaned();
private Cancellable cancellable_folder = new Cancellable();
private Cancellable cancellable_message = new Cancellable();
public MainWindow() {
title = GearyApplication.NAME;
message_list_view = new MessageListView(message_list_store);
message_list_view.conversation_selected.connect(on_conversation_selected);
message_list_view.load_more.connect(on_load_more);
folder_list_view = new FolderListView(folder_list_store);
folder_list_view.folder_selected.connect(on_folder_selected);
add_accel_group(GearyApplication.instance.ui_manager.get_accel_group());
create_layout();
}
~MainWindow() {
if (account != null)
account.folders_added_removed.disconnect(on_folders_added_removed);
}
public void start(Geary.EngineAccount account) {
this.account = account;
account.folders_added_removed.connect(on_folders_added_removed);
folder_list_store.set_user_folders_root_name(account.get_user_folders_label());
do_start.begin();
}
private async void do_start() {
try {
// add all the special folders, which are assumed to always exist
Geary.SpecialFolderMap? special_folders = account.get_special_folder_map();
if (special_folders != null) {
Geary.NonblockingBatch batch = new Geary.NonblockingBatch();
foreach (Geary.SpecialFolder special_folder in special_folders.get_all())
batch.add(new FetchSpecialFolderOperation(account, special_folder));
debug("Listing special folders");
yield batch.execute_all_async();
debug("Completed list of special folders");
foreach (int id in batch.get_ids()) {
FetchSpecialFolderOperation op = (FetchSpecialFolderOperation) batch.get_operation(id);
try {
Geary.Folder folder = (Geary.Folder) batch.get_result(id);
folder_list_store.add_special_folder(op.special_folder, folder);
} catch (Error inner_error) {
message("Unable to fetch special folder %s: %s", op.special_folder.path.to_string(),
inner_error.message);
}
}
// If inbox is specified, select that
Geary.SpecialFolder? inbox = special_folders.get_folder(Geary.SpecialFolderType.INBOX);
if (inbox != null)
folder_list_view.select_path(inbox.path);
}
// pull down the root-level user folders
Gee.Collection<Geary.Folder> folders = yield account.list_folders_async(null);
if (folders != null)
on_folders_added_removed(folders, null);
else
debug("no folders");
} catch (Error err) {
message("%s", err.message);
}
}
public override void show_all() {
set_default_size(GearyApplication.instance.config.window_width,
GearyApplication.instance.config.window_height);
@ -222,274 +104,5 @@ public class MainWindow : Gtk.Window {
add(main_layout);
}
private void on_folder_selected(Geary.Folder? folder) {
if (folder == null) {
debug("no folder selected");
message_list_store.clear();
return;
}
debug("Folder %s selected", folder.to_string());
do_select_folder.begin(folder, on_select_folder_completed);
}
private async void do_select_folder(Geary.Folder folder) throws Error {
cancel_folder();
message_list_store.clear();
if (current_folder != null) {
yield current_folder.close_async();
}
current_folder = folder;
yield current_folder.open_async(true, cancellable_folder);
current_conversations = new Geary.Conversations(current_folder,
MessageListStore.REQUIRED_FIELDS);
current_conversations.monitor_new_messages(cancellable_folder);
current_conversations.scan_started.connect(on_scan_started);
current_conversations.scan_error.connect(on_scan_error);
current_conversations.scan_completed.connect(on_scan_completed);
current_conversations.conversations_added.connect(on_conversations_added);
current_conversations.conversation_appended.connect(on_conversation_appended);
current_conversations.conversation_trimmed.connect(on_conversation_trimmed);
current_conversations.conversation_removed.connect(on_conversation_removed);
current_conversations.updated_placeholders.connect(on_updated_placeholders);
// Do a quick-list of the messages (which should return what's in the local store) if
// supported by the Folder, followed by a complete list if needed
second_list_pass_required =
current_folder.get_supported_list_flags().is_all_set(Geary.Folder.ListFlags.FAST);
// Load all conversations from the DB.
current_conversations.lazy_load(-1, -1, Geary.Folder.ListFlags.FAST, cancellable_folder);
}
public void on_scan_started(Geary.EmailIdentifier? id, int low, int count) {
debug("on scan started. id = %s low = %d count = %d", id != null ? id.to_string() : "(null)",
low, count);
message_list_view.enable_load_more = false;
}
public void on_scan_error(Error err) {
debug("Scan error: %s", err.message);
}
public void on_scan_completed() {
debug("on scan completed");
do_fetch_previews.begin(cancellable_folder);
message_list_view.enable_load_more = true;
}
public void on_conversations_added(Gee.Collection<Geary.Conversation> conversations) {
debug("on conversation added");
foreach (Geary.Conversation c in conversations) {
if (!message_list_store.has_conversation(c))
message_list_store.append_conversation(c);
}
}
public void on_conversation_appended(Geary.Conversation conversation,
Gee.Collection<Geary.Email> email) {
message_list_store.update_conversation(conversation);
}
public void on_conversation_trimmed(Geary.Conversation conversation, Geary.Email email) {
message_list_store.update_conversation(conversation);
}
public void on_conversation_removed(Geary.Conversation conversation) {
message_list_store.remove_conversation(conversation);
}
public void on_updated_placeholders(Geary.Conversation conversation,
Gee.Collection<Geary.Email> email) {
message_list_store.update_conversation(conversation);
}
private void on_load_more() {
debug("on_load_more");
message_list_view.enable_load_more = false;
Geary.EmailIdentifier? low_id = message_list_store.get_email_id_lowest();
if (low_id == null)
return;
current_conversations.load_by_id_async.begin(low_id, - FETCH_EMAIL_CHUNK_COUNT,
Geary.Folder.ListFlags.EXCLUDING_ID, cancellable_folder, on_load_more_completed);
}
private void on_load_more_completed(Object? source, AsyncResult result) {
debug("on load more completed");
try {
current_conversations.load_by_id_async.end(result);
} catch (Error err) {
debug("Error, unable to load conversations: %s", err.message);
}
}
private async void do_fetch_previews(Cancellable? cancellable) throws Error {
Geary.NonblockingBatch batch = new Geary.NonblockingBatch();
int count = message_list_store.get_count();
for (int ctr = 0; ctr < count; ctr++) {
Geary.Conversation? conversation;
Geary.Email? email = message_list_store.get_newest_message_at_index(ctr, out conversation);
if (email != null)
batch.add(new FetchPreviewOperation(this, current_folder, email.id, conversation));
}
debug("Fetching %d previews", count);
yield batch.execute_all_async(cancellable);
debug("Completed fetching %d previews", count);
batch.throw_first_exception();
// with all the previews fetched, now go back and do a full list (if required)
if (second_list_pass_required) {
second_list_pass_required = false;
debug("Doing second list pass now");
current_conversations.lazy_load(-1, FETCH_EMAIL_CHUNK_COUNT, Geary.Folder.ListFlags.NONE,
cancellable);
}
}
private void on_select_folder_completed(Object? source, AsyncResult result) {
try {
do_select_folder.end(result);
} catch (Error err) {
debug("Unable to select folder: %s", err.message);
}
}
private void on_conversation_selected(Geary.Conversation? conversation) {
cancel_message();
message_viewer.clear();
if (conversation != null)
do_select_message.begin(conversation, cancellable_message, on_select_message_completed);
}
private async void do_select_message(Geary.Conversation conversation, Cancellable?
cancellable = null) throws Error {
if (current_folder == null) {
debug("Conversation selected with no folder selected");
return;
}
foreach (Geary.Email email in conversation.get_pool_sorted(compare_email)) {
Geary.Email full_email = yield current_folder.fetch_email_async(email.id,
MessageViewer.REQUIRED_FIELDS, cancellable);
if (cancellable.is_cancelled())
break;
message_viewer.add_message(full_email);
}
}
private void on_select_message_completed(Object? source, AsyncResult result) {
try {
do_select_message.end(result);
} catch (Error err) {
if (!(err is IOError.CANCELLED))
debug("Unable to select message: %s", err.message);
}
}
private void on_folders_added_removed(Gee.Collection<Geary.Folder>? added,
Gee.Collection<Geary.Folder>? removed) {
if (added != null && added.size > 0) {
Gee.Set<Geary.FolderPath>? ignored_paths = account.get_ignored_paths();
Gee.ArrayList<Geary.Folder> skipped = new Gee.ArrayList<Geary.Folder>();
foreach (Geary.Folder folder in added) {
if (ignored_paths != null && ignored_paths.contains(folder.get_path()))
skipped.add(folder);
else
folder_list_store.add_user_folder(folder);
}
Gee.Collection<Geary.Folder> remaining = added;
if (skipped.size > 0) {
remaining = new Gee.ArrayList<Geary.Folder>();
remaining.add_all(added);
remaining.remove_all(skipped);
}
search_folders_for_children.begin(remaining);
}
}
private async void search_folders_for_children(Gee.Collection<Geary.Folder> folders) {
Geary.NonblockingBatch batch = new Geary.NonblockingBatch();
foreach (Geary.Folder folder in folders)
batch.add(new ListFoldersOperation(account, folder.get_path()));
debug("Listing folder children");
try {
yield batch.execute_all_async();
} catch (Error err) {
debug("Unable to execute batch: %s", err.message);
return;
}
debug("Completed listing folder children");
Gee.ArrayList<Geary.Folder> accumulator = new Gee.ArrayList<Geary.Folder>();
foreach (int id in batch.get_ids()) {
ListFoldersOperation op = (ListFoldersOperation) batch.get_operation(id);
try {
Gee.Collection<Geary.Folder> children = (Gee.Collection<Geary.Folder>) batch.get_result(id);
accumulator.add_all(children);
} catch (Error err2) {
debug("Unable to list children of %s: %s", op.path.to_string(), err2.message);
}
}
if (accumulator.size > 0)
on_folders_added_removed(accumulator, null);
}
public void debug_print_selected() {
if (message_viewer.messages.size == 0) {
debug("Nothing to print");
return;
}
debug("---------------------------");
foreach (Geary.Email e in message_viewer.messages) {
debug("Message: %s", e.id.to_string());
if (e.header != null)
debug("\n%s", e.header.buffer.to_utf8());
else
debug("No message data.");
debug("---------------------------");
}
}
private void cancel_folder() {
Cancellable old_cancellable = cancellable_folder;
cancellable_folder = new Cancellable();
cancel_message();
old_cancellable.cancel();
}
private void cancel_message() {
Cancellable old_cancellable = cancellable_message;
cancellable_message = new Cancellable();
old_cancellable.cancel();
}
}

View file

@ -6,6 +6,7 @@
client_src = [
'geary-application.vala',
'geary-config.vala',
'geary-controller.vala',
'main.vala',
'ui/background-box.vala',