[geary/mjog/email-templates: 59/72] Application.Controller, Composer.Widget: Clean up composer construction
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/email-templates: 59/72] Application.Controller, Composer.Widget: Clean up composer construction
- Date: Mon, 20 Apr 2020 13:55:08 +0000 (UTC)
commit 53de3b86b848d9d514f29569ecdc5f07af649cc8
Author: Michael Gratton <mike vee net>
Date: Sun Apr 19 15:51:03 2020 +1000
Application.Controller, Composer.Widget: Clean up composer construction
Clean up and simplify how composers are constructed. Ensure all
composers are constructed via Application.Composer. Provide a set of
APIs for constructing different kinds of composers with minimal
parameters, rather than having one method with eleventy different
parameters.
Mirror API changes in Composer.Widget by splitting `load` method up
into a method for each different composer type. Clean up internals a
bit as a result. Rename `ComposeType` enum and its values to
`ContextType` to better reflect what it does.
src/client/application/application-client.vala | 16 +-
src/client/application/application-controller.vala | 412 +++++++++------------
.../application/application-main-window.vala | 90 +----
.../application/application-plugin-manager.vala | 7 +-
src/client/composer/composer-embed.vala | 2 +-
src/client/composer/composer-web-view.vala | 6 +-
src/client/composer/composer-widget.vala | 355 ++++++++++--------
.../conversation-contact-popover.vala | 2 +-
.../conversation-viewer/conversation-list-box.vala | 4 +-
.../conversation-viewer/conversation-viewer.vala | 2 +-
10 files changed, 415 insertions(+), 481 deletions(-)
---
diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala
index 7a784c90..a5dc995b 100644
--- a/src/client/application/application-client.vala
+++ b/src/client/application/application-client.vala
@@ -513,7 +513,7 @@ public class Application.Client : Gtk.Application {
mailto.substring(B0RKED_GLIB_MAILTO_PREFIX.length)
);
}
- this.new_composer.begin(mailto);
+ this.new_composer_mailto.begin(mailto);
}
}
}
@@ -725,10 +725,14 @@ public class Application.Client : Gtk.Application {
prefs.show();
}
- public async void new_composer(string? mailto) {
+ public async void new_composer(Geary.RFC822.MailboxAddress? to = null) {
yield this.present();
+ yield this.controller.compose_new_email(to);
+ }
- this.controller.compose(mailto);
+ public async void new_composer_mailto(string? mailto) {
+ yield this.present();
+ yield this.controller.compose_mailto(mailto);
}
public async void new_window(Geary.Folder? select_folder,
@@ -824,7 +828,7 @@ public class Application.Client : Gtk.Application {
yield create_controller();
if (uri.down().has_prefix(MAILTO_URI_SCHEME_PREFIX)) {
- yield this.new_composer(uri);
+ yield this.new_composer_mailto(uri);
} else {
string uri_ = uri;
// Support web URLs that omit the protocol.
@@ -1146,7 +1150,7 @@ public class Application.Client : Gtk.Application {
}
private void on_activate_compose() {
- this.new_composer.begin(null);
+ this.new_composer.begin();
}
private void on_activate_inspect() {
@@ -1155,7 +1159,7 @@ public class Application.Client : Gtk.Application {
private void on_activate_mailto(SimpleAction action, Variant? param) {
if (param != null) {
- this.new_composer.begin(param.get_string());
+ this.new_composer_mailto.begin(param.get_string());
}
}
diff --git a/src/client/application/application-controller.vala
b/src/client/application/application-controller.vala
index 6f534d47..530dec32 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -212,16 +212,6 @@ internal class Application.Controller : Geary.BaseObject {
this.expunge_accounts.begin();
}
- /** Returns a context for an account, if any. */
- internal AccountContext? get_context_for_account(Geary.AccountInformation account) {
- return this.accounts.get(account);
- }
-
- /** Returns a read-only collection of contexts each active account. */
- internal Gee.Collection<AccountContext> get_account_contexts() {
- return this.accounts.values.read_only_view;
- }
-
/** Closes all windows and accounts, releasing held resources. */
public async void close() {
// Stop listening for account changes up front so we don't
@@ -333,20 +323,48 @@ internal class Application.Controller : Geary.BaseObject {
}
/**
- * Opens or queues a new composer addressed to a specific email address.
+ * Opens a composer for writing a new, blank message.
*/
- public void compose(string? mailto = null) {
+ public async void compose_new_email(Geary.RFC822.MailboxAddress? to = null) {
+ // If there's already an empty composer open, just use that
+ foreach (Composer.Widget existing in this.composer_widgets) {
+ if (existing != null &&
+ existing.current_mode == PANED &&
+ existing.is_blank) {
+ existing.present();
+ }
+ }
+
+ var composer = new Composer.Widget(
+ this.application,
+ this.application.get_active_main_window().selected_account
+ );
+ register_composer(composer);
+ show_composer(composer, null);
+ try {
+ yield composer.load_empty_body(to);
+ } catch (GLib.Error err) {
+ report_problem(new Geary.ProblemReport(err));
+ }
+ }
+
+ /**
+ * Opens a composer with the given `mailto:` URL.
+ */
+ public async void compose_mailto(string mailto) {
MainWindow? window = this.application.last_active_main_window;
if (window != null && window.selected_account != null) {
- create_compose_widget(
- window,
- window.selected_account,
- NEW_MESSAGE,
- mailto,
- null,
- null,
- false
+ var composer = new Composer.Widget(
+ this.application, window.selected_account
);
+ register_composer(composer);
+ show_composer(composer, null);
+
+ try {
+ yield composer.load_mailto(mailto);
+ } catch (GLib.Error err) {
+ report_problem(new Geary.ProblemReport(err));
+ }
} else {
// Schedule the send for after we have an account open.
this.pending_mailtos.add(mailto);
@@ -355,90 +373,67 @@ internal class Application.Controller : Geary.BaseObject {
/**
* Opens new composer with an existing message as context.
+ *
+ * If the given type is {@link Composer.Widget.ContextType.EDIT},
+ * the context is loaded to be edited (e.g. for drafts, templates,
+ * sending again. Otherwise the context is treated as the email to
+ * be replied to, etc.
*/
- public void compose_with_context_email(MainWindow to_show,
- Geary.Account account,
- Composer.Widget.ComposeType type,
- Geary.Email context,
- string? quote,
- bool is_draft) {
- create_compose_widget(
- to_show, account, type, null, context, quote, is_draft
- );
- }
-
- /** Adds a new composer to be kept track of. */
- public void add_composer(Composer.Widget widget) {
- debug(@"Added composer of type $(widget.compose_type); $(this.composer_widgets.size) composers
total");
- widget.destroy.connect_after(this.on_composer_widget_destroy);
- this.composer_widgets.add(widget);
- }
-
- /** Returns a read-only collection of currently open composers .*/
- public Gee.Collection<Composer.Widget> get_composers() {
- return this.composer_widgets.read_only_view;
- }
-
- /** Opens any pending composers. */
- public void process_pending_composers() {
- foreach (string? mailto in this.pending_mailtos) {
- compose(mailto);
- }
- this.pending_mailtos.clear();
- }
-
- /** Queues the email in a composer for delivery. */
- public async void send_composed_email(Composer.Widget composer) {
- AccountContext? context = this.accounts.get(
- composer.account.information
- );
- if (context != null) {
- try {
- yield context.commands.execute(
- new SendComposerCommand(this.application, context, composer),
- context.cancellable
- );
- } catch (GLib.Error err) {
- report_problem(new Geary.ProblemReport(err));
+ public async void compose_with_context_email(Composer.Widget.ContextType type,
+ Geary.Email context,
+ string? quote) {
+ MainWindow show_on = this.application.get_active_main_window();
+ if (type == EDIT) {
+ // Check all known composers since the context may be open
+ // an existing composer already.
+ foreach (Composer.Widget composer in this.composer_widgets) {
+ if (composer.saved_id != null &&
+ composer.saved_id.equal_to(context.id)) {
+ composer.present();
+ composer.set_focus();
+ return;
+ }
+ }
+ } else {
+ // See whether there is already an inline message in the
+ // current window that is either a reply/forward for that
+ // message, or there is a quote to insert into it.
+ foreach (Composer.Widget existing in this.composer_widgets) {
+ if (existing.get_toplevel() == show_on &&
+ (existing.current_mode == INLINE ||
+ existing.current_mode == INLINE_COMPACT) &&
+ (context.id in existing.get_referred_ids() ||
+ quote != null)) {
+ try {
+ existing.append_to_email(context, quote, type);
+ existing.present();
+ return;
+ } catch (Geary.EngineError error) {
+ report_problem(new Geary.ProblemReport(error));
+ }
+ }
}
- }
- }
- /** Saves the email in a composer as a draft on the server. */
- public async void save_composed_email(Composer.Widget composer) {
- // XXX this doesn't actually do what it says on the tin, since
- // the composer's draft manager is already saving drafts on
- // the server. Until we get that saving local-only, this will
- // only be around for pushing the composer onto the undo stack
- AccountContext? context = this.accounts.get(
- composer.account.information
- );
- if (context != null) {
- try {
- yield context.commands.execute(
- new SaveComposerCommand(this, composer),
- context.cancellable
- );
- } catch (GLib.Error err) {
- report_problem(new Geary.ProblemReport(err));
+ // Can't re-use an existing composer, so need to create a
+ // new one. Replies must open inline in the main window,
+ // so we need to ensure there are no composers open there
+ // first.
+ if (!show_on.close_composer(true)) {
+ return;
}
}
- }
- /** Queues a composer to be discarded. */
- public async void discard_composed_email(Composer.Widget composer) {
- AccountContext? context = this.accounts.get(
- composer.account.information
+ var composer = new Composer.Widget(
+ this.application,
+ this.application.get_active_main_window().selected_account
);
- if (context != null) {
- try {
- yield context.commands.execute(
- new DiscardComposerCommand(this, composer),
- context.cancellable
- );
- } catch (GLib.Error err) {
- report_problem(new Geary.ProblemReport(err));
- }
+ register_composer(composer);
+ show_composer(composer, Geary.Collection.single(context.id));
+
+ try {
+ yield composer.load_context(type, context, quote);
+ } catch (GLib.Error err) {
+ report_problem(new Geary.ProblemReport(err));
}
}
@@ -858,6 +853,16 @@ internal class Application.Controller : Geary.BaseObject {
}
}
+ /** Returns a context for an account, if any. */
+ internal AccountContext? get_context_for_account(Geary.AccountInformation account) {
+ return this.accounts.get(account);
+ }
+
+ /** Returns a read-only collection of contexts each active account. */
+ internal Gee.Collection<AccountContext> get_account_contexts() {
+ return this.accounts.values.read_only_view;
+ }
+
internal void register_window(MainWindow window) {
window.retry_service_problem.connect(on_retry_service_problem);
}
@@ -866,6 +871,69 @@ internal class Application.Controller : Geary.BaseObject {
window.retry_service_problem.disconnect(on_retry_service_problem);
}
+ /** Opens any pending composers. */
+ internal async void process_pending_composers() {
+ foreach (string? mailto in this.pending_mailtos) {
+ yield compose_mailto(mailto);
+ }
+ this.pending_mailtos.clear();
+ }
+
+ /** Queues the email in a composer for delivery. */
+ internal async void send_composed_email(Composer.Widget composer) {
+ AccountContext? context = this.accounts.get(
+ composer.account.information
+ );
+ if (context != null) {
+ try {
+ yield context.commands.execute(
+ new SendComposerCommand(this.application, context, composer),
+ context.cancellable
+ );
+ } catch (GLib.Error err) {
+ report_problem(new Geary.ProblemReport(err));
+ }
+ }
+ }
+
+ /** Saves the email in a composer as a draft on the server. */
+ internal async void save_composed_email(Composer.Widget composer) {
+ // XXX this doesn't actually do what it says on the tin, since
+ // the composer's draft manager is already saving drafts on
+ // the server. Until we get that saving local-only, this will
+ // only be around for pushing the composer onto the undo stack
+ AccountContext? context = this.accounts.get(
+ composer.account.information
+ );
+ if (context != null) {
+ try {
+ yield context.commands.execute(
+ new SaveComposerCommand(this, composer),
+ context.cancellable
+ );
+ } catch (GLib.Error err) {
+ report_problem(new Geary.ProblemReport(err));
+ }
+ }
+ }
+
+ /** Queues a composer to be discarded. */
+ internal async void discard_composed_email(Composer.Widget composer) {
+ AccountContext? context = this.accounts.get(
+ composer.account.information
+ );
+ if (context != null) {
+ try {
+ yield context.commands.execute(
+ new DiscardComposerCommand(this, composer),
+ context.cancellable
+ );
+ } catch (GLib.Error err) {
+ report_problem(new Geary.ProblemReport(err));
+ }
+ }
+ }
+
/** Expunges removed accounts while the controller remains open. */
internal async void expunge_accounts() {
try {
@@ -1330,13 +1398,8 @@ internal class Application.Controller : Geary.BaseObject {
/** Displays a composer on the last active main window. */
internal void show_composer(Composer.Widget composer,
- Gee.Collection<Geary.EmailIdentifier>? refers_to,
- MainWindow? show_on) {
- var target = show_on;
- if (target == null) {
- target = this.application.get_active_main_window();
- }
-
+ Gee.Collection<Geary.EmailIdentifier>? refers_to) {
+ var target = this.application.get_active_main_window();
target.show_composer(composer, refers_to);
composer.set_focus();
}
@@ -1352,135 +1415,17 @@ internal class Application.Controller : Geary.BaseObject {
return do_quit;
}
- /**
- * Creates a composer widget.
- *
- * Depending on the arguments, this can be inline in the
- * conversation or as a new window.
- *
- * @param compose_type - Whether it's a new message, a reply, a
- * forwarded mail, ...
- * @param referred - The mail of which we should copy the from/to/...
- * addresses
- * @param quote - The quote after the mail body
- * @param mailto - A "mailto:"-link
- * @param is_draft - Whether we're starting from a draft (true) or
- * a new mail (false)
- */
- private void create_compose_widget(MainWindow show_on,
- Geary.Account account,
- Composer.Widget.ComposeType compose_type,
- string? mailto,
- Geary.Email? referred,
- string? quote,
- bool is_draft) {
- // There's a few situations where we can re-use an existing
- // composer, check for these first.
- if (compose_type == NEW_MESSAGE && !is_draft) {
- // We're creating a new message that isn't a draft, if
- // there's already an empty composer open, just use
- // that
- foreach (Composer.Widget existing in this.composer_widgets) {
- if (existing != null &&
- existing.current_mode == PANED &&
- existing.is_blank) {
- existing.present();
- return;
- }
- }
- } else if (compose_type != NEW_MESSAGE && referred != null) {
- // A reply/forward was requested, see whether there is
- // already an inline message in the target window that is
- // either a reply/forward for that message, or there is a
- // quote to insert into it.
- foreach (Composer.Widget existing in this.composer_widgets) {
- if (existing.get_toplevel() == show_on &&
- (existing.current_mode == INLINE ||
- existing.current_mode == INLINE_COMPACT) &&
- (referred.id in existing.get_referred_ids() ||
- quote != null)) {
- try {
- existing.append_to_email(referred, quote, compose_type);
- existing.present();
- return;
- } catch (Geary.EngineError error) {
- report_problem(new Geary.ProblemReport(error));
- }
- }
- }
-
- // Can't re-use an existing composer, so need to create a
- // new one. Replies must open inline in the main window,
- // so we need to ensure there are no composers open there
- // first.
- if (!show_on.close_composer(true)) {
- return;
- }
- }
-
- Composer.Widget widget;
- if (mailto != null) {
- widget = new Composer.Widget.from_mailto(
- this.application, account, mailto
- );
- } else {
- widget = new Composer.Widget(
- this.application, account, compose_type
- );
- }
-
- add_composer(widget);
- show_composer(
- widget,
- referred != null ? Geary.Collection.single(referred.id) : null,
- show_on
- );
-
- this.load_composer.begin(
- account,
- widget,
- referred,
- is_draft,
- quote
- );
- }
-
- private async void load_composer(Geary.Account account,
- Composer.Widget widget,
- Geary.Email? referred = null,
- bool is_draft,
- string? quote = null) {
- Geary.Email? full = null;
- GLib.Cancellable? cancellable = null;
- if (referred != null) {
- AccountContext? context = this.accounts.get(account.information);
- if (context != null) {
- cancellable = context.cancellable;
- try {
- full = yield context.emails.fetch_email_async(
- referred.id,
- Geary.ComposedEmail.REQUIRED_REPLY_FIELDS |
- Composer.Widget.REQUIRED_FIELDS,
- NONE,
- cancellable
- );
- } catch (Error e) {
- message("Could not load full message: %s", e.message);
- }
- }
- }
- try {
- yield widget.load(full, is_draft, quote, cancellable);
- } catch (GLib.Error err) {
- report_problem(new Geary.ProblemReport(err));
- }
+ internal void register_composer(Composer.Widget widget) {
+ debug(@"Registered composer of type $(widget.context_type); $(this.composer_widgets.size) composers
total");
+ widget.destroy.connect_after(this.on_composer_widget_destroy);
+ this.composer_widgets.add(widget);
}
private void on_composer_widget_destroy(Gtk.Widget sender) {
Composer.Widget? composer = sender as Composer.Widget;
if (composer != null) {
composer_widgets.remove((Composer.Widget) sender);
- debug(@"Composer type $(composer.compose_type) destroyed; " +
+ debug(@"Composer type $(composer.context_type) destroyed; " +
@"$(this.composer_widgets.size) composers remaining");
}
}
@@ -2485,7 +2430,6 @@ private class Application.SendComposerCommand : ComposerCommand {
public override async void execute(GLib.Cancellable? cancellable)
throws GLib.Error {
Geary.ComposedEmail email = yield this.composer.get_composed_email();
-
if (this.can_undo) {
/// Translators: The label for an in-app notification. The
/// string substitution is a list of recipients of the email.
@@ -2510,7 +2454,9 @@ private class Application.SendComposerCommand : ComposerCommand {
this.saved = null;
this.composer.set_enabled(true);
- this.application.controller.show_composer(this.composer, null, null);
+ this.application.controller.show_composer(
+ this.composer, this.composer.get_referred_ids()
+ );
clear_composer();
}
@@ -2564,7 +2510,9 @@ private class Application.SaveComposerCommand : ComposerCommand {
if (this.composer != null) {
this.destroy_timer.reset();
this.composer.set_enabled(true);
- this.controller.show_composer(this.composer, null, null);
+ this.controller.show_composer(
+ this.composer, this.composer.get_referred_ids()
+ );
clear_composer();
} else {
/// Translators: A label for an in-app notification.
@@ -2622,7 +2570,9 @@ private class Application.DiscardComposerCommand : ComposerCommand {
if (this.composer != null) {
this.destroy_timer.reset();
this.composer.set_enabled(true);
- this.controller.show_composer(this.composer, null, null);
+ this.controller.show_composer(
+ this.composer, this.composer.get_referred_ids()
+ );
clear_composer();
} else {
/// Translators: A label for an in-app notification.
diff --git a/src/client/application/application-main-window.vala
b/src/client/application/application-main-window.vala
index 9fe6ab19..1a3fc269 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -766,7 +766,7 @@ public class Application.MainWindow :
);
yield open_conversation_monitor(this.conversations, cancellable);
- this.controller.process_pending_composers();
+ yield this.controller.process_pending_composers();
}
}
@@ -864,16 +864,6 @@ public class Application.MainWindow :
}
}
- /** Displays a composer addressed to a specific email address. */
- public void open_composer_for_mailbox(Geary.RFC822.MailboxAddress to) {
- var composer = new Composer.Widget.from_mailbox(
- this.application, this.selected_folder.account, to
- );
- this.controller.add_composer(composer);
- show_composer(composer, null);
- composer.load.begin(null, false, null, null);
- }
-
/**
* Displays a composer in the window if possible, else in a new window.
*
@@ -883,8 +873,8 @@ public class Application.MainWindow :
* the composer's {@link Composer.Widget.get_referred_ids} will be
* used.
*/
- public void show_composer(Composer.Widget composer,
- Gee.Collection<Geary.EmailIdentifier>? refers_to) {
+ internal void show_composer(Composer.Widget composer,
+ Gee.Collection<Geary.EmailIdentifier>? refers_to) {
if (this.has_composer) {
composer.detach();
} else {
@@ -923,7 +913,7 @@ public class Application.MainWindow :
* Returns true if none were open or the user approved closing
* them.
*/
- public bool close_composer(bool should_prompt, bool is_shutdown = false) {
+ internal bool close_composer(bool should_prompt, bool is_shutdown = false) {
bool closed = true;
Composer.Widget? composer = this.conversation_viewer.current_composer;
if (composer != null &&
@@ -1547,7 +1537,7 @@ public class Application.MainWindow :
);
}
- private void create_composer_from_viewer(Composer.Widget.ComposeType compose_type) {
+ private void create_composer_from_viewer(Composer.Widget.ContextType type) {
Geary.Account? account = this.selected_account;
ConversationEmail? email_view = null;
ConversationListBox? list_view = this.conversation_viewer.current_list;
@@ -1557,13 +1547,8 @@ public class Application.MainWindow :
if (account != null && email_view != null) {
email_view.get_selection_for_quoting.begin((obj, res) => {
string? quote = email_view.get_selection_for_quoting.end(res);
- this.controller.compose_with_context_email(
- this,
- account,
- compose_type,
- email_view.email,
- quote,
- false
+ this.controller.compose_with_context_email.begin(
+ type, email_view.email, quote ?? ""
);
});
}
@@ -2043,7 +2028,6 @@ public class Application.MainWindow :
list.reply_to_all_email.connect(on_email_reply_to_all);
list.reply_to_sender_email.connect(on_email_reply_to_sender);
list.forward_email.connect(on_email_forward);
- list.edit_email.connect(on_email_edit);
list.trash_email.connect(on_email_trash);
list.delete_email.connect(on_email_delete);
}
@@ -2102,31 +2086,9 @@ public class Application.MainWindow :
// TODO: Determine how to map between conversations
// and drafts correctly.
Geary.Email draft = activated.get_latest_recv_email(IN_FOLDER);
-
- // Check all known composers since the draft may be
- // open in a detached composer
- bool already_open = false;
- foreach (Composer.Widget composer
- in this.controller.get_composers()) {
- if (composer.current_draft_id != null &&
- composer.current_draft_id.equal_to(draft.id)) {
- already_open = true;
- composer.present();
- composer.set_focus();
- break;
- }
- }
-
- if (!already_open) {
- this.controller.compose_with_context_email(
- this,
- activated.base_folder.account,
- NEW_MESSAGE,
- draft,
- null,
- true
- );
- }
+ this.controller.compose_with_context_email.begin(
+ EDIT, draft, null
+ );
}
}
}
@@ -2153,7 +2115,7 @@ public class Application.MainWindow :
}
private void on_reply_conversation() {
- create_composer_from_viewer(REPLY);
+ create_composer_from_viewer(REPLY_SENDER);
}
private void on_reply_all_conversation() {
@@ -2476,37 +2438,25 @@ public class Application.MainWindow :
}
private void on_email_reply_to_sender(Geary.Email target, string? quote) {
- Geary.Account? account = this.selected_account;
- if (account != null) {
- this.controller.compose_with_context_email(
- this, account, REPLY, target, quote, false
+ if (this.selected_account != null) {
+ this.controller.compose_with_context_email.begin(
+ REPLY_SENDER, target, quote
);
}
}
private void on_email_reply_to_all(Geary.Email target, string? quote) {
- Geary.Account? account = this.selected_account;
- if (account != null) {
- this.controller.compose_with_context_email(
- this, account, REPLY_ALL, target, quote, false
+ if (this.selected_account != null) {
+ this.controller.compose_with_context_email.begin(
+ REPLY_ALL, target, quote
);
}
}
private void on_email_forward(Geary.Email target, string? quote) {
- Geary.Account? account = this.selected_account;
- if (account != null) {
- this.controller.compose_with_context_email(
- this, account, FORWARD, target, quote, false
- );
- }
- }
-
- private void on_email_edit(Geary.Email target) {
- Geary.Account? account = this.selected_account;
- if (account != null) {
- this.controller.compose_with_context_email(
- this, account, NEW_MESSAGE, target, null, true
+ if (this.selected_account != null) {
+ this.controller.compose_with_context_email.begin(
+ FORWARD, target, quote
);
}
}
diff --git a/src/client/application/application-plugin-manager.vala
b/src/client/application/application-plugin-manager.vala
index 378e65f4..b36e9e1a 100644
--- a/src/client/application/application-plugin-manager.vala
+++ b/src/client/application/application-plugin-manager.vala
@@ -183,12 +183,7 @@ public class Application.PluginManager : GLib.Object {
}
public void show() {
- var composer = new Composer.Widget(
- this.application, this.account.account, NEW_MESSAGE
- );
- var main_window = this.application.get_active_main_window();
- main_window.show_composer(composer, null);
- composer.load.begin(null, false, null, null);
+ this.application.controller.compose_new_email.begin();
}
}
diff --git a/src/client/composer/composer-embed.vala b/src/client/composer/composer-embed.vala
index d92aca6d..26f6a336 100644
--- a/src/client/composer/composer-embed.vala
+++ b/src/client/composer/composer-embed.vala
@@ -43,7 +43,7 @@ public class Composer.Embed : Gtk.EventBox, Container {
this.composer.embed_header();
Widget.PresentationMode mode = INLINE_COMPACT;
- if (composer.compose_type == FORWARD ||
+ if (composer.context_type == FORWARD ||
composer.has_multiple_from_addresses) {
mode = INLINE;
}
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index 9c78ec75..d187759c 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -148,7 +148,7 @@ public class Composer.WebView : ClientWebView {
public new void load_html(string body,
string quote,
bool top_posting,
- bool is_draft) {
+ bool body_complete) {
const string HTML_PRE = """<html><body class="%s">""";
const string HTML_POST = """</body></html>""";
const string BODY_PRE = """
@@ -165,7 +165,7 @@ public class Composer.WebView : ClientWebView {
StringBuilder html = new StringBuilder();
string body_class = (this.is_rich_text) ? "" : "plain";
html.append(HTML_PRE.printf(body_class));
- if (!is_draft) {
+ if (!body_complete) {
html.append(BODY_PRE);
bool have_body = !Geary.String.is_empty(body);
if (have_body) {
@@ -185,7 +185,7 @@ public class Composer.WebView : ClientWebView {
html.append_printf(QUOTE, quote);
}
} else {
- html.append(quote);
+ html.append(body);
}
html.append(HTML_POST);
base.load_html((string) html.data);
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 6cceec72..8b62eb92 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2017-2019 Michael Gratton <mike vee net>
+ * Copyright © 2016 Software Freedom Conservancy Inc.
+ * Copyright © 2017-2020 Michael Gratton <mike vee net>
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
@@ -25,15 +25,31 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
/** The email fields the composer requires for referred email. */
- public const Geary.Email.Field REQUIRED_FIELDS = ENVELOPE | BODY;
+ public const Geary.Email.Field REQUIRED_FIELDS = ENVELOPE | HEADER | BODY;
/// Translators: Title for an empty composer window
private const string DEFAULT_TITLE = _("New Message");
+
+ /**
+ * Determines the type of the context email passed to the composer
+ *
+ * @see context_type
+ * @see load_context
+ */
+ public enum ContextType {
+ /** No context mail was provided. */
+ NONE,
- public enum ComposeType {
- NEW_MESSAGE,
- REPLY,
+ /** Context is an email to edited, for example a draft or template. */
+ EDIT,
+
+ /** Context is an email being replied to the sender only. */
+ REPLY_SENDER,
+
+ /** Context is an email being replied to all recipients. */
REPLY_ALL,
+
+ /** Context is an email being forwarded. */
FORWARD
}
@@ -242,17 +258,17 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
/** The account the email is being sent from. */
public Geary.Account account { get; private set; }
- /** The identifier of the draft this composer holds, if any. */
- public Geary.EmailIdentifier? current_draft_id {
+ /** The identifier of the saved email this composer holds, if any. */
+ public Geary.EmailIdentifier? saved_id {
get; private set; default = null;
}
+ /** Determines the type of the context email. */
+ public ContextType context_type { get; private set; default = NONE; }
+
/** Determines the composer's current presentation mode. */
public PresentationMode current_mode { get; set; default = NONE; }
- /** Determines the type of email being composed. */
- public ComposeType compose_type { get; private set; default = ComposeType.NEW_MESSAGE; }
-
/** Determines if the composer is completely empty. */
public bool is_blank {
get {
@@ -431,17 +447,12 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
private Gee.Collection<Geary.Account> accounts;
- private string body_html = "";
-
private string? pointer_url = null;
private string? cursor_url = null;
private bool is_attachment_overlay_visible = false;
private Geary.RFC822.MailboxAddresses reply_to_addresses;
private Geary.RFC822.MailboxAddresses reply_cc_addresses;
- private string reply_subject = "";
- private string forward_subject = "";
private bool top_posting = true;
- private string? last_quote = null;
// The message(s) this email is in reply to/forwarded from
private Gee.Set<Geary.EmailIdentifier> referred_ids =
@@ -491,8 +502,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
public Widget(Application.Client application,
- Geary.Account initial_account,
- ComposeType compose_type) {
+ Geary.Account initial_account) {
components_reflow_box_get_type();
base_ref();
this.application = application;
@@ -504,8 +514,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
warning("Could not fetch account info: %s", e.message);
}
- this.compose_type = compose_type;
-
this.header = new Headerbar(application.config);
this.header.expand_composer.connect(on_expand_compact_headers);
@@ -642,18 +650,20 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
update_color_icon.begin(Util.Gtk.rgba(0, 0, 0, 0));
}
- public Widget.from_mailbox(Application.Client application,
- Geary.Account initial_account,
- Geary.RFC822.MailboxAddress to) {
- this(application, initial_account, ComposeType.NEW_MESSAGE);
- this.to = to.to_full_display();
+ ~Widget() {
+ base_unref();
}
- public Widget.from_mailto(Application.Client application,
- Geary.Account initial_account,
- string mailto) {
- this(application, initial_account, ComposeType.NEW_MESSAGE);
+ public async void load_empty_body(Geary.RFC822.MailboxAddress? to = null)
+ throws GLib.Error {
+ if (to != null) {
+ this.to = to.to_full_display();
+ }
+ yield finish_loading("", "", false, null);
+ }
+ public async void load_mailto(string mailto)
+ throws GLib.Error {
Gee.HashMultiMap<string, string> headers = new Gee.HashMultiMap<string, string>();
if (mailto.has_prefix(MAILTO_URI_PREFIX)) {
// Parse the mailto link.
@@ -687,9 +697,14 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
if (headers.contains("subject"))
this.subject = Geary.Collection.first(headers.get("subject"));
- if (headers.contains("body"))
- this.body_html = Geary.HTML.preserve_whitespace(Geary.HTML.escape_markup(
- Geary.Collection.first(headers.get("body"))));
+ var body = "";
+ if (headers.contains("body")) {
+ body = Geary.HTML.preserve_whitespace(
+ Geary.HTML.escape_markup(
+ Geary.Collection.first(headers.get("body"))
+ )
+ );
+ }
Gee.List<string> attachments = new Gee.LinkedList<string>();
attachments.add_all(headers.get("attach"));
@@ -701,67 +716,86 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
attachment_failed(err.message);
}
}
+ yield finish_loading(body, "", false, null);
}
}
- ~Widget() {
- base_unref();
- }
-
- /**
- * Returns the emails referred to by the composed email.
- *
- * A referred email is the email this composer is a reply to, or
- * forwarded from. There may be multiple if a composer was already
- * open and another email was replied to.
- */
- public Gee.Set<Geary.EmailIdentifier> get_referred_ids() {
- return this.referred_ids.read_only_view;
- }
-
/**
* Loads the message into the composer editor.
*/
- public async void load(Geary.Email? referred = null,
- bool is_draft,
- string? quote = null,
- GLib.Cancellable? cancellable)
+ public async void load_context(ContextType type,
+ Geary.Email context,
+ string? quote)
throws GLib.Error {
- if (referred != null &&
- !referred.fields.is_all_set(REQUIRED_FIELDS)) {
+ if (type == NONE) {
+ throw new Geary.EngineError.BAD_PARAMETERS(
+ "Invalid context type: %s", type.to_string()
+ );
+ }
+
+ if (!context.fields.is_all_set(REQUIRED_FIELDS)) {
throw new Geary.EngineError.INCOMPLETE_MESSAGE(
- "Required fields not met: %s", referred.fields.to_string()
+ "Required fields not met: %s", context.fields.to_string()
);
}
- string referred_quote = "";
- this.last_quote = quote;
- if (referred != null) {
- referred_quote = fill_in_from_referred(referred, quote);
- if (is_draft ||
- compose_type == ComposeType.NEW_MESSAGE ||
- compose_type == ComposeType.FORWARD) {
- this.pending_include = AttachPending.ALL;
- }
- if (is_draft) {
- yield restore_reply_to_state();
- }
+
+ this.context_type = type;
+
+ if (type == EDIT ||
+ type == FORWARD) {
+ this.pending_include = AttachPending.ALL;
}
- update_attachments_view();
- update_pending_attachments(this.pending_include, true);
+ var body = "";
+ var complete_quote = "";
+ switch (type) {
+ case EDIT:
+ this.saved_id = context.id;
+ yield restore_reply_to_state();
+ fill_in_from_context(context);
+ Geary.RFC822.Message message = context.get_message();
+ body = (
+ message.has_html_body()
+ ? message.get_html_body(null)
+ : message.get_plain_body(true, null)
+ );
+ break;
- this.editor.load_html(
- this.body_html,
- referred_quote,
- this.top_posting,
- is_draft
- );
+ case REPLY_SENDER:
+ case REPLY_ALL:
+ add_recipients_and_ids(this.context_type, context);
+ fill_in_from_context(context);
+ complete_quote = Util.Email.quote_email_for_reply(
+ context, quote, this.application.config.clock_format, HTML
+ );
+ if (!Geary.String.is_empty(quote)) {
+ this.top_posting = false;
+ } else {
+ this.can_delete_quote = true;
+ }
+ break;
- try {
- yield open_draft_manager(is_draft ? referred.id : null, cancellable);
- } catch (Error e) {
- debug("Could not open draft manager: %s", e.message);
+ case FORWARD:
+ add_recipients_and_ids(this.context_type, context);
+ fill_in_from_context(context);
+ complete_quote = Util.Email.quote_email_for_forward(
+ context, quote, HTML
+ );
+ break;
}
+
+ yield finish_loading(body, complete_quote, (type == EDIT), null);
+ }
+
+ /**
+ * Returns the emails referred to by the composed email.
+ *
+ * A referred email is the email this composer is a reply to, or
+ * forwarded from. There may be multiple if a composer was already
+ * open and another email was replied to.
+ */
+ public Gee.Set<Geary.EmailIdentifier> get_referred_ids() {
+ return this.referred_ids.read_only_view;
}
/** Detaches the composer and opens it in a new window. */
@@ -954,7 +988,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
this.header.set_sensitive(enabled);
if (enabled) {
- this.open_draft_manager.begin(this.current_draft_id, null);
+ this.open_draft_manager.begin(this.saved_id, null);
} else {
if (this.container != null) {
this.container.close();
@@ -1006,25 +1040,18 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
if (email == null)
continue;
- // XXX pretty sure we are calling this only to update the
- // composer's internal set of ids - we really shouldn't be
- // messing around with the draft's recipients since the
- // user may have already updated them.
- add_recipients_and_ids(this.compose_type, email, false);
+ add_recipients_and_ids(this.context_type, email, false);
- if (first_email) {
- this.reply_subject = Geary.RFC822.Utils.create_subject_for_reply(email);
- this.forward_subject = Geary.RFC822.Utils.create_subject_for_forward(email);
- first_email = false;
- }
+ first_email = false;
}
if (first_email) // Either no referenced emails, or we don't have them. Treat as new.
return;
- if (this.cc == "")
- this.compose_type = ComposeType.REPLY;
- else
- this.compose_type = ComposeType.REPLY_ALL;
+ if (this.cc == "") {
+ this.context_type = REPLY_SENDER;
+ } else {
+ this.context_type = REPLY_ALL;
+ }
if (!to_entry.addresses.equal_to(reply_to_addresses))
this.to_entry.set_modified();
@@ -1054,18 +1081,12 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
}
- // Copies the addresses (e.g. From/To/CC) and content from referred into this one
- private string fill_in_from_referred(Geary.Email referred, string? quote) {
- string referred_quote = "";
- if (this.compose_type != ComposeType.NEW_MESSAGE) {
- add_recipients_and_ids(this.compose_type, referred);
- this.reply_subject = Geary.RFC822.Utils.create_subject_for_reply(referred);
- this.forward_subject = Geary.RFC822.Utils.create_subject_for_forward(referred);
- }
+ // Copies the addresses (e.g. From/To/CC) and content from
+ // referred into this one
+ private void fill_in_from_context(Geary.Email referred) {
this.pending_attachments = referred.attachments;
- switch (this.compose_type) {
- // Restoring a draft
- case ComposeType.NEW_MESSAGE:
+ switch (this.context_type) {
+ case EDIT:
if (referred.from != null)
this.from = referred.from;
if (referred.to != null)
@@ -1084,41 +1105,26 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
this.references = referred.references.to_rfc822_string();
if (referred.subject != null)
this.subject = referred.subject.value ?? "";
- try {
- Geary.RFC822.Message message = referred.get_message();
- if (message.has_html_body()) {
- referred_quote = message.get_html_body(null);
- } else {
- referred_quote = message.get_plain_body(true, null);
- }
- } catch (Error error) {
- debug("Error getting draft message body: %s", error.message);
- }
break;
- case ComposeType.REPLY:
- case ComposeType.REPLY_ALL:
- this.subject = reply_subject;
- this.references = Geary.RFC822.Utils.reply_references(referred);
- referred_quote = Util.Email.quote_email_for_reply(referred, quote,
- this.application.config.clock_format,
- Geary.RFC822.TextFormat.HTML);
- if (!Geary.String.is_empty(quote)) {
- this.top_posting = false;
- } else {
- this.can_delete_quote = true;
- }
+ case REPLY_SENDER:
+ case REPLY_ALL:
+ this.subject = Geary.RFC822.Utils.create_subject_for_reply(
+ referred
+ );
+ this.references = Geary.RFC822.Utils.reply_references(
+ referred
+ );
break;
- case ComposeType.FORWARD:
- this.subject = forward_subject;
- referred_quote = Util.Email.quote_email_for_forward(referred, quote,
- Geary.RFC822.TextFormat.HTML);
+ case FORWARD:
+ this.subject = Geary.RFC822.Utils.create_subject_for_forward(
+ referred
+ );
break;
}
update_extended_headers();
- return referred_quote;
}
public void present() {
@@ -1319,7 +1325,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
this.subject
);
- if ((this.compose_type == ComposeType.REPLY || this.compose_type == ComposeType.REPLY_ALL) &&
+ if ((this.context_type == REPLY_SENDER ||
+ this.context_type == REPLY_ALL) &&
!this.in_reply_to.is_empty)
email.set_in_reply_to(
new Geary.RFC822.MessageIDList.from_collection(this.in_reply_to)
@@ -1359,7 +1366,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
/** Appends an email or fragment quoted into the composer. */
public void append_to_email(Geary.Email referred,
string? to_quote,
- ComposeType type)
+ ContextType type)
throws Geary.EngineError {
if (!referred.fields.is_all_set(REQUIRED_FIELDS)) {
throw new Geary.EngineError.INCOMPLETE_MESSAGE(
@@ -1371,30 +1378,29 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
add_recipients_and_ids(type, referred);
}
- if (this.last_quote != to_quote) {
- this.last_quote = to_quote;
- // Always use reply styling, since forward styling doesn't
- // work for inline quotes
- this.editor.insert_html(
- Util.Email.quote_email_for_reply(
- referred,
- to_quote,
- this.application.config.clock_format,
- Geary.RFC822.TextFormat.HTML
- )
- );
- }
+ // Always use reply styling, since forward styling doesn't
+ // work for inline quotes
+ this.editor.insert_html(
+ Util.Email.quote_email_for_reply(
+ referred,
+ to_quote,
+ this.application.config.clock_format,
+ Geary.RFC822.TextFormat.HTML
+ )
+ );
}
- private void add_recipients_and_ids(ComposeType type, Geary.Email referred,
- bool modify_headers = true) {
+ private void add_recipients_and_ids(ContextType type,
+ Geary.Email referred,
+ bool modify_headers = true) {
Gee.List<Geary.RFC822.MailboxAddress> sender_addresses =
account.information.sender_mailboxes;
// Set the preferred from address. New messages should retain
// the account default and drafts should retain the draft's
// from addresses, so don't update them here
- if (this.compose_type != ComposeType.NEW_MESSAGE) {
+ if (this.context_type != NONE &&
+ this.context_type != EDIT) {
if (!check_preferred_from_address(sender_addresses, referred.to)) {
if (!check_preferred_from_address(sender_addresses, referred.cc))
if (!check_preferred_from_address(sender_addresses, referred.bcc))
@@ -1417,16 +1423,25 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
bool recipients_modified = this.to_entry.is_modified || this.cc_entry.is_modified ||
this.bcc_entry.is_modified;
if (!recipients_modified) {
- if (type == ComposeType.REPLY || type == ComposeType.REPLY_ALL)
- this.to_entry.addresses = Geary.RFC822.Utils.merge_addresses(to_entry.addresses,
- to_addresses);
- if (type == ComposeType.REPLY_ALL)
+ if (type == REPLY_SENDER || type == REPLY_ALL) {
+ this.to_entry.addresses = Geary.RFC822.Utils.merge_addresses(
+ to_entry.addresses,
+ to_addresses
+ );
+ }
+ if (type == REPLY_ALL) {
this.cc_entry.addresses = Geary.RFC822.Utils.remove_addresses(
- Geary.RFC822.Utils.merge_addresses(this.cc_entry.addresses, cc_addresses),
- this.to_entry.addresses);
- else
- this.cc_entry.addresses = Geary.RFC822.Utils.remove_addresses(this.cc_entry.addresses,
- this.to_entry.addresses);
+ Geary.RFC822.Utils.merge_addresses(
+ this.cc_entry.addresses, cc_addresses
+ ),
+ this.to_entry.addresses
+ );
+ } else {
+ this.cc_entry.addresses = Geary.RFC822.Utils.remove_addresses(
+ this.cc_entry.addresses,
+ this.to_entry.addresses
+ );
+ }
}
if (referred.message_id != null) {
@@ -1493,6 +1508,27 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
}
+ private async void finish_loading(string body,
+ string quote,
+ bool is_body_complete,
+ GLib.Cancellable? cancellable) {
+ update_attachments_view();
+ update_pending_attachments(this.pending_include, true);
+
+ this.editor.load_html(
+ body,
+ quote,
+ this.top_posting,
+ is_body_complete
+ );
+
+ try {
+ yield open_draft_manager(this.saved_id, cancellable);
+ } catch (Error e) {
+ debug("Could not open draft manager: %s", e.message);
+ }
+ }
+
private async bool should_send() {
bool has_subject = !Geary.String.is_empty(subject.strip());
bool has_attachment = this.attached_files.size > 0;
@@ -1627,7 +1663,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
this.draft_manager = null;
this.draft_status_text = "";
- this.current_draft_id = null;
old_manager.notify[Geary.App.DraftManager.PROP_DRAFT_STATE]
.disconnect(on_draft_state_changed);
@@ -2666,7 +2701,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
}
private void on_draft_id_changed() {
- this.current_draft_id = this.draft_manager.current_draft_id;
+ this.saved_id = this.draft_manager.current_draft_id;
}
private void on_draft_manager_fatal(Error err) {
diff --git a/src/client/conversation-viewer/conversation-contact-popover.vala
b/src/client/conversation-viewer/conversation-contact-popover.vala
index 9d58193c..0aa329a9 100644
--- a/src/client/conversation-viewer/conversation-contact-popover.vala
+++ b/src/client/conversation-viewer/conversation-contact-popover.vala
@@ -240,7 +240,7 @@ public class Conversation.ContactPopover : Gtk.Popover {
private void on_new_conversation() {
var main = this.get_toplevel() as Application.MainWindow;
if (main != null) {
- main.open_composer_for_mailbox(this.mailbox);
+ main.application.new_composer.begin(this.mailbox);
}
}
diff --git a/src/client/conversation-viewer/conversation-list-box.vala
b/src/client/conversation-viewer/conversation-list-box.vala
index 81e22d86..e727a3cb 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -891,8 +891,8 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
add(row);
this.current_composer = row;
- embed.composer.notify["current-draft-id"].connect(
- (id) => { this.draft_id = embed.composer.current_draft_id; }
+ embed.composer.notify["saved-id"].connect(
+ (id) => { this.draft_id = embed.composer.saved_id; }
);
embed.vanished.connect(() => {
this.current_composer = null;
diff --git a/src/client/conversation-viewer/conversation-viewer.vala
b/src/client/conversation-viewer/conversation-viewer.vala
index 461c7c94..dbb50c4d 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -187,7 +187,7 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
if (this.current_list != null) {
this.current_list.add_embedded_composer(
embed,
- composer.current_draft_id != null
+ composer.saved_id != null
);
composer.update_window_title();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]