aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--composer/Makefile.am14
-rw-r--r--composer/e-composer-actions.c76
-rw-r--r--composer/e-composer-activity.c156
-rw-r--r--composer/e-composer-activity.h66
-rw-r--r--composer/e-composer-private.c42
-rw-r--r--composer/e-composer-private.h43
-rw-r--r--composer/e-msg-composer.c2036
-rw-r--r--composer/e-msg-composer.h70
-rw-r--r--composer/mail-composer.error.xml33
-rw-r--r--e-util/e-alert-activity.c2
-rw-r--r--e-util/e-alert-dialog.c297
-rw-r--r--e-util/e-alert-dialog.h79
-rw-r--r--e-util/e-alert.c144
-rw-r--r--e-util/e-alert.h16
-rw-r--r--e-util/e-marshal.list1
-rw-r--r--mail/Makefile.am4
-rw-r--r--mail/e-mail-folder-utils.c142
-rw-r--r--mail/e-mail-folder-utils.h43
-rw-r--r--mail/e-mail-reader.c11
-rw-r--r--mail/e-mail-session-utils.c846
-rw-r--r--mail/e-mail-session-utils.h76
-rw-r--r--mail/em-composer-utils.c1010
-rw-r--r--mail/em-composer-utils.h4
-rw-r--r--modules/composer-autosave/e-autosave-utils.c81
-rw-r--r--plugins/email-custom-header/email-custom-header.c2
-rw-r--r--plugins/face/face.c2
-rw-r--r--plugins/templates/templates.c39
-rw-r--r--widgets/misc/Makefile.am4
-rw-r--r--widgets/misc/e-activity-bar.c351
-rw-r--r--widgets/misc/e-activity-bar.h67
-rw-r--r--widgets/misc/e-activity-proxy.c7
-rw-r--r--widgets/misc/e-alert-bar.c235
-rw-r--r--widgets/misc/e-alert-bar.h66
33 files changed, 4423 insertions, 1642 deletions
diff --git a/composer/Makefile.am b/composer/Makefile.am
index 5caea41ee7..5cd2eab05c 100644
--- a/composer/Makefile.am
+++ b/composer/Makefile.am
@@ -9,15 +9,16 @@ privsolib_LTLIBRARIES = libcomposer.la
libcomposerincludedir = $(privincludedir)/composer
libcomposerinclude_HEADERS = \
- e-composer-header.h \
- e-composer-header-table.h \
+ e-composer-actions.h \
+ e-composer-activity.h \
+ e-composer-common.h \
e-composer-from-header.h \
+ e-composer-header-table.h \
+ e-composer-header.h \
e-composer-name-header.h \
e-composer-post-header.h \
e-composer-private.h \
e-composer-text-header.h \
- e-composer-common.h \
- e-composer-actions.h \
e-msg-composer.h
libcomposer_la_CPPFLAGS = \
@@ -42,9 +43,10 @@ libcomposer_la_CPPFLAGS = \
libcomposer_la_SOURCES = \
$(libcomposerinclude_HEADERS) \
e-composer-actions.c \
- e-composer-header.c \
- e-composer-header-table.c \
+ e-composer-activity.c \
e-composer-from-header.c \
+ e-composer-header-table.c \
+ e-composer-header.c \
e-composer-name-header.c \
e-composer-post-header.c \
e-composer-private.c \
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index a49567cea2..13fe48495e 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -130,8 +130,8 @@ action_save_cb (GtkAction *action,
if (response != GTK_RESPONSE_OK)
return;
} else {
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
+ e_alert_submit (
+ GTK_WIDGET (composer),
E_ALERT_NO_SAVE_FILE, filename,
g_strerror (errno_saved), NULL);
return;
@@ -140,8 +140,8 @@ action_save_cb (GtkAction *action,
close (fd);
if (!gtkhtml_editor_save (editor, filename, TRUE, &error)) {
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
+ e_alert_submit (
+ GTK_WIDGET (composer),
E_ALERT_NO_SAVE_FILE,
filename, error->message, NULL);
g_error_free (error);
@@ -252,20 +252,6 @@ static GtkActionEntry entries[] = {
N_("Close the current file"),
G_CALLBACK (action_close_cb) },
- { "print",
- GTK_STOCK_PRINT,
- N_("_Print..."),
- "<Control>p",
- NULL,
- G_CALLBACK (action_print_cb) },
-
- { "print-preview",
- GTK_STOCK_PRINT_PREVIEW,
- N_("Print Pre_view"),
- "<Shift><Control>p",
- NULL,
- G_CALLBACK (action_print_preview_cb) },
-
{ "save",
GTK_STOCK_SAVE,
N_("_Save"),
@@ -280,20 +266,6 @@ static GtkActionEntry entries[] = {
N_("Save the current file with a different name"),
G_CALLBACK (action_save_as_cb) },
- { "save-draft",
- GTK_STOCK_SAVE,
- N_("Save as _Draft"),
- "<Control>s",
- N_("Save as draft"),
- G_CALLBACK (action_save_draft_cb) },
-
- { "send",
- "mail-send",
- N_("S_end"),
- "<Control>Return",
- N_("Send this message"),
- G_CALLBACK (action_send_cb) },
-
{ "new-message",
"mail-message-new",
N_("New _Message"),
@@ -318,6 +290,37 @@ static GtkActionEntry entries[] = {
NULL }
};
+static GtkActionEntry async_entries[] = {
+
+ { "print",
+ GTK_STOCK_PRINT,
+ N_("_Print..."),
+ "<Control>p",
+ NULL,
+ G_CALLBACK (action_print_cb) },
+
+ { "print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ N_("Print Pre_view"),
+ "<Shift><Control>p",
+ NULL,
+ G_CALLBACK (action_print_preview_cb) },
+
+ { "save-draft",
+ GTK_STOCK_SAVE,
+ N_("Save as _Draft"),
+ "<Control>s",
+ N_("Save as draft"),
+ G_CALLBACK (action_save_draft_cb) },
+
+ { "send",
+ "mail-send",
+ N_("S_end"),
+ "<Control>Return",
+ N_("Send this message"),
+ G_CALLBACK (action_send_cb) },
+};
+
static GtkToggleActionEntry toggle_entries[] = {
{ "pgp-encrypt",
@@ -416,6 +419,15 @@ e_composer_actions_init (EMsgComposer *composer)
G_N_ELEMENTS (toggle_entries), composer);
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ /* Asynchronous Actions */
+ action_group = composer->priv->async_actions;
+ gtk_action_group_set_translation_domain (
+ action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (
+ action_group, async_entries,
+ G_N_ELEMENTS (async_entries), composer);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+
/* Character Set Actions */
action_group = composer->priv->charset_actions;
gtk_action_group_set_translation_domain (
diff --git a/composer/e-composer-activity.c b/composer/e-composer-activity.c
new file mode 100644
index 0000000000..d565bbc1fb
--- /dev/null
+++ b/composer/e-composer-activity.c
@@ -0,0 +1,156 @@
+/*
+ * e-composer-activity.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-composer-private.h"
+
+#define E_COMPOSER_ACTIVITY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityPrivate))
+
+struct _EComposerActivityPrivate {
+ EMsgComposer *composer;
+};
+
+enum {
+ PROP_0,
+ PROP_COMPOSER
+};
+
+G_DEFINE_TYPE (
+ EComposerActivity,
+ e_composer_activity,
+ E_TYPE_ACTIVITY)
+
+static void
+composer_activity_set_sensitive (EMsgComposer *composer,
+ gboolean sensitive)
+{
+ GtkActionGroup *action_group;
+
+ action_group = composer->priv->async_actions;
+ gtk_action_group_set_sensitive (action_group, sensitive);
+}
+
+static void
+composer_activity_set_composer (EComposerActivity *activity,
+ EMsgComposer *composer)
+{
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (activity->priv->composer == NULL);
+
+ activity->priv->composer = g_object_ref (composer);
+
+ composer_activity_set_sensitive (composer, FALSE);
+}
+
+static void
+composer_activity_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_COMPOSER:
+ composer_activity_set_composer (
+ E_COMPOSER_ACTIVITY (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+composer_activity_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_COMPOSER:
+ g_value_set_object (
+ value, e_composer_activity_get_composer (
+ E_COMPOSER_ACTIVITY (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+composer_activity_dispose (GObject *object)
+{
+ EComposerActivityPrivate *priv;
+
+ priv = E_COMPOSER_ACTIVITY_GET_PRIVATE (object);
+
+ if (priv->composer != NULL) {
+ composer_activity_set_sensitive (priv->composer, TRUE);
+ g_object_unref (priv->composer);
+ priv->composer = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_composer_activity_parent_class)->dispose (object);
+}
+
+static void
+e_composer_activity_class_init (EComposerActivityClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EComposerActivityPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = composer_activity_set_property;
+ object_class->get_property = composer_activity_get_property;
+ object_class->dispose = composer_activity_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_COMPOSER,
+ g_param_spec_object (
+ "composer",
+ NULL,
+ NULL,
+ E_TYPE_MSG_COMPOSER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_composer_activity_init (EComposerActivity *activity)
+{
+ activity->priv = E_COMPOSER_ACTIVITY_GET_PRIVATE (activity);
+}
+
+EActivity *
+e_composer_activity_new (EMsgComposer *composer)
+{
+ return g_object_new (
+ E_TYPE_COMPOSER_ACTIVITY,
+ "composer", composer, NULL);
+}
+
+EMsgComposer *
+e_composer_activity_get_composer (EComposerActivity *activity)
+{
+ g_return_val_if_fail (E_IS_COMPOSER_ACTIVITY (activity), NULL);
+
+ return activity->priv->composer;
+}
diff --git a/composer/e-composer-activity.h b/composer/e-composer-activity.h
new file mode 100644
index 0000000000..431e390062
--- /dev/null
+++ b/composer/e-composer-activity.h
@@ -0,0 +1,66 @@
+/*
+ * e-composer-activity.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_COMPOSER_ACTIVITY_H
+#define E_COMPOSER_ACTIVITY_H
+
+#include <e-util/e-activity.h>
+#include <composer/e-msg-composer.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COMPOSER_ACTIVITY \
+ (e_composer_activity_get_type ())
+#define E_COMPOSER_ACTIVITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivity))
+#define E_COMPOSER_ACTIVITY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityClass))
+#define E_IS_COMPOSER_ACTIVITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY))
+#define E_IS_COMPOSER_ACTIVITY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_COMPOSER_ACTIVITY))
+#define E_COMPOSER_ACTIVITY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EComposerActivity EComposerActivity;
+typedef struct _EComposerActivityClass EComposerActivityClass;
+typedef struct _EComposerActivityPrivate EComposerActivityPrivate;
+
+struct _EComposerActivity {
+ EActivity parent;
+ EComposerActivityPrivate *priv;
+};
+
+struct _EComposerActivityClass {
+ EActivityClass parent_class;
+};
+
+GType e_composer_activity_get_type (void);
+EActivity * e_composer_activity_new (EMsgComposer *composer);
+EMsgComposer * e_composer_activity_get_composer
+ (EComposerActivity *activity);
+
+G_END_DECLS
+
+#endif /* E_COMPOSER_ACTIVITY_H */
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 01ffda6501..d96832d887 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -176,6 +176,7 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->window_group = gtk_window_group_new ();
gtk_window_group_add_window (priv->window_group, window);
+ priv->async_actions = gtk_action_group_new ("async");
priv->charset_actions = gtk_action_group_new ("charset");
priv->composer_actions = gtk_action_group_new ("composer");
@@ -253,17 +254,31 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->focus_tracker = focus_tracker;
- /* Construct the header table. */
-
container = editor->vbox;
+ /* Construct the activity bar. */
+
+ widget = e_activity_bar_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->activity_bar = g_object_ref (widget);
+ /* EActivityBar controls its own visibility. */
+
+ /* Construct the alert bar for errors. */
+
+ widget = e_alert_bar_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->alert_bar = g_object_ref (widget);
+ /* EAlertBar controls its own visibility. */
+
+ /* Construct the header table. */
+
widget = e_composer_header_table_new (shell);
gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
- gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
if (small_screen_mode)
- gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 1);
+ gtk_box_reorder_child (GTK_BOX (container), widget, 1);
else
- gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 2);
+ gtk_box_reorder_child (GTK_BOX (container), widget, 2);
priv->header_table = g_object_ref (widget);
gtk_widget_show (widget);
@@ -399,8 +414,6 @@ e_composer_private_constructed (EMsgComposer *composer)
g_signal_connect (
html, "url-requested",
G_CALLBACK (msg_composer_url_requested_cb), composer);
-
- priv->mail_sent = FALSE;
}
void
@@ -436,6 +449,16 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->header_table = NULL;
}
+ if (composer->priv->activity_bar != NULL) {
+ g_object_unref (composer->priv->activity_bar);
+ composer->priv->activity_bar = NULL;
+ }
+
+ if (composer->priv->alert_bar != NULL) {
+ g_object_unref (composer->priv->alert_bar);
+ composer->priv->alert_bar = NULL;
+ }
+
if (composer->priv->attachment_paned != NULL) {
g_object_unref (composer->priv->attachment_paned);
composer->priv->attachment_paned = NULL;
@@ -451,6 +474,11 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->window_group = NULL;
}
+ if (composer->priv->async_actions != NULL) {
+ g_object_unref (composer->priv->async_actions);
+ composer->priv->async_actions = NULL;
+ }
+
if (composer->priv->charset_actions != NULL) {
g_object_unref (composer->priv->charset_actions);
composer->priv->charset_actions = NULL;
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index 026ed96954..1e9130b196 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -24,20 +24,44 @@
#include <errno.h>
-#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
#include "e-composer-actions.h"
+#include "e-composer-activity.h"
#include "e-composer-header-table.h"
+#include "e-util/e-alert-sink.h"
#include "e-util/e-binding.h"
#include "e-util/e-charset.h"
+#include "e-util/e-extensible.h"
+#include "e-util/e-marshal.h"
#include "e-util/e-mktemp.h"
+#include "e-util/e-plugin-ui.h"
#include "e-util/e-selection.h"
#include "e-util/e-util.h"
#include "e-util/gconf-bridge.h"
+#include "widgets/misc/e-activity-bar.h"
+#include "widgets/misc/e-alert-bar.h"
+#include "widgets/misc/e-attachment.h"
#include "widgets/misc/e-attachment-icon-view.h"
#include "widgets/misc/e-attachment-paned.h"
#include "widgets/misc/e-attachment-store.h"
+#include "widgets/misc/e-signature-combo-box.h"
+#include "widgets/misc/e-web-view.h"
+#include "shell/e-shell.h"
+
+#ifdef HAVE_XFREE
+#include <X11/XF86keysym.h>
+#endif
+
+/* backward-compatibility cruft */
+#include "e-util/gtk-compat.h"
#define E_MSG_COMPOSER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -92,15 +116,19 @@ struct _EMsgComposerPrivate {
GtkWidget *html_editor;
GtkWidget *header_table;
+ GtkWidget *activity_bar;
+ GtkWidget *alert_bar;
GtkWidget *attachment_paned;
EFocusTracker *focus_tracker;
GtkWindowGroup *window_group;
+ GtkActionGroup *async_actions;
GtkActionGroup *charset_actions;
GtkActionGroup *composer_actions;
- GPtrArray *extra_hdr_names, *extra_hdr_values;
+ GPtrArray *extra_hdr_names;
+ GPtrArray *extra_hdr_values;
GArray *gconf_bridge_binding_ids;
GtkWidget *focused_entry;
@@ -111,9 +139,10 @@ struct _EMsgComposerPrivate {
GHashTable *inline_images_by_url;
GList *current_images;
- gchar *mime_type, *mime_body, *charset;
+ gchar *mime_type;
+ gchar *mime_body;
+ gchar *charset;
- guint32 is_alternative : 1;
guint32 autosaved : 1;
guint32 mode_post : 1;
guint32 in_signature_insert : 1;
@@ -122,12 +151,6 @@ struct _EMsgComposerPrivate {
CamelMimeMessage *redirect;
gboolean is_from_message;
-
- /* The mail composed has been sent. This bit will be set when
- * the mail passed sanity checking and is sent out, which
- * indicates that the composer can be destroyed. This bit can
- * be set/get by using API e_msg_composer_{set,get}_mail_sent (). */
- gboolean mail_sent;
};
void e_composer_private_constructed (EMsgComposer *composer);
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 0ea116f6dc..cc4b3c3b08 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -37,45 +37,53 @@
#include <ctype.h>
#include <fcntl.h>
-#include <glib.h>
-
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
#include "e-util/e-account-utils.h"
#include "e-util/e-alert-dialog.h"
-#include "e-util/e-alert-sink.h"
#include "e-util/e-dialog-utils.h"
-#include "e-util/e-extensible.h"
-#include "e-util/e-plugin-ui.h"
#include "e-util/e-signature-utils.h"
#include "e-util/e-util-private.h"
-#include "e-signature-combo-box.h"
-#include "shell/e-shell.h"
#include "em-format/em-format.h"
#include "em-format/em-format-quote.h"
-#include "misc/e-web-view.h"
-#include "e-msg-composer.h"
-#include "e-attachment.h"
#include "e-composer-private.h"
-#include "e-composer-header-table.h"
-#ifdef HAVE_XFREE
-#include <X11/XF86keysym.h>
-#endif
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ EActivity *activity;
+
+ CamelMimeMessage *message;
+ CamelDataWrapper *top_level_part;
+ CamelDataWrapper *text_plain_part;
+
+ EAccount *account;
+ CamelSession *session;
+ CamelInternetAddress *from;
-/* backward-compatibility cruft */
-#include "e-util/gtk-compat.h"
+ CamelTransferEncoding plain_encoding;
+ GtkPrintOperationAction print_action;
+
+ GPtrArray *recipients;
-#define d(x)
+ guint skip_content : 1;
+ guint need_thread : 1;
+ guint pgp_sign : 1;
+ guint pgp_encrypt : 1;
+ guint smime_sign : 1;
+ guint smime_encrypt : 1;
+};
-#define E_MSG_COMPOSER_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_MSG_COMPOSER, EMsgComposerPrivate))
+/* Flags for building a message. */
+typedef enum {
+ COMPOSER_FLAG_HTML_CONTENT = 1 << 0,
+ COMPOSER_FLAG_SAVE_OBJECT_DATA = 1 << 1,
+ COMPOSER_FLAG_PRIORITIZE_MESSAGE = 1 << 2,
+ COMPOSER_FLAG_REQUEST_READ_RECEIPT = 1 << 3,
+ COMPOSER_FLAG_PGP_SIGN = 1 << 4,
+ COMPOSER_FLAG_PGP_ENCRYPT = 1 << 5,
+ COMPOSER_FLAG_SMIME_SIGN = 1 << 6,
+ COMPOSER_FLAG_SMIME_ENCRYPT = 1 << 7
+} ComposerFlags;
enum {
PROP_0,
@@ -84,6 +92,7 @@ enum {
};
enum {
+ PRESEND,
SEND,
SAVE_DRAFT,
PRINT,
@@ -92,11 +101,6 @@ enum {
static guint signals[LAST_SIGNAL];
-/* local prototypes */
-static GList *add_recipients (GList *list, const gchar *recips);
-
-static void handle_mailto (EMsgComposer *composer, const gchar *mailto);
-
/* used by e_msg_composer_add_message_attachments () */
static void add_attachments_from_multipart (EMsgComposer *composer,
CamelMultipart *multipart,
@@ -121,13 +125,46 @@ static void handle_multipart_signed (EMsgComposer *composer,
GCancellable *cancellable,
gint depth);
+static void e_msg_composer_alert_sink_init (EAlertSinkInterface *interface);
+
G_DEFINE_TYPE_WITH_CODE (
EMsgComposer,
e_msg_composer,
GTKHTML_TYPE_EDITOR,
- G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ALERT_SINK, e_msg_composer_alert_sink_init)
G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->activity != NULL)
+ g_object_unref (context->activity);
+
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->top_level_part != NULL)
+ g_object_unref (context->top_level_part);
+
+ if (context->text_plain_part != NULL)
+ g_object_unref (context->text_plain_part);
+
+ if (context->account != NULL)
+ g_object_unref (context->account);
+
+ if (context->session != NULL)
+ g_object_unref (context->session);
+
+ if (context->from != NULL)
+ g_object_unref (context->from);
+
+ if (context->recipients != NULL)
+ g_ptr_array_free (context->recipients, TRUE);
+
+ g_slice_free (AsyncContext, context);
+}
+
/**
* emcu_part_to_html:
* @part:
@@ -181,52 +218,6 @@ emcu_part_to_html (CamelMimePart *part,
return text;
}
-/* copy of em_utils_prompt_user from mailer */
-static gboolean
-emcu_prompt_user (GtkWindow *parent, const gchar *promptkey, const gchar *tag, ...)
-{
- GtkDialog *mbox;
- GtkWidget *check = NULL;
- GtkWidget *container;
- va_list ap;
- gint button;
- GConfClient *gconf = gconf_client_get_default ();
- EAlert *alert = NULL;
-
- if (promptkey
- && !gconf_client_get_bool (gconf, promptkey, NULL)) {
- g_object_unref (gconf);
- return TRUE;
- }
-
- va_start (ap, tag);
- alert = e_alert_new_valist (tag, ap);
- va_end (ap);
-
- mbox = (GtkDialog*) e_alert_dialog_new (parent, alert);
- g_object_unref (alert);
-
- if (promptkey) {
- check = gtk_check_button_new_with_mnemonic (_("_Do not show this message again."));
- gtk_container_set_border_width ((GtkContainer *)check, 12);
- container = gtk_dialog_get_content_area (mbox);
- gtk_box_pack_start (GTK_BOX (container), check, TRUE, TRUE, 0);
- gtk_widget_show (check);
- }
-
- button = gtk_dialog_run (mbox);
- if (promptkey)
- gconf_client_set_bool (
- gconf, promptkey,
- !gtk_toggle_button_get_active (
- GTK_TOGGLE_BUTTON (check)), NULL);
-
- gtk_widget_destroy ((GtkWidget*) mbox);
- g_object_unref (gconf);
-
- return button == GTK_RESPONSE_YES;
-}
-
/* copy of mail_tool_remove_xevolution_headers */
static struct _camel_header_raw *
emcu_remove_xevolution_headers (CamelMimeMessage *message)
@@ -607,42 +598,413 @@ account_hash_algo_to_camel_hash (const gchar *hash_algo)
return res;
}
-static CamelMimeMessage *
-build_message (EMsgComposer *composer,
- gboolean html_content,
- gboolean save_html_object_data,
- GCancellable *cancellable,
- GError **error)
+static void
+composer_add_charset_filter (CamelStream *stream,
+ const gchar *charset)
{
- GtkhtmlEditor *editor;
- EMsgComposerPrivate *p = composer->priv;
+ CamelMimeFilter *filter;
+
+ filter = camel_mime_filter_charset_new ("UTF-8", charset);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+}
+
+static void
+composer_add_quoted_printable_filter (CamelStream *stream)
+{
+ CamelMimeFilter *filter;
+
+ filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_ENC);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+
+ filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+}
+
+/* Helper for composer_build_message_thread() */
+static gboolean
+composer_build_message_pgp (AsyncContext *context,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelCipherContext *cipher;
+ CamelDataWrapper *content;
+ CamelMimePart *mime_part;
+ const gchar *pgp_userid;
+ gboolean have_pgp_key;
+
+ /* Return silently if we're not signing or encrypting with PGP. */
+ if (!context->pgp_sign && !context->pgp_encrypt)
+ return TRUE;
+
+ have_pgp_key =
+ (context->account != NULL) &&
+ (context->account->pgp_key != NULL) &&
+ (context->account->pgp_key[0] != '\0');
+
+ mime_part = camel_mime_part_new ();
+
+ camel_medium_set_content (
+ CAMEL_MEDIUM (mime_part),
+ context->top_level_part);
+
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ mime_part, context->plain_encoding);
+
+ g_object_unref (context->top_level_part);
+ context->top_level_part = NULL;
+
+ if (have_pgp_key)
+ pgp_userid = context->account->pgp_key;
+ else
+ camel_internet_address_get (
+ context->from, 0, NULL, &pgp_userid);
+
+ if (context->pgp_sign) {
+ CamelMimePart *npart;
+ gboolean success;
+
+ npart = camel_mime_part_new ();
+
+ cipher = camel_gpg_context_new (context->session);
+ if (context->account != NULL)
+ camel_gpg_context_set_always_trust (
+ CAMEL_GPG_CONTEXT (cipher),
+ context->account->pgp_always_trust);
+
+ success = camel_cipher_context_sign_sync (
+ cipher, pgp_userid,
+ account_hash_algo_to_camel_hash (
+ (context->account != NULL) ?
+ e_account_get_string (context->account,
+ E_ACCOUNT_PGP_HASH_ALGORITHM) : NULL),
+ mime_part, npart, cancellable, error);
+
+ g_object_unref (cipher);
+
+ g_object_unref (mime_part);
+
+ if (!success) {
+ g_object_unref (npart);
+ return FALSE;
+ }
+
+ mime_part = npart;
+ }
+
+ if (context->pgp_encrypt) {
+ CamelMimePart *npart;
+ gboolean encrypt_to_self;
+ gboolean success;
+
+ encrypt_to_self =
+ (context->account != NULL) &&
+ (context->account->pgp_encrypt_to_self) &&
+ (pgp_userid != NULL);
+
+ npart = camel_mime_part_new ();
+
+ /* Check to see if we should encrypt to self.
+ * NB gets removed immediately after use */
+ if (encrypt_to_self)
+ g_ptr_array_add (
+ context->recipients,
+ g_strdup (pgp_userid));
+
+ cipher = camel_gpg_context_new (context->session);
+ if (context->account != NULL)
+ camel_gpg_context_set_always_trust (
+ CAMEL_GPG_CONTEXT (cipher),
+ context->account->pgp_always_trust);
+ success = camel_cipher_context_encrypt_sync (
+ cipher, pgp_userid, context->recipients,
+ mime_part, npart, cancellable, error);
+
+ g_object_unref (cipher);
+
+ if (encrypt_to_self)
+ g_ptr_array_set_size (
+ context->recipients,
+ context->recipients->len - 1);
+
+ g_object_unref (mime_part);
+
+ if (!success) {
+ g_object_unref (npart);
+ return FALSE;
+ }
+
+ mime_part = npart;
+ }
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+ context->top_level_part = g_object_ref (content);
+
+ g_object_unref (mime_part);
+
+ return TRUE;
+}
+
+#ifdef HAVE_SSL
+static gboolean
+composer_build_message_smime (AsyncContext *context,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelCipherContext *cipher;
+ CamelMimePart *mime_part;
+ gboolean have_signing_certificate;
+ gboolean have_encryption_certificate;
+
+ /* Return silently if we're not signing or encrypting with S/MIME. */
+ if (!context->smime_sign && !context->smime_encrypt)
+ return TRUE;
+
+ have_signing_certificate =
+ (context->account != NULL) &&
+ (context->account->smime_sign_key != NULL) &&
+ (context->account->smime_sign_key[0] != '\0');
+
+ have_encryption_certificate =
+ (context->account != NULL) &&
+ (context->account->smime_encrypt_key != NULL) &&
+ (context->account->smime_encrypt_key[0] != '\0');
+
+ if (context->smime_sign && !have_signing_certificate) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("Cannot sign outgoing message: "
+ "No signing certificate set for "
+ "this account"));
+ return FALSE;
+ }
+
+ if (context->smime_encrypt && !have_encryption_certificate) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("Cannot encrypt outgoing message: "
+ "No encryption certificate set for "
+ "this account"));
+ return FALSE;
+ }
+
+ mime_part = camel_mime_part_new ();
+
+ camel_medium_set_content (
+ CAMEL_MEDIUM (mime_part),
+ context->top_level_part);
+
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ mime_part, context->plain_encoding);
+
+ g_object_unref (context->top_level_part);
+ context->top_level_part = NULL;
+
+ if (context->smime_sign) {
+ CamelMimePart *npart;
+ gboolean success;
+
+ npart = camel_mime_part_new ();
+
+ cipher = camel_smime_context_new (context->session);
+
+ /* if we're also encrypting, envelope-sign rather than clear-sign */
+ if (context->smime_encrypt) {
+ camel_smime_context_set_sign_mode (
+ (CamelSMIMEContext *) cipher,
+ CAMEL_SMIME_SIGN_ENVELOPED);
+ camel_smime_context_set_encrypt_key (
+ (CamelSMIMEContext *) cipher, TRUE,
+ context->account->smime_encrypt_key);
+ } else if (have_encryption_certificate) {
+ camel_smime_context_set_encrypt_key (
+ (CamelSMIMEContext *) cipher, TRUE,
+ context->account->smime_encrypt_key);
+ }
+
+ success = camel_cipher_context_sign_sync (
+ cipher, context->account->smime_sign_key,
+ account_hash_algo_to_camel_hash (
+ (context->account != NULL) ?
+ e_account_get_string (context->account,
+ E_ACCOUNT_SMIME_HASH_ALGORITHM) : NULL),
+ mime_part, npart, cancellable, error);
+
+ g_object_unref (cipher);
+
+ g_object_unref (mime_part);
+
+ if (!success) {
+ g_object_unref (npart);
+ return FALSE;
+ }
+
+ mime_part = npart;
+ }
+
+ if (context->smime_encrypt) {
+ gboolean success;
+
+ /* check to see if we should encrypt to self, NB removed after use */
+ if (context->account->smime_encrypt_to_self)
+ g_ptr_array_add (
+ context->recipients, g_strdup (
+ context->account->smime_encrypt_key));
+
+ cipher = camel_smime_context_new (context->session);
+ camel_smime_context_set_encrypt_key (
+ (CamelSMIMEContext *) cipher, TRUE,
+ context->account->smime_encrypt_key);
+
+ success = camel_cipher_context_encrypt_sync (
+ cipher, NULL,
+ context->recipients, mime_part,
+ CAMEL_MIME_PART (context->message),
+ cancellable, error);
+
+ g_object_unref (cipher);
+
+ if (!success)
+ return FALSE;
+
+ if (context->account->smime_encrypt_to_self)
+ g_ptr_array_set_size (
+ context->recipients,
+ context->recipients->len - 1);
+ }
+
+ /* we replaced the message directly, we don't want to do reparenting foo */
+ if (context->smime_encrypt) {
+ context->skip_content = TRUE;
+ } else {
+ CamelDataWrapper *content;
+
+ content = camel_medium_get_content (
+ CAMEL_MEDIUM (mime_part));
+ context->top_level_part = g_object_ref (content);
+ }
+
+ g_object_unref (mime_part);
+
+ return TRUE;
+}
+#endif
+
+static void
+composer_build_message_thread (GSimpleAsyncResult *simple,
+ EMsgComposer *composer,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ /* Setup working recipient list if we're encrypting. */
+ if (context->pgp_encrypt || context->smime_encrypt) {
+ gint ii, jj;
+
+ const gchar *types[] = {
+ CAMEL_RECIPIENT_TYPE_TO,
+ CAMEL_RECIPIENT_TYPE_CC,
+ CAMEL_RECIPIENT_TYPE_BCC
+ };
+
+ context->recipients = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_free);
+ for (ii = 0; ii < G_N_ELEMENTS (types); ii++) {
+ CamelInternetAddress *addr;
+ const gchar *address;
+
+ addr = camel_mime_message_get_recipients (
+ context->message, types[ii]);
+ for (jj = 0; camel_internet_address_get (addr, jj, NULL, &address); jj++)
+ g_ptr_array_add (
+ context->recipients,
+ g_strdup (address));
+ }
+ }
+
+ if (!composer_build_message_pgp (context, cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+
+#if defined (HAVE_NSS)
+ if (!composer_build_message_smime (context, cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+#endif /* HAVE_NSS */
+}
+
+static void
+composer_add_evolution_format_header (CamelMedium *medium,
+ ComposerFlags flags)
+{
+ GString *string;
+
+ string = g_string_sized_new (128);
+
+ if (flags & COMPOSER_FLAG_HTML_CONTENT)
+ g_string_append (string, "text/html");
+ else
+ g_string_append (string, "text/plain");
+
+ if (flags & COMPOSER_FLAG_PGP_SIGN)
+ g_string_append (string, ", pgp-sign");
+
+ if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
+ g_string_append (string, ", pgp-encrypt");
+
+ if (flags & COMPOSER_FLAG_SMIME_SIGN)
+ g_string_append (string, ", smime-sign");
+
+ if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
+ g_string_append (string, ", smime-encrypt");
+
+ camel_medium_add_header (
+ medium, "X-Evolution-Format", string->str);
+
+ g_string_free (string, TRUE);
+}
+
+static void
+composer_build_message (EMsgComposer *composer,
+ ComposerFlags flags,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EMsgComposerPrivate *priv;
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+ GtkhtmlEditor *editor;
EAttachmentView *view;
EAttachmentStore *store;
EComposerHeaderTable *table;
- GtkToggleAction *action;
- CamelDataWrapper *plain, *html, *current;
- CamelTransferEncoding plain_encoding;
+ CamelDataWrapper *html;
const gchar *iconv_charset = NULL;
- GPtrArray *recipients = NULL;
CamelMultipart *body = NULL;
CamelContentType *type;
- CamelMimeMessage *new;
CamelSession *session;
CamelStream *stream;
+ CamelStream *mem_stream;
CamelMimePart *part;
GByteArray *data;
EAccount *account;
gchar *charset;
- gboolean pgp_sign;
- gboolean pgp_encrypt;
- gboolean smime_sign;
- gboolean smime_encrypt;
gint i;
- GError *local_error = NULL;
-
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ priv = composer->priv;
editor = GTKHTML_EDITOR (composer);
table = e_msg_composer_get_header_table (composer);
account = e_composer_header_table_get_account (table);
@@ -650,55 +1012,129 @@ build_message (EMsgComposer *composer,
store = e_attachment_view_get_store (view);
session = e_msg_composer_get_session (composer);
- /* evil kludgy hack for Redirect */
- if (p->redirect) {
- build_message_headers (composer, p->redirect, TRUE);
- g_object_ref (p->redirect);
- return p->redirect;
+ /* Do all the non-blocking work here, and defer
+ * any blocking operations to a separate thread. */
+
+ context = g_slice_new0 (AsyncContext);
+ context->account = g_object_ref (account);
+ context->session = g_object_ref (session);
+ context->from = e_msg_composer_get_from (composer);
+
+ if (flags & COMPOSER_FLAG_PGP_SIGN)
+ context->pgp_sign = TRUE;
+
+ if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
+ context->pgp_encrypt = TRUE;
+
+ if (flags & COMPOSER_FLAG_SMIME_SIGN)
+ context->smime_sign = TRUE;
+
+ if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
+ context->smime_encrypt = TRUE;
+
+ context->need_thread =
+ context->pgp_sign || context->pgp_encrypt ||
+ context->smime_sign || context->smime_encrypt;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, composer_build_message);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ /* If this is a redirected message, just tweak the headers. */
+ if (priv->redirect) {
+ context->message = g_object_ref (priv->redirect);
+ build_message_headers (composer, context->message, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ return;
}
- new = camel_mime_message_new ();
- build_message_headers (composer, new, FALSE);
- for (i = 0; i < p->extra_hdr_names->len; i++) {
+ context->message = camel_mime_message_new ();
+
+ build_message_headers (composer, context->message, FALSE);
+ for (i = 0; i < priv->extra_hdr_names->len; i++) {
camel_medium_add_header (
- CAMEL_MEDIUM (new),
- p->extra_hdr_names->pdata[i],
- p->extra_hdr_values->pdata[i]);
+ CAMEL_MEDIUM (context->message),
+ priv->extra_hdr_names->pdata[i],
+ priv->extra_hdr_values->pdata[i]);
}
- /* Message Disposition Notification */
- action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT));
- if (gtk_toggle_action_get_active (action)) {
+ /* Disposition-Notification-To */
+ if (flags & COMPOSER_FLAG_REQUEST_READ_RECEIPT) {
gchar *mdn_address = account->id->reply_to;
if (!mdn_address || !*mdn_address)
mdn_address = account->id->address;
camel_medium_add_header (
- CAMEL_MEDIUM (new),
+ CAMEL_MEDIUM (context->message),
"Disposition-Notification-To", mdn_address);
}
- /* Message Priority */
- action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE));
- if (gtk_toggle_action_get_active (action))
+ /* X-Priority */
+ if (flags & COMPOSER_FLAG_PRIORITIZE_MESSAGE)
camel_medium_add_header (
- CAMEL_MEDIUM (new), "X-Priority", "1");
+ CAMEL_MEDIUM (context->message),
+ "X-Priority", "1");
- if (p->mime_body) {
- if (text_requires_quoted_printable (p->mime_body, -1)) {
- plain_encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
+ if (account != NULL) {
+ /* X-Evolution-Account */
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "X-Evolution-Account", account->uid);
+
+ /* X-Evolution-Fcc */
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "X-Evolution-Fcc", account->sent_folder_uri);
+
+ /* X-Evolution-Transport */
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "X-Evolution-Transport", account->transport->url);
+
+ /* Organization */
+ if (account->id->organization != NULL) {
+ gchar *organization;
+
+ organization = camel_header_encode_string (
+ (const guchar *) account->id->organization);
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "Organization", organization);
+ g_free (organization);
+ }
+ }
+
+ /* X-Evolution-Format */
+ composer_add_evolution_format_header (
+ CAMEL_MEDIUM (context->message), flags);
+
+ /* Build the text/plain part. */
+
+ if (priv->mime_body) {
+ if (text_requires_quoted_printable (priv->mime_body, -1)) {
+ context->plain_encoding =
+ CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
} else {
- plain_encoding = CAMEL_TRANSFER_ENCODING_7BIT;
- for (i = 0; p->mime_body[i]; i++) {
- if ((guchar) p->mime_body[i] > 127) {
- plain_encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
+ context->plain_encoding = CAMEL_TRANSFER_ENCODING_7BIT;
+ for (i = 0; priv->mime_body[i]; i++) {
+ if ((guchar) priv->mime_body[i] > 127) {
+ context->plain_encoding =
+ CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
break;
}
}
}
+
data = g_byte_array_new ();
- g_byte_array_append (data, (const guint8 *)p->mime_body, strlen (p->mime_body));
- type = camel_content_type_decode (p->mime_type);
+ g_byte_array_append (
+ data, (const guint8 *) priv->mime_body,
+ strlen (priv->mime_body));
+ type = camel_content_type_decode (priv->mime_type);
+
} else {
gchar *text;
gsize length;
@@ -708,78 +1144,70 @@ build_message (EMsgComposer *composer,
g_byte_array_append (data, (guint8 *) text, (guint) length);
g_free (text);
- /* FIXME: we may want to do better than this... */
-
type = camel_content_type_new ("text", "plain");
- if ((charset = best_charset (data, p->charset, &plain_encoding))) {
+ charset = best_charset (
+ data, priv->charset, &context->plain_encoding);
+ if (charset != NULL) {
camel_content_type_set_param (type, "charset", charset);
iconv_charset = camel_iconv_charset_name (charset);
g_free (charset);
}
}
- stream = camel_stream_mem_new_with_byte_array (data);
-
- /* convert the stream to the appropriate charset */
- if (iconv_charset && g_ascii_strcasecmp (iconv_charset, "UTF-8") != 0) {
- CamelStream *filter_stream;
- CamelMimeFilter *filter;
+ mem_stream = camel_stream_mem_new_with_byte_array (data);
+ stream = camel_stream_filter_new (mem_stream);
+ g_object_unref (mem_stream);
- filter_stream = camel_stream_filter_new (stream);
- g_object_unref (stream);
-
- stream = filter_stream;
- filter = camel_mime_filter_charset_new ("UTF-8", iconv_charset);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filter_stream), filter);
- g_object_unref (filter);
- }
+ /* Convert the stream to the appropriate charset. */
+ if (iconv_charset && g_ascii_strcasecmp (iconv_charset, "UTF-8") != 0)
+ composer_add_charset_filter (stream, iconv_charset);
- if (plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
- /* encode to quoted-printable by ourself, together with
- * taking care of "\nFrom " text */
- CamelStream *filter_stream;
- CamelMimeFilter *mf, *qp;
+ /* Encode the stream to quoted-printable if necessary. */
+ if (context->plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
+ composer_add_quoted_printable_filter (stream);
- if (!CAMEL_IS_STREAM_FILTER (stream)) {
- filter_stream = camel_stream_filter_new (stream);
- g_object_unref (stream);
-
- stream = filter_stream;
- }
-
- qp = camel_mime_filter_basic_new (
- CAMEL_MIME_FILTER_BASIC_QP_ENC);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), qp);
- g_object_unref (qp);
-
- mf = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), mf);
- g_object_unref (mf);
- }
-
- /* construct the content object */
- plain = camel_data_wrapper_new ();
+ /* Construct the content object. This does not block since
+ * we're constructing the data wrapper from a memory stream. */
+ context->top_level_part = camel_data_wrapper_new ();
camel_data_wrapper_construct_from_stream_sync (
- plain, stream, NULL, NULL);
+ context->top_level_part, stream, NULL, NULL);
g_object_unref (stream);
- if (plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
- /* to not re-encode the data when pushing it to a part */
- plain->encoding = plain_encoding;
- }
+ context->text_plain_part = g_object_ref (context->top_level_part);
+
+ /* Avoid re-encoding the data when adding it to a MIME part. */
+ if (context->plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
+ context->top_level_part->encoding = context->plain_encoding;
+
+ camel_data_wrapper_set_mime_type_field (
+ context->top_level_part, type);
- camel_data_wrapper_set_mime_type_field (plain, type);
camel_content_type_unref (type);
- if (html_content) {
+ /* Build the text/html part, and wrap it and the text/plain part
+ * in a multipart/alternative part. Additionally, if there are
+ * inline images then wrap the multipart/alternative part along
+ * with the images in a multipart/related part.
+ *
+ * So the structure of all this will be:
+ *
+ * multipart/related
+ * multipart/alternative
+ * text/plain
+ * text/html
+ * image/<<whatever>>
+ * image/<<whatever>>
+ * ...
+ */
+
+ if (flags & COMPOSER_FLAG_HTML_CONTENT) {
gchar *text;
gsize length;
gboolean pre_encode;
clear_current_images (composer);
- if (save_html_object_data)
+ if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
gtkhtml_editor_run_command (editor, "save-data-on");
data = g_byte_array_new ();
@@ -788,45 +1216,30 @@ build_message (EMsgComposer *composer,
pre_encode = text_requires_quoted_printable (text, length);
g_free (text);
- if (save_html_object_data)
+ if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
gtkhtml_editor_run_command (editor, "save-data-off");
- html = camel_data_wrapper_new ();
-
- stream = camel_stream_mem_new_with_byte_array (data);
-
- if (pre_encode) {
- /* encode to quoted-printable by ourself, together with
- * taking care of "\nFrom " text */
- CamelStream *filter_stream;
- CamelMimeFilter *mf, *qp;
+ mem_stream = camel_stream_mem_new_with_byte_array (data);
+ stream = camel_stream_filter_new (mem_stream);
+ g_object_unref (mem_stream);
- if (!CAMEL_IS_STREAM_FILTER (stream)) {
- filter_stream = camel_stream_filter_new (stream);
- g_object_unref (stream);
-
- stream = filter_stream;
- }
-
- qp = camel_mime_filter_basic_new (
- CAMEL_MIME_FILTER_BASIC_QP_ENC);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), qp);
- g_object_unref (qp);
-
- mf = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), mf);
- g_object_unref (mf);
- }
+ if (pre_encode)
+ composer_add_quoted_printable_filter (stream);
+ /* Construct the content object. This does not block since
+ * we're constructing the data wrapper from a memory stream. */
+ html = camel_data_wrapper_new ();
camel_data_wrapper_construct_from_stream_sync (
html, stream, NULL, NULL);
g_object_unref (stream);
- camel_data_wrapper_set_mime_type (html, "text/html; charset=utf-8");
- if (pre_encode) {
- /* to not re-encode the data when pushing it to a part */
- html->encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
- }
+ camel_data_wrapper_set_mime_type (
+ html, "text/html; charset=utf-8");
+
+ /* Avoid re-encoding the data when adding it to a MIME part. */
+ if (pre_encode)
+ html->encoding =
+ CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
/* Build the multipart/alternative */
body = camel_multipart_new ();
@@ -834,357 +1247,124 @@ build_message (EMsgComposer *composer,
CAMEL_DATA_WRAPPER (body), "multipart/alternative");
camel_multipart_set_boundary (body, NULL);
+ /* Add the text/plain part. */
part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), plain);
- g_object_unref (plain);
- camel_mime_part_set_encoding (part, plain_encoding);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part), context->top_level_part);
+ camel_mime_part_set_encoding (part, context->plain_encoding);
camel_multipart_add_part (body, part);
g_object_unref (part);
+ /* Add the text/html part. */
part = camel_mime_part_new ();
camel_medium_set_content (CAMEL_MEDIUM (part), html);
- g_object_unref (html);
- camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
+ camel_mime_part_set_encoding (
+ part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
camel_multipart_add_part (body, part);
g_object_unref (part);
- /* If there are inlined images, construct a
- * multipart/related containing the
- * multipart/alternative and the images.
- */
- if (p->current_images) {
+ g_object_unref (context->top_level_part);
+ g_object_unref (html);
+
+ /* If there are inlined images, construct a multipart/related
+ * containing the multipart/alternative and the images. */
+ if (priv->current_images) {
CamelMultipart *html_with_images;
html_with_images = camel_multipart_new ();
camel_data_wrapper_set_mime_type (
CAMEL_DATA_WRAPPER (html_with_images),
- "multipart/related; type=\"multipart/alternative\"");
+ "multipart/related; "
+ "type=\"multipart/alternative\"");
camel_multipart_set_boundary (html_with_images, NULL);
part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (body));
- g_object_unref (body);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part),
+ CAMEL_DATA_WRAPPER (body));
camel_multipart_add_part (html_with_images, part);
g_object_unref (part);
+ g_object_unref (body);
+
add_inlined_images (composer, html_with_images);
clear_current_images (composer);
- current = CAMEL_DATA_WRAPPER (html_with_images);
+ context->top_level_part =
+ CAMEL_DATA_WRAPPER (html_with_images);
} else
- current = CAMEL_DATA_WRAPPER (body);
- } else
- current = plain;
+ context->top_level_part =
+ CAMEL_DATA_WRAPPER (body);
+ }
+ /* If there are attachments, wrap what we've built so far
+ * along with the attachments in a multipart/mixed part. */
if (e_attachment_store_get_num_attachments (store) > 0) {
CamelMultipart *multipart = camel_multipart_new ();
- if (p->is_alternative) {
- camel_data_wrapper_set_mime_type (
- CAMEL_DATA_WRAPPER (multipart),
- "multipart/alternative");
- }
-
/* Generate a random boundary. */
camel_multipart_set_boundary (multipart, NULL);
part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), current);
- if (current == plain)
- camel_mime_part_set_encoding (part, plain_encoding);
- g_object_unref (current);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part),
+ context->top_level_part);
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ part, context->plain_encoding);
camel_multipart_add_part (multipart, part);
g_object_unref (part);
e_attachment_store_add_to_multipart (
- store, multipart, p->charset);
+ store, multipart, priv->charset);
- if (p->is_alternative) {
- for (i = camel_multipart_get_number (multipart); i > 1; i--) {
- part = camel_multipart_get_part (multipart, i - 1);
- camel_medium_remove_header (CAMEL_MEDIUM (part), "Content-Disposition");
- }
- }
-
- current = CAMEL_DATA_WRAPPER (multipart);
+ g_object_unref (context->top_level_part);
+ context->top_level_part = CAMEL_DATA_WRAPPER (multipart);
}
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- pgp_sign = gtk_toggle_action_get_active (action);
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- pgp_encrypt = gtk_toggle_action_get_active (action);
-
-#if defined (HAVE_NSS)
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- smime_sign = gtk_toggle_action_get_active (action);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- smime_encrypt = gtk_toggle_action_get_active (action);
-#else
- smime_sign = FALSE;
- smime_encrypt = FALSE;
-#endif
-
- /* Setup working recipient list if we're encrypting */
- if (pgp_encrypt || smime_encrypt) {
- gint j;
- const gchar *types[] = {
- CAMEL_RECIPIENT_TYPE_TO,
- CAMEL_RECIPIENT_TYPE_CC,
- CAMEL_RECIPIENT_TYPE_BCC
- };
-
- recipients = g_ptr_array_new ();
- for (i = 0; i < G_N_ELEMENTS (types); i++) {
- CamelInternetAddress *addr;
- const gchar *address;
-
- addr = camel_mime_message_get_recipients (new, types[i]);
- for (j=0;camel_internet_address_get (addr, j, NULL, &address); j++)
- g_ptr_array_add (recipients, g_strdup (address));
-
- }
- }
-
- if (pgp_sign || pgp_encrypt) {
- const gchar *pgp_userid;
- CamelInternetAddress *from = NULL;
- CamelCipherContext *cipher;
-
- part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), current);
- if (current == plain)
- camel_mime_part_set_encoding (part, plain_encoding);
- g_object_unref (current);
-
- if (account && account->pgp_key && *account->pgp_key) {
- pgp_userid = account->pgp_key;
- } else {
- from = e_msg_composer_get_from (composer);
- camel_internet_address_get (from, 0, NULL, &pgp_userid);
- }
-
- if (pgp_sign) {
- CamelMimePart *npart = camel_mime_part_new ();
-
- cipher = camel_gpg_context_new (session);
- if (account != NULL)
- camel_gpg_context_set_always_trust (
- CAMEL_GPG_CONTEXT (cipher),
- account->pgp_always_trust);
-
- camel_cipher_context_sign_sync (
- cipher, pgp_userid,
- account_hash_algo_to_camel_hash (
- account ? e_account_get_string (account, E_ACCOUNT_PGP_HASH_ALGORITHM) : NULL),
- part, npart, cancellable, &local_error);
-
- g_object_unref (cipher);
-
- if (local_error != NULL) {
- g_object_unref (npart);
- goto exception;
- }
-
- g_object_unref (part);
- part = npart;
- }
-
- if (pgp_encrypt) {
- CamelMimePart *npart = camel_mime_part_new ();
-
- /* Check to see if we should encrypt to self.
- * NB gets removed immediately after use */
- if (account && account->pgp_encrypt_to_self && pgp_userid)
- g_ptr_array_add (recipients, g_strdup (pgp_userid));
-
- cipher = camel_gpg_context_new (session);
- if (account != NULL)
- camel_gpg_context_set_always_trust (
- CAMEL_GPG_CONTEXT (cipher),
- account->pgp_always_trust);
-
- camel_cipher_context_encrypt_sync (
- cipher, pgp_userid, recipients,
- part, npart, cancellable, &local_error);
-
- g_object_unref (cipher);
-
- if (account && account->pgp_encrypt_to_self && pgp_userid)
- g_ptr_array_set_size (recipients, recipients->len - 1);
-
- if (local_error != NULL) {
- g_object_unref (npart);
- goto exception;
- }
-
- g_object_unref (part);
- part = npart;
- }
-
- if (from)
- g_object_unref (from);
-
- current = camel_medium_get_content (CAMEL_MEDIUM (part));
- g_object_ref (current);
- g_object_unref (part);
- }
-
-#if defined (HAVE_NSS)
- if (smime_sign || smime_encrypt) {
- CamelInternetAddress *from = NULL;
- CamelCipherContext *cipher;
-
- part = camel_mime_part_new ();
- camel_medium_set_content ((CamelMedium *)part, current);
- if (current == plain)
- camel_mime_part_set_encoding (part, plain_encoding);
- g_object_unref (current);
-
- if (smime_sign && (account == NULL ||
- account->smime_sign_key == NULL ||
- account->smime_sign_key[0] == 0)) {
- g_set_error (
- &local_error,
- CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("Cannot sign outgoing message: "
- "No signing certificate set for "
- "this account"));
- goto exception;
- }
-
- if (smime_encrypt && (account == NULL ||
- account->smime_sign_key == NULL ||
- account->smime_sign_key[0] == 0)) {
- g_set_error (
- &local_error,
- CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("Cannot encrypt outgoing message: "
- "No encryption certificate set for "
- "this account"));
- goto exception;
- }
-
- if (smime_sign) {
- CamelMimePart *npart = camel_mime_part_new ();
-
- cipher = camel_smime_context_new (session);
-
- /* if we're also encrypting, envelope-sign rather than clear-sign */
- if (smime_encrypt) {
- camel_smime_context_set_sign_mode (
- (CamelSMIMEContext *) cipher,
- CAMEL_SMIME_SIGN_ENVELOPED);
- camel_smime_context_set_encrypt_key (
- (CamelSMIMEContext *) cipher,
- TRUE, account->smime_encrypt_key);
- } else if (account &&
- account->smime_encrypt_key &&
- *account->smime_encrypt_key) {
- camel_smime_context_set_encrypt_key (
- (CamelSMIMEContext *) cipher,
- TRUE, account->smime_encrypt_key);
- }
-
- camel_cipher_context_sign_sync (
- cipher, account->smime_sign_key,
- account_hash_algo_to_camel_hash (
- (account != NULL) ?
- e_account_get_string (account,
- E_ACCOUNT_SMIME_HASH_ALGORITHM) : NULL),
- part, npart, cancellable, &local_error);
-
- g_object_unref (cipher);
-
- if (local_error != NULL) {
- g_object_unref (npart);
- goto exception;
- }
-
- g_object_unref (part);
- part = npart;
- }
-
- if (smime_encrypt) {
-
- /* check to see if we should encrypt to self, NB removed after use */
- if (account->smime_encrypt_to_self)
- g_ptr_array_add (
- recipients, g_strdup (
- account->smime_encrypt_key));
-
- cipher = camel_smime_context_new (session);
- camel_smime_context_set_encrypt_key (
- (CamelSMIMEContext *) cipher, TRUE,
- account->smime_encrypt_key);
-
- camel_cipher_context_encrypt_sync (
- cipher, NULL, recipients, part,
- (CamelMimePart *) new, cancellable,
- &local_error);
-
- g_object_unref (cipher);
-
- if (local_error != NULL)
- goto exception;
-
- if (account->smime_encrypt_to_self)
- g_ptr_array_set_size (recipients, recipients->len - 1);
- }
-
- if (from)
- g_object_unref (from);
-
- /* we replaced the message directly, we don't want to do reparenting foo */
- if (smime_encrypt) {
- g_object_unref (part);
- goto skip_content;
- } else {
- current = camel_medium_get_content ((CamelMedium *)part);
- g_object_ref (current);
- g_object_unref (part);
- }
- }
-#endif /* HAVE_NSS */
-
- camel_medium_set_content (CAMEL_MEDIUM (new), current);
- if (current == plain)
- camel_mime_part_set_encoding (CAMEL_MIME_PART (new), plain_encoding);
- g_object_unref (current);
+ /* Run any blocking operations in a separate thread. */
+ if (context->need_thread)
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ composer_build_message_thread,
+ io_priority, cancellable);
+ else
+ g_simple_async_result_complete (simple);
-#if defined (HAVE_NSS)
-skip_content:
-#endif
- if (recipients) {
- for (i=0; i<recipients->len; i++)
- g_free (recipients->pdata[i]);
- g_ptr_array_free (recipients, TRUE);
- }
+ g_object_unref (simple);
+}
- /* Attach whether this message was written in HTML */
- camel_medium_set_header (
- CAMEL_MEDIUM (new), "X-Evolution-Format",
- html_content ? "text/html" : "text/plain");
+static CamelMimeMessage *
+composer_build_message_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
- return new;
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer), composer_build_message), NULL);
-exception:
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
- if (part != CAMEL_MIME_PART (new))
- g_object_unref (part);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
- g_object_unref (new);
+ /* Finalize some details before returning. */
- if (recipients) {
- for (i=0; i<recipients->len; i++)
- g_free (recipients->pdata[i]);
- g_ptr_array_free (recipients, TRUE);
- }
+ if (!context->skip_content)
+ camel_medium_set_content (
+ CAMEL_MEDIUM (context->message),
+ context->top_level_part);
- g_propagate_error (error, local_error);
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ CAMEL_MIME_PART (context->message),
+ context->plain_encoding);
- return NULL;
+ return g_object_ref (context->message);
}
/* Signatures */
@@ -1678,6 +1858,11 @@ msg_composer_delete_event_cb (EMsgComposer *composer)
shell = e_msg_composer_get_shell (composer);
+ /* If the "async" action group is insensitive, it means an
+ * asynchronous operation is in progress. Block the event. */
+ if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
+ return TRUE;
+
if (g_list_length (e_shell_get_watched_windows (shell)) == 1) {
/* This is the last watched window, use the quit
* mechanism to have a draft saved properly */
@@ -2219,6 +2404,56 @@ msg_composer_object_deleted (GtkhtmlEditor *editor)
gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
}
+static gboolean
+msg_composer_presend (EMsgComposer *composer)
+{
+ /* This keeps the signal accumulator at TRUE. */
+ return TRUE;
+}
+
+static void
+msg_composer_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ EMsgComposerPrivate *priv;
+ EAlertBar *alert_bar;
+ GtkWidget *dialog;
+ GtkWindow *parent;
+
+ priv = E_MSG_COMPOSER_GET_PRIVATE (alert_sink);
+
+ switch (e_alert_get_message_type (alert)) {
+ case GTK_MESSAGE_INFO:
+ case GTK_MESSAGE_WARNING:
+ case GTK_MESSAGE_ERROR:
+ alert_bar = E_ALERT_BAR (priv->alert_bar);
+ e_alert_bar_add_alert (alert_bar, alert);
+ break;
+
+ default:
+ parent = GTK_WINDOW (alert_sink);
+ dialog = e_alert_dialog_new (parent, alert);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ break;
+ }
+}
+
+static gboolean
+msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy)
+{
+ gboolean v_boolean;
+
+ v_boolean = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accu, v_boolean);
+
+ /* FALSE means abort the signal emission. */
+ return v_boolean;
+}
+
static void
e_msg_composer_class_init (EMsgComposerClass *class)
{
@@ -2250,6 +2485,8 @@ e_msg_composer_class_init (EMsgComposerClass *class)
editor_class->link_clicked = msg_composer_link_clicked;
editor_class->object_deleted = msg_composer_object_deleted;
+ class->presend = msg_composer_presend;
+
g_object_class_install_property (
object_class,
PROP_FOCUS_TRACKER,
@@ -2271,30 +2508,54 @@ e_msg_composer_class_init (EMsgComposerClass *class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
+ signals[PRESEND] = g_signal_new (
+ "presend",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMsgComposerClass, presend),
+ msg_composer_accumulator_false_abort,
+ NULL,
+ e_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+
signals[SEND] = g_signal_new (
"send",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ G_STRUCT_OFFSET (EMsgComposerClass, send),
+ NULL, NULL,
+ e_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_MIME_MESSAGE,
+ E_TYPE_ACTIVITY);
signals[SAVE_DRAFT] = g_signal_new (
"save-draft",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ G_STRUCT_OFFSET (EMsgComposerClass, save_draft),
+ NULL, NULL,
+ e_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_MIME_MESSAGE,
+ E_TYPE_ACTIVITY);
signals[PRINT] = g_signal_new (
"print",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
- g_cclosure_marshal_VOID__ENUM,
- G_TYPE_NONE, 1,
- GTK_TYPE_PRINT_OPERATION_ACTION);
+ e_marshal_VOID__ENUM_OBJECT_OBJECT,
+ G_TYPE_NONE, 3,
+ GTK_TYPE_PRINT_OPERATION_ACTION,
+ CAMEL_TYPE_MIME_MESSAGE,
+ E_TYPE_ACTIVITY);
+}
+
+static void
+e_msg_composer_alert_sink_init (EAlertSinkInterface *interface)
+{
+ interface->submit_alert = msg_composer_submit_alert;
}
static void
@@ -3214,6 +3475,51 @@ e_msg_composer_get_shell (EMsgComposer *composer)
return E_SHELL (composer->priv->shell);
}
+static void
+msg_composer_send_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelMimeMessage *message;
+ GtkhtmlEditor *editor;
+ GError *error = NULL;
+
+ message = e_msg_composer_get_message_finish (composer, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail-composer:no-build-message",
+ error->message, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ g_signal_emit (
+ composer, signals[SEND], 0,
+ message, context->activity);
+
+ g_object_unref (message);
+
+ async_context_free (context);
+
+ /* XXX This should be elsewhere. */
+ editor = GTKHTML_EDITOR (composer);
+ gtkhtml_editor_set_changed (editor, FALSE);
+}
+
/**
* e_msg_composer_send:
* @composer: an #EMsgComposer
@@ -3223,15 +3529,84 @@ e_msg_composer_get_shell (EMsgComposer *composer)
void
e_msg_composer_send (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ AsyncContext *context;
+ GtkAction *action;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+ gboolean proceed_with_send = TRUE;
+ const gchar *icon_name;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- editor = GTKHTML_EDITOR (composer);
+ /* This gives the user a chance to abort the send. */
+ g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_send);
+
+ if (!proceed_with_send)
+ return;
+
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_composer_activity_new (composer);
+
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (context->activity, cancellable);
+ g_object_unref (cancellable);
+
+ action = ACTION (SEND);
+ icon_name = gtk_action_get_icon_name (action);
+ e_activity_set_icon_name (context->activity, icon_name);
+
+ activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, context->activity);
+
+ e_msg_composer_get_message (
+ composer, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) msg_composer_send_cb,
+ context);
+}
+
+static void
+msg_composer_save_draft_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelMimeMessage *message;
+ GtkhtmlEditor *editor;
+ GError *error = NULL;
+
+ message = e_msg_composer_get_message_draft_finish (
+ composer, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail-composer:no-build-message",
+ error->message, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ g_signal_emit (
+ composer, signals[SAVE_DRAFT], 0,
+ message, context->activity);
+
+ g_object_unref (message);
- g_signal_emit (composer, signals[SEND], 0);
+ async_context_free (context);
/* XXX This should be elsewhere. */
+ editor = GTKHTML_EDITOR (composer);
gtkhtml_editor_set_changed (editor, FALSE);
}
@@ -3244,32 +3619,113 @@ e_msg_composer_send (EMsgComposer *composer)
void
e_msg_composer_save_draft (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ AsyncContext *context;
+ GtkAction *action;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+ const gchar *icon_name;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- editor = GTKHTML_EDITOR (composer);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_composer_activity_new (composer);
- g_signal_emit (composer, signals[SAVE_DRAFT], 0);
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (context->activity, cancellable);
+ g_object_unref (cancellable);
- /* XXX This should be elsewhere. */
- gtkhtml_editor_set_changed (editor, FALSE);
+ action = ACTION (SAVE_DRAFT);
+ icon_name = gtk_action_get_icon_name (action);
+ e_activity_set_icon_name (context->activity, icon_name);
+
+ activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, context->activity);
+
+ e_msg_composer_get_message_draft (
+ composer, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) msg_composer_save_draft_cb,
+ context);
+}
+
+static void
+msg_composer_print_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelMimeMessage *message;
+ GError *error = NULL;
+
+ message = e_msg_composer_get_message_print_finish (
+ composer, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail-composer:no-build-message",
+ error->message, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ g_signal_emit (
+ composer, signals[PRINT], 0,
+ context->print_action, message, context->activity);
+
+ g_object_unref (message);
+
+ async_context_free (context);
}
/**
* e_msg_composer_print:
* @composer: an #EMsgComposer
- * @action: the print action to start
+ * @print_action: the print action to start
*
* Print the message in @composer.
**/
void
e_msg_composer_print (EMsgComposer *composer,
- GtkPrintOperationAction action)
+ GtkPrintOperationAction print_action)
{
+ AsyncContext *context;
+ GtkAction *action;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+ const gchar *icon_name;
+
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- g_signal_emit (composer, signals[PRINT], 0, action);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_composer_activity_new (composer);
+ context->print_action = print_action;
+
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (context->activity, cancellable);
+ g_object_unref (cancellable);
+
+ action = ACTION (PRINT);
+ icon_name = gtk_action_get_icon_name (action);
+ e_activity_set_icon_name (context->activity, icon_name);
+
+ activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, context->activity);
+
+ e_msg_composer_get_message_print (
+ composer, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) msg_composer_print_cb,
+ context);
}
static GList *
@@ -3542,81 +3998,164 @@ e_msg_composer_set_body (EMsgComposer *composer,
/**
* e_msg_composer_add_header:
- * @composer: a composer object
- * @name: the header name
- * @value: the header value
+ * @composer: an #EMsgComposer
+ * @name: the header's name
+ * @value: the header's value
*
- * Adds a header with @name and @value to the message. This header
- * may not be displayed by the composer, but will be included in
- * the message it outputs.
+ * Adds a new custom header created from @name and @value. The header
+ * is not shown in the user interface but will be added to the resulting
+ * MIME message when sending or saving.
**/
void
e_msg_composer_add_header (EMsgComposer *composer,
const gchar *name,
const gchar *value)
{
- EMsgComposerPrivate *p = composer->priv;
+ EMsgComposerPrivate *priv;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (name != NULL);
g_return_if_fail (value != NULL);
- g_ptr_array_add (p->extra_hdr_names, g_strdup (name));
- g_ptr_array_add (p->extra_hdr_values, g_strdup (value));
+ priv = composer->priv;
+
+ g_ptr_array_add (priv->extra_hdr_names, g_strdup (name));
+ g_ptr_array_add (priv->extra_hdr_values, g_strdup (value));
}
/**
- * e_msg_composer_modify_header :
- * @composer : a composer object
- * @name: the header name
- * @change_value: the header value to put in place of the previous
- * value
+ * e_msg_composer_set_header:
+ * @composer: an #EMsgComposer
+ * @name: the header's name
+ * @value: the header's value
*
- * Searches for a header with name=@name ,if found it removes
- * that header and adds a new header with the @name and @change_value .
- * If not found then it creates a new header with @name and @change_value .
+ * Replaces all custom headers matching @name that were added with
+ * e_msg_composer_add_header() or e_msg_composer_set_header(), with
+ * a new custom header created from @name and @value. The header is
+ * not shown in the user interface but will be added to the resulting
+ * MIME message when sending or saving.
**/
void
-e_msg_composer_modify_header (EMsgComposer *composer,
- const gchar *name,
- const gchar *change_value)
+e_msg_composer_set_header (EMsgComposer *composer,
+ const gchar *name,
+ const gchar *value)
{
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (name != NULL);
- g_return_if_fail (change_value != NULL);
+ g_return_if_fail (value != NULL);
e_msg_composer_remove_header (composer, name);
- e_msg_composer_add_header (composer, name, change_value);
+ e_msg_composer_add_header (composer, name, value);
}
/**
- * e_msg_composer_modify_header :
- * @composer : a composer object
- * @name: the header name
+ * e_msg_composer_remove_header:
+ * @composer: an #EMsgComposer
+ * @name: the header's name
*
- * Searches for the header and if found it removes it .
+ * Removes all custom headers matching @name that were added with
+ * e_msg_composer_add_header() or e_msg_composer_set_header().
**/
void
e_msg_composer_remove_header (EMsgComposer *composer,
const gchar *name)
{
- EMsgComposerPrivate *p;
- gint i;
+ EMsgComposerPrivate *priv;
+ guint ii;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (name != NULL);
- p = composer->priv;
+ priv = composer->priv;
- for (i = 0; i < p->extra_hdr_names->len; i++) {
- if (strcmp (p->extra_hdr_names->pdata[i], name) == 0) {
- g_ptr_array_remove_index (p->extra_hdr_names, i);
- g_ptr_array_remove_index (p->extra_hdr_values, i);
+ for (ii = 0; ii < priv->extra_hdr_names->len; ii++) {
+ if (g_strcmp0 (priv->extra_hdr_names->pdata[ii], name) == 0) {
+ g_free (priv->extra_hdr_names->pdata[ii]);
+ g_free (priv->extra_hdr_values->pdata[ii]);
+ g_ptr_array_remove_index (priv->extra_hdr_names, ii);
+ g_ptr_array_remove_index (priv->extra_hdr_values, ii);
}
}
}
/**
+ * e_msg_composer_set_draft_headers:
+ * @composer: an #EMsgComposer
+ * @folder_uri: folder URI of the last saved draft
+ * @message_uid: message UID of the last saved draft
+ *
+ * Add special X-Evolution-Draft headers to remember the most recently
+ * saved draft message, even across Evolution sessions. These headers
+ * can be used to mark the draft message for deletion after saving a
+ * newer draft or sending the composed message.
+ **/
+void
+e_msg_composer_set_draft_headers (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid)
+{
+ const gchar *header_name;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (folder_uri != NULL);
+ g_return_if_fail (message_uid != NULL);
+
+ header_name = "X-Evolution-Draft-Folder";
+ e_msg_composer_set_header (composer, header_name, folder_uri);
+
+ header_name = "X-Evolution-Draft-Message";
+ e_msg_composer_set_header (composer, header_name, message_uid);
+}
+
+/**
+ * e_msg_composer_set_source_headers:
+ * @composer: an #EMsgComposer
+ * @folder_uri: folder URI of the source message
+ * @message_uid: message UID of the source message
+ * @flags: flags to set on the source message after sending
+ *
+ * Add special X-Evolution-Source headers to remember the message being
+ * forwarded or replied to, even across Evolution sessions. These headers
+ * can be used to set appropriate flags on the source message after sending
+ * the composed message.
+ **/
+void
+e_msg_composer_set_source_headers (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid,
+ CamelMessageFlags flags)
+{
+ GString *buffer;
+ const gchar *header_name;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (folder_uri != NULL);
+ g_return_if_fail (message_uid != NULL);
+
+ buffer = g_string_sized_new (32);
+
+ if (flags & CAMEL_MESSAGE_ANSWERED)
+ g_string_append (buffer, "ANSWERED ");
+ if (flags & CAMEL_MESSAGE_ANSWERED_ALL)
+ g_string_append (buffer, "ANSWERED_ALL ");
+ if (flags & CAMEL_MESSAGE_FORWARDED)
+ g_string_append (buffer, "FORWARDED ");
+ if (flags & CAMEL_MESSAGE_SEEN)
+ g_string_append (buffer, "SEEN ");
+
+ header_name = "X-Evolution-Source-Folder";
+ e_msg_composer_set_header (composer, header_name, folder_uri);
+
+ header_name = "X-Evolution-Source-Message";
+ e_msg_composer_set_header (composer, header_name, message_uid);
+
+ header_name = "X-Evolution-Source-Flags";
+ e_msg_composer_set_header (composer, header_name, buffer->str);
+
+ g_string_free (buffer, TRUE);
+}
+
+/**
* e_msg_composer_attach:
* @composer: a composer object
* @mime_part: the #CamelMimePart to attach
@@ -3745,207 +4284,211 @@ e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer,
g_strdup (location), part);
}
+static void
+composer_get_message_ready (EMsgComposer *composer,
+ GAsyncResult *result,
+ GSimpleAsyncResult *simple)
+{
+ CamelMimeMessage *message;
+ GError *error = NULL;
+
+ message = composer_build_message_finish (composer, result, &error);
+
+ if (message != NULL)
+ g_simple_async_result_set_op_res_gpointer (
+ simple, message, (GDestroyNotify) g_object_unref);
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete (simple);
+
+ g_object_unref (simple);
+}
+
/**
* e_msg_composer_get_message:
- * @composer: A message composer widget
+ * @composer: an #EMsgComposer
*
- * Retrieve the message edited by the user as a CamelMimeMessage. The
- * CamelMimeMessage object is created on the fly; subsequent calls to this
+ * Retrieve the message edited by the user as a #CamelMimeMessage. The
+ * #CamelMimeMessage object is created on the fly; subsequent calls to this
* function will always create new objects from scratch.
- *
- * Returns: A pointer to the new CamelMimeMessage object
**/
-CamelMimeMessage *
+void
e_msg_composer_get_message (EMsgComposer *composer,
- gboolean save_html_object_data,
+ gint io_priority,
GCancellable *cancellable,
- GError **error)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- EAttachmentView *view;
- EAttachmentStore *store;
- GtkhtmlEditor *editor;
- gboolean html_content;
+ GSimpleAsyncResult *simple;
+ GtkAction *action;
+ ComposerFlags flags = 0;
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- view = e_msg_composer_get_attachment_view (composer);
- store = e_attachment_view_get_store (view);
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, e_msg_composer_get_message);
- if (e_attachment_store_get_num_loading (store) > 0) {
- if (!emcu_prompt_user (GTK_WINDOW (composer), NULL,
- "mail-composer:ask-send-message-pending-download", NULL)) {
- return NULL;
- }
- }
+ if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
+ flags |= COMPOSER_FLAG_HTML_CONTENT;
- editor = GTKHTML_EDITOR (composer);
- html_content = gtkhtml_editor_get_html_mode (editor);
+ action = ACTION (PRIORITIZE_MESSAGE);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_PRIORITIZE_MESSAGE;
- return build_message (
- composer, html_content,
- save_html_object_data, cancellable, error);
-}
+ action = ACTION (REQUEST_READ_RECEIPT);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_REQUEST_READ_RECEIPT;
-static gchar *
-msg_composer_get_message_print_helper (EMsgComposer *composer,
- gboolean html_content)
-{
- GtkToggleAction *action;
- GString *string;
+ action = ACTION (PGP_SIGN);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_PGP_SIGN;
- string = g_string_sized_new (128);
+ action = ACTION (PGP_ENCRYPT);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_PGP_ENCRYPT;
- if (html_content)
- g_string_append (string, "text/html");
- else
- g_string_append (string, "text/plain");
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", pgp-sign");
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", pgp-encrypt");
- gtk_toggle_action_set_active (action, FALSE);
+#ifdef HAVE_NSS
+ action = ACTION (SMIME_SIGN);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_SMIME_SIGN;
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", smime-sign");
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", smime-encrypt");
- gtk_toggle_action_set_active (action, FALSE);
+ action = ACTION (SMIME_ENCRYPT);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
+#endif
- return g_string_free (string, FALSE);
+ composer_build_message (
+ composer, flags, io_priority,
+ cancellable, (GAsyncReadyCallback)
+ composer_get_message_ready, simple);
}
CamelMimeMessage *
-e_msg_composer_get_message_print (EMsgComposer *composer,
- gboolean save_html_object_data,
- GCancellable *cancellable)
+e_msg_composer_get_message_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
{
- EShell *shell;
- GtkhtmlEditor *editor;
- EMsgComposer *temp_composer;
- CamelMimeMessage *msg;
- gboolean html_content;
- gchar *flags;
+ GSimpleAsyncResult *simple;
+ CamelMimeMessage *message;
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer),
+ e_msg_composer_get_message), NULL);
- editor = GTKHTML_EDITOR (composer);
- html_content = gtkhtml_editor_get_html_mode (editor);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ message = g_simple_async_result_get_op_res_gpointer (simple);
- msg = build_message (
- composer, html_content, save_html_object_data,
- cancellable, NULL);
- if (msg == NULL)
+ if (g_simple_async_result_propagate_error (simple, error))
return NULL;
- shell = e_msg_composer_get_shell (composer);
- temp_composer = e_msg_composer_new_with_message (
- shell, msg, cancellable);
- g_object_unref (msg);
-
- /* Override composer flags. */
- flags = msg_composer_get_message_print_helper (
- temp_composer, html_content);
-
- msg = build_message (
- temp_composer, TRUE, save_html_object_data,
- cancellable, NULL);
- if (msg != NULL)
- camel_medium_set_header (
- CAMEL_MEDIUM (msg), "X-Evolution-Format", flags);
-
- gtk_widget_destroy (GTK_WIDGET (temp_composer));
- g_free (flags);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- return msg;
+ return g_object_ref (message);
}
-CamelMimeMessage *
-e_msg_composer_get_message_draft (EMsgComposer *composer,
+void
+e_msg_composer_get_message_print (EMsgComposer *composer,
+ gint io_priority,
GCancellable *cancellable,
- GError **error)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GtkhtmlEditor *editor;
- EComposerHeaderTable *table;
- GtkToggleAction *action;
- CamelMimeMessage *msg;
- EAccount *account;
- gboolean html_content;
- gboolean pgp_encrypt;
- gboolean pgp_sign;
- gboolean smime_encrypt;
- gboolean smime_sign;
- GString *flags;
+ GSimpleAsyncResult *simple;
+ ComposerFlags flags = 0;
- editor = GTKHTML_EDITOR (composer);
- table = e_msg_composer_get_header_table (composer);
- html_content = gtkhtml_editor_get_html_mode (editor);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- pgp_sign = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, e_msg_composer_get_message_print);
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- pgp_encrypt = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+ flags |= COMPOSER_FLAG_HTML_CONTENT;
+ flags |= COMPOSER_FLAG_SAVE_OBJECT_DATA;
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- smime_sign = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+ composer_build_message (
+ composer, flags, io_priority,
+ cancellable, (GAsyncReadyCallback)
+ composer_get_message_ready, simple);
+}
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- smime_encrypt = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+CamelMimeMessage *
+e_msg_composer_get_message_print_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ CamelMimeMessage *message;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer),
+ e_msg_composer_get_message_print), NULL);
- msg = build_message (composer, TRUE, TRUE, cancellable, error);
- if (msg == NULL)
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ message = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
return NULL;
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- gtk_toggle_action_set_active (action, pgp_sign);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- gtk_toggle_action_set_active (action, pgp_encrypt);
+ return g_object_ref (message);
+}
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- gtk_toggle_action_set_active (action, smime_sign);
+void
+e_msg_composer_get_message_draft (EMsgComposer *composer,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ ComposerFlags flags = 0;
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- gtk_toggle_action_set_active (action, smime_encrypt);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- /* Attach account info to the draft. */
- account = e_composer_header_table_get_account (table);
- if (account && account->name)
- camel_medium_set_header (
- CAMEL_MEDIUM (msg),
- "X-Evolution-Account", account->uid);
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, e_msg_composer_get_message_draft);
+
+ if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
+ flags |= COMPOSER_FLAG_HTML_CONTENT;
- flags = g_string_new (html_content ? "text/html" : "text/plain");
+ composer_build_message (
+ composer, flags, io_priority,
+ cancellable, (GAsyncReadyCallback)
+ composer_get_message_ready, simple);
+}
- /* This should probably only save the setting if it is
- * different from the from-account default? */
- if (pgp_sign)
- g_string_append (flags, ", pgp-sign");
- if (pgp_encrypt)
- g_string_append (flags, ", pgp-encrypt");
- if (smime_sign)
- g_string_append (flags, ", smime-sign");
- if (smime_encrypt)
- g_string_append (flags, ", smime-encrypt");
+CamelMimeMessage *
+e_msg_composer_get_message_draft_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ CamelMimeMessage *message;
- camel_medium_set_header (
- CAMEL_MEDIUM (msg), "X-Evolution-Format", flags->str);
- g_string_free (flags, TRUE);
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer),
+ e_msg_composer_get_message_draft), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ message = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- return msg;
+ return g_object_ref (message);
}
/**
@@ -4153,50 +4696,6 @@ e_msg_composer_can_close (EMsgComposer *composer,
return res;
}
-EMsgComposer *
-e_msg_composer_load_from_file (EShell *shell,
- const gchar *filename,
- GCancellable *cancellable)
-{
- CamelStream *stream;
- CamelMimeMessage *message;
- EMsgComposer *composer;
-
- g_return_val_if_fail (E_IS_SHELL (shell), NULL);
- g_return_val_if_fail (filename != NULL, NULL);
-
- stream = camel_stream_fs_new_with_name (
- filename, O_RDONLY, 0, NULL);
- if (stream == NULL)
- return NULL;
-
- message = camel_mime_message_new ();
- camel_data_wrapper_construct_from_stream_sync (
- CAMEL_DATA_WRAPPER (message), stream, NULL, NULL);
- g_object_unref (stream);
-
- composer = e_msg_composer_new_with_message (
- shell, message, cancellable);
- if (composer != NULL)
- gtk_widget_show (GTK_WIDGET (composer));
-
- return composer;
-}
-
-void
-e_msg_composer_set_alternative (EMsgComposer *composer,
- gboolean alt)
-{
- GtkhtmlEditor *editor;
-
- g_return_if_fail (E_IS_MSG_COMPOSER (composer));
-
- editor = GTKHTML_EDITOR (composer);
-
- composer->priv->is_alternative = alt;
- gtkhtml_editor_set_html_mode (editor, !alt);
-}
-
void
e_msg_composer_reply_indent (EMsgComposer *composer)
{
@@ -4330,20 +4829,3 @@ e_save_spell_languages (GList *spell_languages)
g_error_free (error);
}
}
-
-void
-e_msg_composer_set_mail_sent (EMsgComposer *composer,
- gboolean mail_sent)
-{
- g_return_if_fail (composer != NULL);
-
- composer->priv->mail_sent = mail_sent;
-}
-
-gboolean
-e_msg_composer_get_mail_sent (EMsgComposer *composer)
-{
- g_return_val_if_fail (composer != NULL, FALSE);
-
- return composer->priv->mail_sent;
-}
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index 866774ed9c..5cf781022e 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -66,6 +66,19 @@ struct _EMsgComposer {
struct _EMsgComposerClass {
GtkhtmlEditorClass parent_class;
+
+ /* Signals */
+ gboolean (*presend) (EMsgComposer *composer);
+ void (*print) (EMsgComposer *composer,
+ GtkPrintOperationAction print_action,
+ CamelMimeMessage *message,
+ EActivity *activity);
+ void (*save_draft) (EMsgComposer *composer,
+ CamelMimeMessage *message,
+ EActivity *activity);
+ void (*send) (EMsgComposer *composer,
+ CamelMimeMessage *message,
+ EActivity *activity);
};
GType e_msg_composer_get_type (void);
@@ -87,10 +100,7 @@ EShell * e_msg_composer_get_shell (EMsgComposer *composer);
void e_msg_composer_send (EMsgComposer *composer);
void e_msg_composer_save_draft (EMsgComposer *composer);
void e_msg_composer_print (EMsgComposer *composer,
- GtkPrintOperationAction action);
-
-void e_msg_composer_set_alternative (EMsgComposer *composer,
- gboolean alt);
+ GtkPrintOperationAction print_action);
void e_msg_composer_set_body_text (EMsgComposer *composer,
const gchar *text,
@@ -101,11 +111,20 @@ void e_msg_composer_set_body (EMsgComposer *composer,
void e_msg_composer_add_header (EMsgComposer *composer,
const gchar *name,
const gchar *value);
-void e_msg_composer_modify_header (EMsgComposer *composer,
+void e_msg_composer_set_header (EMsgComposer *composer,
const gchar *name,
const gchar *value);
void e_msg_composer_remove_header (EMsgComposer *composer,
const gchar *name);
+void e_msg_composer_set_draft_headers
+ (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid);
+void e_msg_composer_set_source_headers
+ (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid,
+ CamelMessageFlags flags);
void e_msg_composer_attach (EMsgComposer *composer,
CamelMimePart *mime_part);
CamelMimePart * e_msg_composer_add_inline_image_from_file
@@ -114,20 +133,37 @@ CamelMimePart * e_msg_composer_add_inline_image_from_file
void e_msg_composer_add_inline_image_from_mime_part
(EMsgComposer *composer,
CamelMimePart *part);
-CamelMimeMessage *
- e_msg_composer_get_message (EMsgComposer *composer,
- gboolean save_html_object_data,
+void e_msg_composer_get_message (EMsgComposer *composer,
+ gint io_priority,
GCancellable *cancellable,
- GError **error);
+ GAsyncReadyCallback callback,
+ gpointer user_data);
CamelMimeMessage *
- e_msg_composer_get_message_print
+ e_msg_composer_get_message_finish
(EMsgComposer *composer,
- gboolean save_html_object_data,
- GCancellable *cancellable);
+ GAsyncResult *result,
+ GError **error);
+void e_msg_composer_get_message_print
+ (EMsgComposer *composer,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
CamelMimeMessage *
- e_msg_composer_get_message_draft
+ e_msg_composer_get_message_print_finish
+ (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error);
+void e_msg_composer_get_message_draft
(EMsgComposer *composer,
+ gint io_priority,
GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelMimeMessage *
+ e_msg_composer_get_message_draft_finish
+ (EMsgComposer *composer,
+ GAsyncResult *result,
GError **error);
void e_msg_composer_show_sig_file (EMsgComposer *composer);
@@ -147,10 +183,6 @@ void e_msg_composer_request_close (EMsgComposer *composer);
gboolean e_msg_composer_can_close (EMsgComposer *composer,
gboolean can_save_draft);
-EMsgComposer * e_msg_composer_load_from_file (EShell *shell,
- const gchar *filename,
- GCancellable *cancellable);
-
void e_msg_composer_reply_indent (EMsgComposer *composer);
EComposerHeaderTable *
@@ -166,10 +198,6 @@ gboolean e_msg_composer_is_exiting (EMsgComposer *composer);
GList * e_load_spell_languages (void);
void e_save_spell_languages (GList *spell_languages);
-gboolean e_msg_composer_get_mail_sent (EMsgComposer *composer);
-void e_msg_composer_set_mail_sent (EMsgComposer *composer,
- gboolean mail_sent);
-
G_END_DECLS
#endif /* E_MSG_COMPOSER_H */
diff --git a/composer/mail-composer.error.xml b/composer/mail-composer.error.xml
index 262eee459c..90f0187b0e 100644
--- a/composer/mail-composer.error.xml
+++ b/composer/mail-composer.error.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<error-list domain="mail-composer">
- <error id="no-attach" type="error" modal="true">
+ <error id="no-attach" type="error">
<_primary>You cannot attach the file &quot;{0}&quot; to this message.</_primary>
<!--For Translators: '{1}' is the exception description,describing why the file could not be attached to the message -->
<secondary>{1}</secondary>
@@ -35,7 +35,7 @@
<button _label="_Send" response="GTK_RESPONSE_YES"/>
</error>
- <error id="exit-unsaved" modal="true" type="warning" default="GTK_RESPONSE_YES">
+ <error id="exit-unsaved" type="warning" default="GTK_RESPONSE_YES">
<_primary>Are you sure you want to discard the message, titled '{0}', you are composing?</_primary>
<_secondary>Closing this composer window will discard the message permanently, unless you choose to save the message in your Drafts folder. This will allow you to continue the message at a later date.</_secondary>
<button _label="_Discard Changes" response="GTK_RESPONSE_NO"/>
@@ -43,30 +43,39 @@
<button _label="_Save Draft" response="GTK_RESPONSE_YES"/>
</error>
- <error id="no-build-message" type="error" modal="true">
+ <error id="no-build-message" type="error">
<_primary>Could not create message.</_primary>
<_secondary>Because &quot;{0}&quot;, you may need to select different mail options.</_secondary>
</error>
- <error id="no-sig-file" type="warning" modal="true">
+ <error id="no-sig-file" type="warning">
<_primary>Could not read signature file &quot;{0}&quot;.</_primary>
<_secondary>Because &quot;{1}&quot;.</_secondary>
</error>
- <error id="all-accounts-deleted" type="warning" modal="true">
+ <error id="all-accounts-deleted" type="warning">
<_primary>All accounts have been removed.</_primary>
<_secondary>You need to configure an account before you can compose mail.</_secondary>
</error>
- <error id="no-address-control" type="error" modal="true">
- <_primary>Could not create composer window.</_primary>
- <_secondary>Unable to activate the address selector control.</_secondary>
+ <error id="append-to-outbox-error" type="error">
+ <_primary>An error occurred while saving to your Outbox folder.</_primary>
+ <_secondary>The reported error was &quot;{0}&quot;. The message has not been sent.</_secondary>
</error>
- <error id="no-editor-control" type="error" modal="true">
- <_primary>Could not create composer window.</_primary>
- <_secondary xml:space="preserve">Unable to activate the HTML editor control.
+ <error id="save-draft-error" type="error">
+ <_primary>An error occurred while saving to your Drafts folder.</_primary>
+ <_secondary>The reported error was &quot;{0}&quot;. The message has most likely not been saved.</_secondary>
+ </error>
+
+ <error id="send-error" type="error">
+ <_primary>An error occurred while sending.</_primary>
+ <_secondary>The reported error was &quot;{0}&quot;.</_secondary>
+ </error>
-Please make sure that you have the correct version of gtkhtml and libgtkhtml installed.</_secondary>
+ <error id="saved-to-outbox" type="info">
+ <_primary>Message saved to Outbox.</_primary>
+ <_secondary>Because you are working offline, the message has been saved to your local Outbox folder. When you are back online you can send the message by clicking the Send/Receive button in Evolution's toolbar.</_secondary>
</error>
+
</error-list>
diff --git a/e-util/e-alert-activity.c b/e-util/e-alert-activity.c
index 3a97a4eab4..7d7e5a7999 100644
--- a/e-util/e-alert-activity.c
+++ b/e-util/e-alert-activity.c
@@ -124,8 +124,6 @@ alert_activity_constructed (GObject *object)
secondary_text = e_alert_get_secondary_text (alert);
e_activity_set_secondary_text (activity, secondary_text);
- g_object_unref (alert);
-
/* This is a constructor property, so can't do it in init().
* XXX What we really want to do is override the property's
* default value, but GObject does not support that. */
diff --git a/e-util/e-alert-dialog.c b/e-util/e-alert-dialog.c
index 0057ed91aa..4d6fcd8e6e 100644
--- a/e-util/e-alert-dialog.c
+++ b/e-util/e-alert-dialog.c
@@ -24,95 +24,90 @@
#include "e-alert-dialog.h"
#include "e-util.h"
-G_DEFINE_TYPE (
- EAlertDialog,
- e_alert_dialog,
- GTK_TYPE_DIALOG)
-
-#define ALERT_DIALOG_PRIVATE(o) \
+#define E_ALERT_DIALOG_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), E_TYPE_ALERT_DIALOG, EAlertDialogPrivate))
-struct _EAlertDialogPrivate
-{
+struct _EAlertDialogPrivate {
GtkWindow *parent;
EAlert *alert;
};
-enum
-{
+enum {
PROP_0,
- PROP_PARENT,
PROP_ALERT
};
+G_DEFINE_TYPE (
+ EAlertDialog,
+ e_alert_dialog,
+ GTK_TYPE_DIALOG)
+
static void
-e_alert_dialog_set_property (GObject *object, guint property_id,
- const GValue *value, GParamSpec *pspec)
+alert_dialog_set_alert (EAlertDialog *dialog,
+ EAlert *alert)
{
- EAlertDialog *dialog = (EAlertDialog*) object;
+ g_return_if_fail (E_IS_ALERT (alert));
+ g_return_if_fail (dialog->priv->alert == NULL);
- switch (property_id)
- {
- case PROP_PARENT:
- dialog->priv->parent = g_value_dup_object (value);
- break;
+ dialog->priv->alert = g_object_ref (alert);
+}
+
+static void
+alert_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
case PROP_ALERT:
- dialog->priv->alert = g_value_dup_object (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ alert_dialog_set_alert (
+ E_ALERT_DIALOG (object),
+ g_value_get_object (value));
+ return;
}
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
-e_alert_dialog_get_property (GObject *object, guint property_id,
- GValue *value, GParamSpec *pspec)
+alert_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- EAlertDialog *dialog = (EAlertDialog*) object;
-
- switch (property_id)
- {
- case PROP_PARENT:
- g_value_set_object (value, dialog->priv->parent);
- break;
+ switch (property_id) {
case PROP_ALERT:
- g_value_set_object (value, dialog->priv->alert);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ g_value_set_object (
+ value, e_alert_dialog_get_alert (
+ E_ALERT_DIALOG (object)));
+ return;
}
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
-e_alert_dialog_dispose (GObject *object)
+alert_dialog_dispose (GObject *object)
{
- EAlertDialog *dialog = (EAlertDialog*) object;
+ EAlertDialogPrivate *priv;
- if (dialog->priv->parent) {
- g_object_unref (dialog->priv->parent);
- dialog->priv->parent = NULL;
- }
+ priv = E_ALERT_DIALOG_GET_PRIVATE (object);
- if (dialog->priv->alert) {
- g_object_unref (dialog->priv->alert);
- dialog->priv->alert = NULL;
+ if (priv->alert) {
+ g_object_unref (priv->alert);
+ priv->alert = NULL;
}
+ /* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_alert_dialog_parent_class)->dispose (object);
}
static void
-e_alert_dialog_init (EAlertDialog *self)
-{
- self->priv = ALERT_DIALOG_PRIVATE (self);
-}
-
-static void
-e_alert_dialog_constructed (GObject *obj)
+alert_dialog_constructed (GObject *object)
{
- EAlertDialog *self = (EAlertDialog*) obj;
+ EAlertDialog *self = (EAlertDialog*) object;
EAlert *alert;
- struct _e_alert_button *b;
+ EAlertButton *b;
GtkWidget *action_area;
GtkWidget *content_area;
GtkWidget *container;
@@ -123,58 +118,49 @@ e_alert_dialog_constructed (GObject *obj)
g_return_if_fail (self != NULL);
- self->priv = ALERT_DIALOG_PRIVATE (self);
- alert = self->priv->alert;
+ alert = e_alert_dialog_get_alert (E_ALERT_DIALOG (self));
gtk_window_set_title (GTK_WINDOW (self), " ");
- action_area = gtk_dialog_get_action_area ((GtkDialog*) self);
- content_area = gtk_dialog_get_content_area ((GtkDialog*) self);
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (self));
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (self));
#if !GTK_CHECK_VERSION(2,90,7)
g_object_set (self, "has-separator", FALSE, NULL);
#endif
- gtk_widget_ensure_style ((GtkWidget *)self);
+ gtk_widget_ensure_style (GTK_WIDGET (self));
gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
- if (self->priv->parent)
- gtk_window_set_transient_for ((GtkWindow *)self, self->priv->parent);
- else
- g_warning (
- "Something called %s() with a NULL parent window. "
- "This is no longer legal, please fix it.", G_STRFUNC);
-
- gtk_window_set_destroy_with_parent ((GtkWindow *)self, TRUE);
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (self), TRUE);
b = e_alert_peek_buttons (alert);
if (b == NULL) {
- gtk_dialog_add_button ((GtkDialog*) self, GTK_STOCK_OK, GTK_RESPONSE_OK);
+ gtk_dialog_add_button (
+ GTK_DIALOG (self), GTK_STOCK_OK, GTK_RESPONSE_OK);
} else {
for (; b; b=b->next) {
if (b->stock) {
- if (b->label) {
-#if 0
- /* FIXME: So although this looks like it will work, it wont.
- Need to do it the hard way ... it also breaks the
- default_response stuff */
- w = gtk_button_new_from_stock (b->stock);
- gtk_button_set_label ((GtkButton *)w, b->label);
- gtk_widget_show (w);
- gtk_dialog_add_action_widget (self, w, b->response);
-#endif
- gtk_dialog_add_button ((GtkDialog*) self, b->label, b->response);
- } else
- gtk_dialog_add_button ((GtkDialog*) self, b->stock, b->response);
+ if (b->label != NULL)
+ gtk_dialog_add_button (
+ GTK_DIALOG (self),
+ b->label, b->response);
+ else
+ gtk_dialog_add_button (
+ GTK_DIALOG (self),
+ b->stock, b->response);
} else
- gtk_dialog_add_button ((GtkDialog*) self, b->label, b->response);
+ gtk_dialog_add_button (
+ GTK_DIALOG (self),
+ b->label, b->response);
}
}
if (e_alert_get_default_response (alert))
- gtk_dialog_set_default_response ((GtkDialog*) self,
- e_alert_get_default_response (alert));
+ gtk_dialog_set_default_response (
+ GTK_DIALOG (self),
+ e_alert_get_default_response (alert));
widget = gtk_hbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
@@ -224,90 +210,118 @@ e_alert_dialog_constructed (GObject *obj)
}
static void
-e_alert_dialog_class_init (EAlertDialogClass *klass)
+alert_dialog_response (GtkDialog *dialog,
+ gint response_id)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (EAlertDialogPrivate));
-
- object_class->dispose = e_alert_dialog_dispose;
- object_class->get_property = e_alert_dialog_get_property;
- object_class->set_property = e_alert_dialog_set_property;
- object_class->constructed = e_alert_dialog_constructed;
-
- g_object_class_install_property (object_class,
- PROP_PARENT,
- g_param_spec_object ("parent",
- "parent window",
- "A parent window to be transient for",
- GTK_TYPE_WINDOW,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_ALERT,
- g_param_spec_object ("alert",
- "alert",
- "EAlert to be displayed",
- E_TYPE_ALERT,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS));
+ EAlert *alert;
+
+ alert = e_alert_dialog_get_alert (E_ALERT_DIALOG (dialog));
+ e_alert_response (alert, response_id);
}
-GtkWidget*
+static void
+e_alert_dialog_class_init (EAlertDialogClass *class)
+{
+ GObjectClass *object_class;
+ GtkDialogClass *dialog_class;
+
+ g_type_class_add_private (class, sizeof (EAlertDialogPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = alert_dialog_set_property;
+ object_class->get_property = alert_dialog_get_property;
+ object_class->dispose = alert_dialog_dispose;
+ object_class->constructed = alert_dialog_constructed;
+
+ dialog_class = GTK_DIALOG_CLASS (class);
+ dialog_class->response = alert_dialog_response;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ALERT,
+ g_param_spec_object (
+ "alert",
+ "Alert",
+ "Alert to be displayed",
+ E_TYPE_ALERT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_alert_dialog_init (EAlertDialog *self)
+{
+ self->priv = E_ALERT_DIALOG_GET_PRIVATE (self);
+}
+
+GtkWidget *
e_alert_dialog_new (GtkWindow *parent,
EAlert *alert)
{
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
return g_object_new (
E_TYPE_ALERT_DIALOG,
- "parent", parent, "alert", alert, NULL);
+ "alert", alert, "transient-for", parent, NULL);
}
-GtkWidget*
-e_alert_dialog_new_for_args (GtkWindow *parent, const gchar *tag, ...)
+GtkWidget *
+e_alert_dialog_new_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...)
{
- GtkWidget *d;
- EAlert *e;
+ GtkWidget *dialog;
+ EAlert *alert;
va_list ap;
+ g_return_val_if_fail (tag != NULL, NULL);
+
va_start (ap, tag);
- e = e_alert_new_valist (tag, ap);
+ alert = e_alert_new_valist (tag, ap);
va_end (ap);
- d = e_alert_dialog_new (parent, e);
- g_object_unref (e);
+ dialog = e_alert_dialog_new (parent, alert);
+
+ g_object_unref (alert);
- return d;
+ return dialog;
}
gint
-e_alert_run_dialog (GtkWindow *parent, EAlert *alert)
+e_alert_run_dialog (GtkWindow *parent,
+ EAlert *alert)
{
GtkWidget *dialog;
- gint res;
+ gint response;
- dialog = e_alert_dialog_new (parent, alert);
+ g_return_val_if_fail (E_IS_ALERT (alert), 0);
- res = gtk_dialog_run ((GtkDialog *)dialog);
+ dialog = e_alert_dialog_new (parent, alert);
+ response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
- return res;
+ return response;
}
gint
-e_alert_run_dialog_for_args (GtkWindow *parent, const gchar *tag, ...)
+e_alert_run_dialog_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...)
{
- EAlert *e;
- va_list ap;
+ EAlert *alert;
gint response;
+ va_list ap;
+
+ g_return_val_if_fail (tag != NULL, 0);
va_start (ap, tag);
- e = e_alert_new_valist (tag, ap);
+ alert = e_alert_new_valist (tag, ap);
va_end (ap);
- response = e_alert_run_dialog (parent, e);
- g_object_unref (e);
+ response = e_alert_run_dialog (parent, alert);
+
+ g_object_unref (alert);
return response;
}
@@ -329,7 +343,7 @@ e_alert_dialog_count_buttons (EAlertDialog *dialog)
g_return_val_if_fail (E_IS_ALERT_DIALOG (dialog), 0);
- container = gtk_dialog_get_action_area ((GtkDialog*) dialog);
+ container = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
children = gtk_container_get_children (GTK_CONTAINER (container));
/* Iterate over the children looking for buttons. */
@@ -346,19 +360,14 @@ e_alert_dialog_count_buttons (EAlertDialog *dialog)
* e_alert_dialog_get_alert:
* @dialog: a #EAlertDialog
*
- * Convenience API for getting the #EAlert associated with @dialog
+ * Returns the #EAlert associated with @dialog.
*
- * Return value: the #EAlert associated with @dialog. The alert should be
- * unreffed when no longer needed.
- */
+ * Returns: the #EAlert associated with @dialog
+ **/
EAlert *
e_alert_dialog_get_alert (EAlertDialog *dialog)
{
- EAlert *alert = NULL;
-
- g_return_val_if_fail (dialog != NULL, NULL);
+ g_return_val_if_fail (E_IS_ALERT_DIALOG (dialog), NULL);
- g_object_get (dialog, "alert", &alert,
- NULL);
- return alert;
+ return dialog->priv->alert;
}
diff --git a/e-util/e-alert-dialog.h b/e-util/e-alert-dialog.h
index 54235a757b..96253a582e 100644
--- a/e-util/e-alert-dialog.h
+++ b/e-util/e-alert-dialog.h
@@ -21,63 +21,60 @@
* Copyright (C) 2009 Intel Corporation
*/
-#ifndef _E_ALERT_DIALOG_H
-#define _E_ALERT_DIALOG_H
+#ifndef E_ALERT_DIALOG_H
+#define E_ALERT_DIALOG_H
#include <gtk/gtk.h>
#include <e-util/e-alert.h>
-G_BEGIN_DECLS
-
-#define E_TYPE_ALERT_DIALOG e_alert_dialog_get_type()
-
+/* Standard GObject macros */
+#define E_TYPE_ALERT_DIALOG \
+ (e_alert_dialog_get_type ())
#define E_ALERT_DIALOG(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
- E_TYPE_ALERT_DIALOG, EAlertDialog))
-
-#define E_ALERT_DIALOG_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST ((klass), \
- E_TYPE_ALERT_DIALOG, EAlertDialogClass))
-
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT_DIALOG, EAlertDialog))
+#define E_ALERT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT_DIALOG, EAlertDialogClass))
#define E_IS_ALERT_DIALOG(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
- E_TYPE_ALERT_DIALOG))
-
-#define E_IS_ALERT_DIALOG_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE ((klass), \
- E_TYPE_ALERT_DIALOG))
-
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT_DIALOG))
+#define E_IS_ALERT_DIALOG_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT_DIALOG))
#define E_ALERT_DIALOG_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), \
- E_TYPE_ALERT_DIALOG, EAlertDialogClass))
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALERT_DIALOG, EAlertDialogClass))
+
+G_BEGIN_DECLS
typedef struct _EAlertDialog EAlertDialog;
typedef struct _EAlertDialogClass EAlertDialogClass;
typedef struct _EAlertDialogPrivate EAlertDialogPrivate;
-struct _EAlertDialog
-{
- GtkDialog parent;
- EAlertDialogPrivate *priv;
+struct _EAlertDialog {
+ GtkDialog parent;
+ EAlertDialogPrivate *priv;
};
-struct _EAlertDialogClass
-{
- GtkDialogClass parent_class;
+struct _EAlertDialogClass {
+ GtkDialogClass parent_class;
};
-GType e_alert_dialog_get_type (void);
-
-GtkWidget* e_alert_dialog_new (GtkWindow* parent, EAlert *alert);
-GtkWidget* e_alert_dialog_new_for_args (GtkWindow* parent, const gchar *tag, ...) G_GNUC_NULL_TERMINATED;
-
-/* Convenience functions for displaying the alert in a GtkDialog */
-gint e_alert_run_dialog (GtkWindow *parent, EAlert *alert);
-gint e_alert_run_dialog_for_args (GtkWindow *parent, const gchar *tag, ...) G_GNUC_NULL_TERMINATED;
-
-guint e_alert_dialog_count_buttons (EAlertDialog *dialog);
-EAlert *e_alert_dialog_get_alert (EAlertDialog *dialog);
+GType e_alert_dialog_get_type (void);
+GtkWidget * e_alert_dialog_new (GtkWindow *parent,
+ EAlert *alert);
+GtkWidget * e_alert_dialog_new_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...) G_GNUC_NULL_TERMINATED;
+gint e_alert_run_dialog (GtkWindow *parent,
+ EAlert *alert);
+gint e_alert_run_dialog_for_args (GtkWindow *parent,
+ const gchar *tag,
+ ...) G_GNUC_NULL_TERMINATED;
+guint e_alert_dialog_count_buttons (EAlertDialog *dialog);
+EAlert * e_alert_dialog_get_alert (EAlertDialog *dialog);
G_END_DECLS
-#endif /* _E_ALERT_DIALOG_H */
+#endif /* E_ALERT_DIALOG_H */
diff --git a/e-util/e-alert.c b/e-util/e-alert.c
index ced83ddc0e..1846e6d1fa 100644
--- a/e-util/e-alert.c
+++ b/e-util/e-alert.c
@@ -52,7 +52,7 @@ struct _e_alert {
gint default_response;
const gchar *primary_text;
const gchar *secondary_text;
- struct _e_alert_button *buttons;
+ EAlertButton *buttons;
};
struct _e_alert_table {
@@ -65,7 +65,7 @@ static GHashTable *alert_table;
/* ********************************************************************** */
-static struct _e_alert_button default_ok_button = {
+static EAlertButton default_ok_button = {
NULL, "gtk-ok", NULL, GTK_RESPONSE_OK
};
@@ -78,31 +78,48 @@ static struct _e_alert default_alerts[] = {
/* ********************************************************************** */
-static struct {
- const gchar *name;
- gint id;
-} response_map[] = {
- { "GTK_RESPONSE_REJECT", GTK_RESPONSE_REJECT },
- { "GTK_RESPONSE_ACCEPT", GTK_RESPONSE_ACCEPT },
- { "GTK_RESPONSE_OK", GTK_RESPONSE_OK },
- { "GTK_RESPONSE_CANCEL", GTK_RESPONSE_CANCEL },
- { "GTK_RESPONSE_CLOSE", GTK_RESPONSE_CLOSE },
- { "GTK_RESPONSE_YES", GTK_RESPONSE_YES },
- { "GTK_RESPONSE_NO", GTK_RESPONSE_NO },
- { "GTK_RESPONSE_APPLY", GTK_RESPONSE_APPLY },
- { "GTK_RESPONSE_HELP", GTK_RESPONSE_HELP },
+struct _EAlertPrivate {
+ gchar *tag;
+ GPtrArray *args;
+ gchar *primary_text;
+ gchar *secondary_text;
+ struct _e_alert *definition;
+ GtkMessageType message_type;
+ gint default_response;
};
+enum {
+ PROP_0,
+ PROP_ARGS,
+ PROP_TAG,
+ PROP_MESSAGE_TYPE,
+ PROP_PRIMARY_TEXT,
+ PROP_SECONDARY_TEXT
+};
+
+enum {
+ RESPONSE,
+ LAST_SIGNAL
+};
+
+static gulong signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (
+ EAlert,
+ e_alert,
+ G_TYPE_OBJECT)
+
static gint
map_response (const gchar *name)
{
- gint i;
+ GEnumClass *class;
+ GEnumValue *value;
- for (i = 0; i < G_N_ELEMENTS (response_map); i++)
- if (!strcmp (name, response_map[i].name))
- return response_map[i].id;
+ class = g_type_class_ref (GTK_TYPE_RESPONSE_TYPE);
+ value = g_enum_get_value_by_name (class, name);
+ g_type_class_unref (class);
- return 0;
+ return (value != NULL) ? value->value : 0;
}
static GtkMessageType
@@ -111,36 +128,13 @@ map_type (const gchar *nick)
GEnumClass *class;
GEnumValue *value;
- class = g_type_class_peek (GTK_TYPE_MESSAGE_TYPE);
+ class = g_type_class_ref (GTK_TYPE_MESSAGE_TYPE);
value = g_enum_get_value_by_nick (class, nick);
+ g_type_class_unref (class);
return (value != NULL) ? value->value : GTK_MESSAGE_ERROR;
}
-G_DEFINE_TYPE (
- EAlert,
- e_alert,
- G_TYPE_OBJECT)
-
-enum {
- PROP_0,
- PROP_ARGS,
- PROP_TAG,
- PROP_MESSAGE_TYPE,
- PROP_PRIMARY_TEXT,
- PROP_SECONDARY_TEXT
-};
-
-struct _EAlertPrivate {
- gchar *tag;
- GPtrArray *args;
- gchar *primary_text;
- gchar *secondary_text;
- struct _e_alert *definition;
- GtkMessageType message_type;
- gint default_response;
-};
-
/*
XML format:
@@ -152,9 +146,6 @@ struct _EAlertPrivate {
response="response_id"? /> *
</error>
- The tool e-error-tool is used to extract the translatable strings for
- translation.
-
*/
static void
e_alert_load (const gchar *path)
@@ -162,7 +153,7 @@ e_alert_load (const gchar *path)
xmlDocPtr doc = NULL;
xmlNodePtr root, error, scan;
struct _e_alert *e;
- struct _e_alert_button *lastbutton;
+ EAlertButton *lastbutton;
struct _e_alert_table *table;
gchar *tmp;
@@ -217,7 +208,7 @@ e_alert_load (const gchar *path)
e->id = g_strdup (tmp);
xmlFree (tmp);
- lastbutton = (struct _e_alert_button *)&e->buttons;
+ lastbutton = (EAlertButton *)&e->buttons;
tmp = (gchar *)xmlGetProp(error, (const guchar *)"type");
e->message_type = map_type (tmp);
@@ -242,7 +233,7 @@ e_alert_load (const gchar *path)
xmlFree (tmp);
}
} else if (!strcmp((gchar *)scan->name, "button")) {
- struct _e_alert_button *b;
+ EAlertButton *b;
gchar *label = NULL;
gchar *stock = NULL;
@@ -557,6 +548,16 @@ e_alert_class_init (EAlertClass *class)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ signals[RESPONSE] = g_signal_new (
+ "response",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EAlertClass, response),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+
e_alert_load_tables ();
}
@@ -730,16 +731,8 @@ e_alert_set_secondary_text (EAlert *alert,
g_object_notify (G_OBJECT (alert), "secondary-text");
}
-struct _e_alert_button *
-e_alert_peek_buttons (EAlert *alert)
-{
- g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL);
- return alert->priv->definition->buttons;
-}
-
-GtkWidget *
-e_alert_create_image (EAlert *alert,
- GtkIconSize size)
+const gchar *
+e_alert_get_stock_id (EAlert *alert)
{
const gchar *stock_id;
@@ -764,10 +757,39 @@ e_alert_create_image (EAlert *alert,
break;
}
+ return stock_id;
+}
+
+EAlertButton *
+e_alert_peek_buttons (EAlert *alert)
+{
+ g_return_val_if_fail (alert && alert->priv && alert->priv->definition, NULL);
+ return alert->priv->definition->buttons;
+}
+
+GtkWidget *
+e_alert_create_image (EAlert *alert,
+ GtkIconSize size)
+{
+ const gchar *stock_id;
+
+ g_return_val_if_fail (E_IS_ALERT (alert), NULL);
+
+ stock_id = e_alert_get_stock_id (alert);
+
return gtk_image_new_from_stock (stock_id, size);
}
void
+e_alert_response (EAlert *alert,
+ gint response_id)
+{
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ g_signal_emit (alert, signals[RESPONSE], 0, response_id);
+}
+
+void
e_alert_submit (GtkWidget *widget,
const gchar *tag,
...)
diff --git a/e-util/e-alert.h b/e-util/e-alert.h
index fe705c5036..e0ad92c7d2 100644
--- a/e-util/e-alert.h
+++ b/e-util/e-alert.h
@@ -61,8 +61,10 @@ typedef struct _EAlert EAlert;
typedef struct _EAlertClass EAlertClass;
typedef struct _EAlertPrivate EAlertPrivate;
-struct _e_alert_button {
- struct _e_alert_button *next;
+typedef struct _EAlertButton EAlertButton;
+
+struct _EAlertButton {
+ EAlertButton *next;
const gchar *stock;
const gchar *label;
gint response;
@@ -75,6 +77,10 @@ struct _EAlert {
struct _EAlertClass {
GObjectClass parent_class;
+
+ /* Signals */
+ void (*response) (EAlert *alert,
+ gint response_id);
};
GType e_alert_get_type (void);
@@ -96,10 +102,12 @@ void e_alert_set_primary_text (EAlert *alert,
const gchar * e_alert_get_secondary_text (EAlert *alert);
void e_alert_set_secondary_text (EAlert *alert,
const gchar *secondary_text);
-struct _e_alert_button *
- e_alert_peek_buttons (EAlert *alert);
+const gchar * e_alert_get_stock_id (EAlert *alert);
+EAlertButton * e_alert_peek_buttons (EAlert *alert);
GtkWidget * e_alert_create_image (EAlert *alert,
GtkIconSize size);
+void e_alert_response (EAlert *alert,
+ gint response_id);
void e_alert_submit (GtkWidget *widget,
const gchar *tag,
diff --git a/e-util/e-marshal.list b/e-util/e-marshal.list
index 9cc975f3b4..d4e6f43f15 100644
--- a/e-util/e-marshal.list
+++ b/e-util/e-marshal.list
@@ -20,6 +20,7 @@ INT:INT,INT,BOXED
INT:INT,POINTER,INT,BOXED
INT:OBJECT,BOXED
INT:POINTER
+NONE:ENUM,OBJECT,OBJECT
NONE:INT,INT
NONE:INT,INT,BOXED
NONE:INT,INT,OBJECT
diff --git a/mail/Makefile.am b/mail/Makefile.am
index f30de3eea9..6b1615ec91 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -45,6 +45,7 @@ mailinclude_HEADERS = \
e-mail-backend.h \
e-mail-browser.h \
e-mail-display.h \
+ e-mail-folder-utils.h \
e-mail-label-action.h \
e-mail-label-dialog.h \
e-mail-label-list-store.h \
@@ -55,6 +56,7 @@ mailinclude_HEADERS = \
e-mail-reader.h \
e-mail-reader-utils.h \
e-mail-session.h \
+ e-mail-session-utils.h \
e-mail-sidebar.h \
e-mail-store.h \
e-mail-tag-editor.h \
@@ -114,6 +116,7 @@ libevolution_mail_la_SOURCES = \
e-mail-backend.c \
e-mail-browser.c \
e-mail-display.c \
+ e-mail-folder-utils.c \
e-mail-label-action.c \
e-mail-label-dialog.c \
e-mail-label-list-store.c \
@@ -124,6 +127,7 @@ libevolution_mail_la_SOURCES = \
e-mail-reader.c \
e-mail-reader-utils.c \
e-mail-session.c \
+ e-mail-session-utils.c \
e-mail-sidebar.c \
e-mail-store.c \
e-mail-tag-editor.c \
diff --git a/mail/e-mail-folder-utils.c b/mail/e-mail-folder-utils.c
new file mode 100644
index 0000000000..7186589849
--- /dev/null
+++ b/mail/e-mail-folder-utils.c
@@ -0,0 +1,142 @@
+/*
+ * e-mail-folder-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-folder-utils.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution " VERSION SUB_VERSION " " VERSION_COMMENT)
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ GCancellable *cancellable;
+ gchar *message_uid;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->cancellable != NULL)
+ g_object_unref (context->cancellable);
+
+ g_free (context->message_uid);
+
+ g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_folder_append_message_ready (CamelFolder *folder,
+ GAsyncResult *result,
+ GSimpleAsyncResult *simple)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ camel_folder_append_message_finish (
+ folder, result, &context->message_uid, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ camel_operation_pop_message (context->cancellable);
+
+ g_simple_async_result_complete (simple);
+
+ g_object_unref (simple);
+}
+
+void
+e_mail_folder_append_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+ CamelMedium *medium;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ medium = CAMEL_MEDIUM (message);
+
+ context = g_slice_new0 (AsyncContext);
+
+ if (G_IS_CANCELLABLE (cancellable))
+ context->cancellable = g_object_ref (cancellable);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_append_message);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ camel_operation_push_message (
+ context->cancellable,
+ _("Saving message to folder '%s'"),
+ camel_folder_get_full_name (folder));
+
+ if (camel_medium_get_header (medium, "X-Mailer") == NULL)
+ camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+ camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
+
+ camel_folder_append_message (
+ folder, message, info, io_priority,
+ context->cancellable, (GAsyncReadyCallback)
+ mail_folder_append_message_ready, simple);
+}
+
+gboolean
+e_mail_folder_append_message_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **appended_uid,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_append_message), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ if (appended_uid != NULL) {
+ *appended_uid = context->message_uid;
+ context->message_uid = NULL;
+ }
+
+ return TRUE;
+}
diff --git a/mail/e-mail-folder-utils.h b/mail/e-mail-folder-utils.h
new file mode 100644
index 0000000000..c5d3fefe13
--- /dev/null
+++ b/mail/e-mail-folder-utils.h
@@ -0,0 +1,43 @@
+/*
+ * e-mail-folder-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_FOLDER_UTILS_H
+#define E_MAIL_FOLDER_UTILS_H
+
+/* CamelFolder wrappers with Evolution-specific policies. */
+
+#include <camel/camel.h>
+
+G_BEGIN_DECLS
+
+void e_mail_folder_append_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_folder_append_message_finish
+ (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **appended_uid,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_FOLDER_UTILS_H */
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 0e28d06c88..184cc757df 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -462,9 +462,12 @@ check_close_browser_reader (EMailReader *reader)
GtkWindow *parent;
gint response;
EShell *shell;
+ EMailBackend *backend;
EShellBackend *shell_backend;
- shell_backend = e_mail_reader_get_shell_backend (reader);
+ backend = e_mail_reader_get_backend (reader);
+
+ shell_backend = E_SHELL_BACKEND (backend);
shell = e_shell_backend_get_shell (shell_backend);
parent = e_shell_get_active_window (shell);
@@ -714,16 +717,20 @@ action_mail_message_edit_cb (GtkAction *action,
EMailBackend *backend;
EShellBackend *shell_backend;
CamelFolder *folder;
+ const gchar *folder_uri;
GPtrArray *uids;
+ gboolean replace;
backend = e_mail_reader_get_backend (reader);
folder = e_mail_reader_get_folder (reader);
+ folder_uri = e_mail_reader_get_folder_uri (reader);
uids = e_mail_reader_get_selected_uids (reader);
shell_backend = E_SHELL_BACKEND (backend);
shell = e_shell_backend_get_shell (shell_backend);
- em_utils_edit_messages (shell, folder, uids, FALSE);
+ replace = em_utils_folder_is_drafts (folder, folder_uri);
+ em_utils_edit_messages (shell, folder, uids, replace);
}
static void
diff --git a/mail/e-mail-session-utils.c b/mail/e-mail-session-utils.c
new file mode 100644
index 0000000000..e9cdd83914
--- /dev/null
+++ b/mail/e-mail-session-utils.c
@@ -0,0 +1,846 @@
+/*
+ * e-mail-session-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-mail-session-utils.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <mail/mail-tools.h>
+#include <mail/e-mail-local.h>
+#include <e-util/e-account-utils.h>
+#include <filter/e-filter-rule.h>
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution " VERSION SUB_VERSION " " VERSION_COMMENT)
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ CamelFolder *sent_folder;
+ CamelFolder *outbox_folder;
+
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+
+ CamelAddress *from;
+ CamelAddress *recipients;
+
+ CamelFilterDriver *driver;
+
+ GCancellable *cancellable;
+ gint io_priority;
+
+ /* X-Evolution headers */
+ struct _camel_header_raw *xev;
+
+ GPtrArray *post_to_uris;
+
+ gchar *destination;
+ gchar *message_uid;
+ gchar *transport_uri;
+ gchar *sent_folder_uri;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->sent_folder != NULL)
+ g_object_unref (context->sent_folder);
+
+ if (context->outbox_folder != NULL)
+ g_object_unref (context->outbox_folder);
+
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->info != NULL)
+ camel_message_info_free (context->info);
+
+ if (context->from != NULL)
+ g_object_unref (context->from);
+
+ if (context->recipients != NULL)
+ g_object_unref (context->recipients);
+
+ if (context->driver != NULL)
+ g_object_unref (context->driver);
+
+ if (context->cancellable != NULL) {
+ camel_operation_pop_message (context->cancellable);
+ g_object_unref (context->cancellable);
+ }
+
+ if (context->xev != NULL)
+ camel_header_raw_clear (&context->xev);
+
+ if (context->post_to_uris != NULL) {
+ g_ptr_array_foreach (
+ context->post_to_uris, (GFunc) g_free, NULL);
+ g_ptr_array_free (context->post_to_uris, TRUE);
+ }
+
+ g_free (context->destination);
+ g_free (context->message_uid);
+ g_free (context->transport_uri);
+ g_free (context->sent_folder_uri);
+
+ g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_session_handle_draft_headers_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_handle_draft_headers_sync (
+ session, context->message, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_session_handle_draft_headers_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelMedium *medium;
+ const gchar *folder_uri;
+ const gchar *message_uid;
+ const gchar *header_name;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ header_name = "X-Evolution-Draft-Folder";
+ folder_uri = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Draft-Message";
+ message_uid = camel_medium_get_header (medium, header_name);
+
+ /* Don't report errors about missing X-Evolution-Draft
+ * headers. These headers are optional, so their absence
+ * is handled by doing nothing. */
+ if (folder_uri == NULL || message_uid == NULL)
+ return TRUE;
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, error);
+
+ if (folder == NULL)
+ return FALSE;
+
+ camel_folder_set_message_flags (
+ folder, message_uid,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
+
+ success = camel_folder_synchronize_message_sync (
+ folder, message_uid, cancellable, error);
+
+ g_object_unref (folder);
+
+ return success;
+}
+
+void
+e_mail_session_handle_draft_headers (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_handle_draft_headers);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_handle_draft_headers_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_draft_headers_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_handle_draft_headers), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_handle_source_headers_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_session_handle_source_headers_sync (
+ session, context->message, cancellable, &error);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+}
+
+gboolean
+e_mail_session_handle_source_headers_sync (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelMedium *medium;
+ CamelMessageFlags flags = 0;
+ const gchar *folder_uri;
+ const gchar *message_uid;
+ const gchar *flag_string;
+ const gchar *header_name;
+ gboolean success;
+ guint length, ii;
+ gchar **tokens;
+ gchar *string;
+
+ g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ header_name = "X-Evolution-Source-Folder";
+ folder_uri = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Source-Message";
+ message_uid = camel_medium_get_header (medium, header_name);
+
+ header_name = "X-Evolution-Source-Flags";
+ flag_string = camel_medium_get_header (medium, header_name);
+
+ /* Don't report errors about missing X-Evolution-Source
+ * headers. These headers are optional, so their absence
+ * is handled by doing nothing. */
+ if (folder_uri == NULL || message_uid == NULL || flag_string == NULL)
+ return TRUE;
+
+ /* Convert the flag string to CamelMessageFlags. */
+
+ string = g_strstrip (g_strdup (flag_string));
+ tokens = g_strsplit (string, " ", 0);
+ g_free (string);
+
+ /* If tokens is NULL, a length of 0 will skip the loop. */
+ length = (tokens != NULL) ? g_strv_length (tokens) : 0;
+
+ for (ii = 0; ii < length; ii++) {
+ /* Note: We're only checking for flags known to
+ * be used in X-Evolution-Source-Flags headers.
+ * Add more as needed. */
+ if (g_strcmp0 (tokens[ii], "ANSWERED") == 0)
+ flags |= CAMEL_MESSAGE_ANSWERED;
+ else if (g_strcmp0 (tokens[ii], "ANSWERED_ALL") == 0)
+ flags |= CAMEL_MESSAGE_ANSWERED_ALL;
+ else if (g_strcmp0 (tokens[ii], "FORWARDED") == 0)
+ flags |= CAMEL_MESSAGE_FORWARDED;
+ else if (g_strcmp0 (tokens[ii], "SEEN") == 0)
+ flags |= CAMEL_MESSAGE_SEEN;
+ else
+ g_warning (
+ "Unknown flag '%s' in %s",
+ tokens[ii], header_name);
+ }
+
+ g_strfreev (tokens);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, error);
+
+ if (folder == NULL)
+ return FALSE;
+
+ camel_folder_set_message_flags (
+ folder, message_uid, flags, flags);
+
+ success = camel_folder_synchronize_message_sync (
+ folder, message_uid, cancellable, error);
+
+ g_object_unref (folder);
+
+ return success;
+}
+
+void
+e_mail_session_handle_source_headers (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback, user_data,
+ e_mail_session_handle_source_headers);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_handle_source_headers_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_session_handle_source_headers_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_handle_draft_headers), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_session_send_to_thread (GSimpleAsyncResult *simple,
+ EMailSession *session,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ CamelFolder *local_sent_folder;
+ GString *error_messages;
+ gboolean copy_to_sent = TRUE;
+ guint ii;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ /* Send the message to all recipients. */
+ if (camel_address_length (context->recipients) > 0) {
+ CamelTransport *transport;
+ CamelProviderFlags flags;
+
+ /* XXX This API does not allow for cancellation. */
+ transport = camel_session_get_transport (
+ CAMEL_SESSION (session),
+ context->transport_uri, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (transport == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_TRANSPORT (transport));
+
+ flags = CAMEL_SERVICE (transport)->provider->flags;
+ if (flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)
+ copy_to_sent = FALSE;
+
+ camel_transport_send_to_sync (
+ transport, context->message,
+ context->from, context->recipients,
+ cancellable, &error);
+
+ g_object_unref (transport);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ /* Post the message to requested folders. */
+ for (ii = 0; ii < context->post_to_uris->len; ii++) {
+ CamelFolder *folder;
+ const gchar *folder_uri;
+
+ folder_uri = g_ptr_array_index (context->post_to_uris, ii);
+
+ folder = e_mail_session_uri_to_folder_sync (
+ session, folder_uri, 0, cancellable, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (folder == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+ camel_folder_append_message_sync (
+ folder, context->message, context->info,
+ NULL, cancellable, &error);
+
+ g_object_unref (folder);
+
+ if (error != NULL) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ /*** Post Processing ***/
+
+ /* This accumulates error messages during post-processing. */
+ error_messages = g_string_sized_new (256);
+
+ mail_tool_restore_xevolution_headers (context->message, context->xev);
+
+ /* Run filters on the outgoing message. */
+ if (context->driver != NULL) {
+ camel_filter_driver_filter_message (
+ context->driver, context->message, context->info,
+ NULL, NULL, NULL, "", cancellable, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (error != NULL) {
+ g_string_append_printf (
+ error_messages,
+ _("Failed to apply outgoing filters: %s"),
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (!copy_to_sent)
+ goto cleanup;
+
+ /* Append the sent message to a Sent folder. */
+
+ local_sent_folder = e_mail_local_get_folder (E_MAIL_FOLDER_SENT);
+
+ /* Try to extract a CamelFolder from the Sent folder URI. */
+ if (context->sent_folder_uri != NULL) {
+ context->sent_folder = e_mail_session_uri_to_folder_sync (
+ session, context->sent_folder_uri, 0,
+ cancellable, &error);
+ if (error != NULL) {
+ g_warn_if_fail (context->sent_folder == NULL);
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ context->sent_folder_uri, error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ /* Fall back to the local Sent folder. */
+ if (context->sent_folder == NULL)
+ context->sent_folder = g_object_ref (local_sent_folder);
+
+ /* Append the message. */
+ camel_folder_append_message_sync (
+ context->sent_folder, context->message,
+ context->info, NULL, cancellable, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ if (error == NULL)
+ goto cleanup;
+
+ /* If appending to a remote Sent folder failed,
+ * try appending to the local Sent folder. */
+ if (context->sent_folder != local_sent_folder) {
+ const gchar *description;
+
+ description = camel_folder_get_description (
+ context->sent_folder);
+
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to %s: %s\n"
+ "Appending to local 'Sent' folder instead."),
+ description, error->message);
+ g_clear_error (&error);
+
+ camel_folder_append_message_sync (
+ local_sent_folder, context->message,
+ context->info, NULL, cancellable, &error);
+ }
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto exit;
+
+ /* We can't even append to the local Sent folder?
+ * In that case just leave the message in Outbox. */
+ if (error != NULL) {
+ if (error_messages->len > 0)
+ g_string_append (error_messages, "\n\n");
+ g_string_append_printf (
+ error_messages,
+ _("Failed to append to local 'Sent' folder: %s"),
+ error->message);
+ g_clear_error (&error);
+ goto exit;
+ }
+
+cleanup:
+
+ /* The send operation was successful; ignore cleanup errors. */
+
+ /* Mark the Outbox message for deletion. */
+ camel_folder_set_message_flags (
+ context->outbox_folder, context->message_uid,
+ CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN, ~0);
+
+ /* Synchronize the Outbox folder. */
+ camel_folder_synchronize_sync (
+ context->outbox_folder, FALSE, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* Mark the draft message for deletion, if present. */
+ e_mail_session_handle_draft_headers_sync (
+ session, context->message, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* Set flags on the original source message, if present.
+ * Source message refers to the message being forwarded
+ * or replied to. */
+ e_mail_session_handle_source_headers_sync (
+ session, context->message, cancellable, &error);
+ if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+exit:
+
+ /* If we were cancelled, disregard any other errors. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+
+ /* Stuff the accumulated error messages in a GError. */
+ } else if (error_messages->len > 0) {
+ g_simple_async_result_set_error (
+ simple, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ "%s", error_messages->str);
+ }
+
+ /* Synchronize the Sent folder. */
+ if (context->sent_folder != NULL)
+ camel_folder_synchronize_sync (
+ context->sent_folder, FALSE, cancellable, NULL);
+
+ g_string_free (error_messages, TRUE);
+}
+
+static void
+mail_session_send_to_prepare (CamelFolder *outbox_folder,
+ GAsyncResult *result,
+ GSimpleAsyncResult *simple)
+{
+ AsyncContext *context;
+ CamelAddress *from;
+ CamelAddress *recipients;
+ CamelMedium *medium;
+ CamelMessageInfo *info;
+ CamelMimeMessage *message;
+ EAccount *account = NULL;
+ GPtrArray *post_to_uris;
+ struct _camel_header_raw *xev;
+ struct _camel_header_raw *header;
+ const gchar *string;
+ const gchar *resent_from;
+ gchar *transport_uri = NULL;
+ gchar *sent_folder_uri = NULL;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ message = camel_folder_get_message_finish (
+ outbox_folder, result, &error);
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ medium = CAMEL_MEDIUM (message);
+
+ camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+ xev = mail_tool_remove_xevolution_headers (message);
+
+ /* Extract directives from X-Evolution headers. */
+
+ string = camel_header_raw_find (&xev, "X-Evolution-Account", NULL);
+ if (string != NULL) {
+ gchar *account_uid;
+
+ account_uid = g_strstrip (g_strdup (string));
+ account = e_get_account_by_uid (account_uid);
+ g_free (account_uid);
+ }
+
+ if (account != NULL) {
+ if (account->transport != NULL)
+ transport_uri = g_strdup (account->transport->url);
+ sent_folder_uri = g_strdup (account->sent_folder_uri);
+ }
+
+ string = camel_header_raw_find (&xev, "X-Evolution-Transport", NULL);
+ if (transport_uri == NULL && string != NULL)
+ transport_uri = g_strstrip (g_strdup (string));
+
+ string = camel_header_raw_find (&xev, "X-Evolution-Fcc", NULL);
+ if (sent_folder_uri == NULL && string != NULL)
+ sent_folder_uri = g_strstrip (g_strdup (string));
+
+ if (transport_uri == NULL)
+ transport_uri = g_strdup (context->destination);
+
+ post_to_uris = g_ptr_array_new ();
+ for (header = xev; header != NULL; header = header->next) {
+ gchar *folder_uri;
+
+ if (g_strcmp0 (header->name, "X-Evolution-PostTo") != 0)
+ continue;
+
+ folder_uri = g_strstrip (g_strdup (header->name));
+ g_ptr_array_add (post_to_uris, folder_uri);
+ }
+
+ /* Collect sender and recipients from headers. */
+
+ from = (CamelAddress *) camel_internet_address_new ();
+ recipients = (CamelAddress *) camel_internet_address_new ();
+ resent_from = camel_medium_get_header (medium, "Resent-From");
+
+ if (resent_from != NULL) {
+ const CamelInternetAddress *addr;
+ const gchar *type;
+
+ camel_address_decode (from, resent_from);
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_RESENT_BCC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ } else {
+ const CamelInternetAddress *addr;
+ const gchar *type;
+
+ addr = camel_mime_message_get_from (message);
+ camel_address_copy (from, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_TO;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_CC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+
+ type = CAMEL_RECIPIENT_TYPE_BCC;
+ addr = camel_mime_message_get_recipients (message, type);
+ camel_address_cat (recipients, CAMEL_ADDRESS (addr));
+ }
+
+ /* Miscellaneous preparations. */
+
+ info = camel_message_info_new (NULL);
+ camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+ /* The rest of the processing happens in a thread. */
+
+ context->from = from;
+ context->recipients = recipients;
+ context->message = g_object_ref (message);
+ context->info = info;
+ context->xev = xev;
+ context->post_to_uris = post_to_uris;
+ context->transport_uri = transport_uri;
+ context->sent_folder_uri = sent_folder_uri;
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ mail_session_send_to_thread,
+ context->io_priority,
+ context->cancellable);
+
+ g_object_unref (simple);
+}
+
+void
+e_mail_session_send_to (EMailSession *session,
+ CamelFolder *outbox_folder,
+ const gchar *message_uid,
+ const gchar *destination,
+ gint io_priority,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder_func,
+ gpointer get_folder_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_MAIL_SESSION (session));
+ g_return_if_fail (CAMEL_IS_FOLDER (outbox_folder));
+ g_return_if_fail (message_uid != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->outbox_folder = g_object_ref (outbox_folder);
+ context->message_uid = g_strdup (message_uid);
+ context->destination = g_strdup (destination);
+ context->io_priority = io_priority;
+
+ if (G_IS_CANCELLABLE (cancellable))
+ context->cancellable = g_object_ref (cancellable);
+
+ /* More convenient to do this here than in the prepare function.
+ * Failure here emits a runtime warning but is non-fatal. */
+ context->driver = camel_session_get_filter_driver (
+ CAMEL_SESSION (session), E_FILTER_SOURCE_OUTGOING, &error);
+ if (context->driver != NULL)
+ camel_filter_driver_set_folder_func (
+ context->driver, get_folder_func, get_folder_data);
+ if (error != NULL) {
+ g_warn_if_fail (context->driver == NULL);
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (session), callback,
+ user_data, e_mail_session_send_to);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ /* This gets popped in async_context_free(). */
+ camel_operation_push_message (
+ context->cancellable, _("Sending message"));
+
+ camel_folder_get_message (
+ outbox_folder, message_uid, io_priority,
+ context->cancellable, (GAsyncReadyCallback)
+ mail_session_send_to_prepare, simple);
+}
+
+gboolean
+e_mail_session_send_to_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (session),
+ e_mail_session_send_to), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
diff --git a/mail/e-mail-session-utils.h b/mail/e-mail-session-utils.h
new file mode 100644
index 0000000000..fcbc2636f7
--- /dev/null
+++ b/mail/e-mail-session-utils.h
@@ -0,0 +1,76 @@
+/*
+ * e-mail-session-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_MAIL_SESSION_UTILS_H
+#define E_MAIL_SESSION_UTILS_H
+
+/* High-level operations with Evolution-specific policies. */
+
+#include <mail/e-mail-session.h>
+
+G_BEGIN_DECLS
+
+gboolean e_mail_session_handle_draft_headers_sync
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_handle_draft_headers
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_handle_draft_headers_finish
+ (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_mail_session_handle_source_headers_sync
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ GCancellable *cancellable,
+ GError **error);
+void e_mail_session_handle_source_headers
+ (EMailSession *session,
+ CamelMimeMessage *message,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_handle_source_headers_finish
+ (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+void e_mail_session_send_to (EMailSession *session,
+ CamelFolder *outbox_folder,
+ const gchar *message_uid,
+ const gchar *destination,
+ gint io_priority,
+ GCancellable *cancellable,
+ CamelFilterGetFolderFunc get_folder_func,
+ gpointer get_folder_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_mail_session_send_to_finish (EMailSession *session,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_SESSION_UTILS_H */
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c
index 11143124e0..316046eecd 100644
--- a/mail/em-composer-utils.c
+++ b/mail/em-composer-utils.c
@@ -44,8 +44,10 @@
#include "shell/e-shell.h"
+#include "e-mail-folder-utils.h"
#include "e-mail-local.h"
#include "e-mail-session.h"
+#include "e-mail-session-utils.h"
#include "em-utils.h"
#include "em-composer-utils.h"
#include "composer/e-msg-composer.h"
@@ -69,97 +71,32 @@
#define GCONF_KEY_TEMPLATE_PLACEHOLDERS "/apps/evolution/mail/template_placeholders"
-static void em_utils_composer_send_cb (EMsgComposer *composer);
-static void em_utils_composer_save_draft_cb (EMsgComposer *composer);
+typedef struct _AsyncContext AsyncContext;
-struct emcs_t {
- guint ref_count;
-
- CamelFolder *drafts_folder;
- gchar *drafts_uid;
-
- CamelFolder *folder;
- guint32 flags, set;
- gchar *uid;
+struct _AsyncContext {
+ CamelMimeMessage *message;
+ EMsgComposer *composer;
+ EActivity *activity;
+ gchar *folder_uri;
+ gchar *message_uid;
};
-static struct emcs_t *
-emcs_new (void)
-{
- struct emcs_t *emcs;
-
- emcs = g_new0 (struct emcs_t, 1);
- emcs->ref_count = 1;
-
- return emcs;
-}
-
-static void
-emcs_set_drafts_info (struct emcs_t *emcs,
- CamelFolder *drafts_folder,
- const gchar *drafts_uid)
-{
- g_return_if_fail (emcs != NULL);
- g_return_if_fail (drafts_folder != NULL);
- g_return_if_fail (drafts_uid != NULL);
-
- if (emcs->drafts_folder != NULL)
- g_object_unref (emcs->drafts_folder);
- g_free (emcs->drafts_uid);
-
- g_object_ref (drafts_folder);
- emcs->drafts_folder = drafts_folder;
- emcs->drafts_uid = g_strdup (drafts_uid);
-}
-
-static void
-emcs_set_folder_info (struct emcs_t *emcs,
- CamelFolder *folder,
- const gchar *uid,
- guint32 flags,
- guint32 set)
-{
- g_return_if_fail (emcs != NULL);
- g_return_if_fail (folder != NULL);
- g_return_if_fail (uid != NULL);
-
- if (emcs->folder != NULL)
- g_object_unref (emcs->folder);
- g_free (emcs->uid);
-
- g_object_ref (folder);
- emcs->folder = folder;
- emcs->uid = g_strdup (uid);
- emcs->flags = flags;
- emcs->set = set;
-}
-
static void
-free_emcs (struct emcs_t *emcs)
+async_context_free (AsyncContext *context)
{
- if (emcs->drafts_folder != NULL)
- g_object_unref (emcs->drafts_folder);
- g_free (emcs->drafts_uid);
+ if (context->message != NULL)
+ g_object_unref (context->message);
- if (emcs->folder != NULL)
- g_object_unref (emcs->folder);
- g_free (emcs->uid);
+ if (context->composer != NULL)
+ g_object_unref (context->composer);
- g_free (emcs);
-}
+ if (context->activity != NULL)
+ g_object_unref (context->activity);
-static void
-emcs_ref (struct emcs_t *emcs)
-{
- emcs->ref_count++;
-}
+ g_free (context->folder_uri);
+ g_free (context->message_uid);
-static void
-emcs_unref (struct emcs_t *emcs)
-{
- emcs->ref_count--;
- if (emcs->ref_count == 0)
- free_emcs (emcs);
+ g_slice_free (AsyncContext, context);
}
static gboolean
@@ -211,69 +148,6 @@ ask_confirm_for_only_bcc (EMsgComposer *composer, gboolean hidden_list_case)
hidden_list_case?"mail:ask-send-only-bcc-contact":"mail:ask-send-only-bcc", NULL);
}
-struct _send_data {
- struct emcs_t *emcs;
- EMsgComposer *composer;
- gboolean send;
-};
-
-static void
-composer_send_queued_cb (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info,
- gint queued, const gchar *appended_uid, gpointer data)
-{
- CamelSession *session;
- struct emcs_t *emcs;
- struct _send_data *send = data;
-
- emcs = send->emcs;
-
- session = e_msg_composer_get_session (send->composer);
-
- if (queued) {
- if (emcs && emcs->drafts_folder) {
- /* delete the old draft message */
- camel_folder_set_message_flags (
- emcs->drafts_folder, emcs->drafts_uid,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
- g_object_unref (emcs->drafts_folder);
- emcs->drafts_folder = NULL;
- g_free (emcs->drafts_uid);
- emcs->drafts_uid = NULL;
- }
-
- if (emcs && emcs->folder) {
- /* set any replied flags etc */
- camel_folder_set_message_flags (
- emcs->folder, emcs->uid,
- emcs->flags, emcs->set);
- camel_folder_set_message_user_flag (
- emcs->folder, emcs->uid,
- "receipt-handled", TRUE);
- g_object_unref (emcs->folder);
- emcs->folder = NULL;
- g_free (emcs->uid);
- emcs->uid = NULL;
- }
-
- gtk_widget_destroy (GTK_WIDGET (send->composer));
-
- if (send->send && camel_session_get_online (session)) {
- /* queue a message send */
- mail_send (E_MAIL_SESSION (session));
- }
- } else
- gtk_widget_show (GTK_WIDGET (send->composer));
-
- camel_message_info_free (info);
-
- if (send->emcs)
- emcs_unref (send->emcs);
-
- g_object_unref (send->composer);
- g_free (send);
-}
-
static gboolean
is_group_definition (const gchar *str)
{
@@ -286,106 +160,101 @@ is_group_definition (const gchar *str)
return colon > str && strchr (str, ';') > colon;
}
-static CamelMimeMessage *
-composer_get_message (EMsgComposer *composer, gboolean save_html_object_data)
+static gboolean
+composer_presend_check_recipients (EMsgComposer *composer)
{
- CamelMimeMessage *message = NULL;
- EDestination **recipients, **recipients_bcc;
- gboolean html_mode, send_html, confirm_html;
+ EDestination **recipients;
+ EDestination **recipients_bcc;
CamelInternetAddress *cia;
- gint hidden = 0, shown = 0;
- gint num = 0, num_bcc = 0, num_post = 0;
- const gchar *subject;
- GConfClient *gconf;
- EAccount *account;
- gint i;
- EMEvent *eme;
- EMEventTargetComposer *target;
EComposerHeaderTable *table;
EComposerHeader *post_to_header;
GString *invalid_addrs = NULL;
- GError *error = NULL;
-
- gconf = mail_config_get_gconf_client ();
- table = e_msg_composer_get_header_table (composer);
+ gboolean check_passed = FALSE;
+ gint hidden = 0;
+ gint shown = 0;
+ gint num = 0;
+ gint num_bcc = 0;
+ gint num_post = 0;
+ gint ii;
- /* We should do all of the validity checks based on the composer, and not on
- the created message, as extra interaction may occur when we get the message
- (e.g. to get a passphrase to sign a message) */
+ /* We should do all of the validity checks based on the composer,
+ * and not on the created message, as extra interaction may occur
+ * when we get the message (e.g. passphrase to sign a message). */
- /* get the message recipients */
+ table = e_msg_composer_get_header_table (composer);
recipients = e_composer_header_table_get_destinations (table);
cia = camel_internet_address_new ();
- /* see which ones are visible/present, etc */
- if (recipients) {
- for (i = 0; recipients[i] != NULL; i++) {
- const gchar *addr = e_destination_get_address (recipients[i]);
-
- if (addr && addr[0]) {
- gint len, j;
-
- camel_address_decode ((CamelAddress *) cia, addr);
- len = camel_address_length ((CamelAddress *) cia);
-
- if (len > 0) {
- if (!e_destination_is_evolution_list (recipients[i])) {
- for (j = 0; j < len; j++) {
- const gchar *name = NULL, *eml = NULL;
-
- if (!camel_internet_address_get (cia, j, &name, &eml) ||
- !eml ||
- strchr (eml, '@') <= eml) {
- if (!invalid_addrs)
- invalid_addrs = g_string_new ("");
- else
- g_string_append (invalid_addrs, ", ");
-
- if (name)
- g_string_append (invalid_addrs, name);
- if (eml) {
- g_string_append (invalid_addrs, name ? " <" : "");
- g_string_append (invalid_addrs, eml);
- g_string_append (invalid_addrs, name ? ">" : "");
- }
- }
- }
- }
+ /* See which ones are visible, present, etc. */
+ for (ii = 0; recipients != NULL && recipients[ii] != NULL; ii++) {
+ const gchar *addr;
+ gint len, j;
- camel_address_remove ((CamelAddress *) cia, -1);
- num++;
- if (e_destination_is_evolution_list (recipients[i])
- && !e_destination_list_show_addresses (recipients[i])) {
- hidden++;
- } else {
- shown++;
+ addr = e_destination_get_address (recipients[ii]);
+ if (addr == NULL || *addr == '\0')
+ continue;
+
+ camel_address_decode (CAMEL_ADDRESS (cia), addr);
+ len = camel_address_length (CAMEL_ADDRESS (cia));
+
+ if (len > 0) {
+ if (!e_destination_is_evolution_list (recipients[ii])) {
+ for (j = 0; j < len; j++) {
+ const gchar *name = NULL, *eml = NULL;
+
+ if (!camel_internet_address_get (cia, j, &name, &eml) ||
+ !eml ||
+ strchr (eml, '@') <= eml) {
+ if (!invalid_addrs)
+ invalid_addrs = g_string_new ("");
+ else
+ g_string_append (invalid_addrs, ", ");
+
+ if (name)
+ g_string_append (invalid_addrs, name);
+ if (eml) {
+ g_string_append (invalid_addrs, name ? " <" : "");
+ g_string_append (invalid_addrs, eml);
+ g_string_append (invalid_addrs, name ? ">" : "");
+ }
}
- } else if (is_group_definition (addr)) {
- /* like an address, it will not claim on only-bcc */
- shown++;
- num++;
- } else if (!invalid_addrs) {
- invalid_addrs = g_string_new (addr);
- } else {
- g_string_append (invalid_addrs, ", ");
- g_string_append (invalid_addrs, addr);
}
}
+
+ camel_address_remove (CAMEL_ADDRESS (cia), -1);
+ num++;
+ if (e_destination_is_evolution_list (recipients[ii])
+ && !e_destination_list_show_addresses (recipients[ii])) {
+ hidden++;
+ } else {
+ shown++;
+ }
+ } else if (is_group_definition (addr)) {
+ /* like an address, it will not claim on only-bcc */
+ shown++;
+ num++;
+ } else if (!invalid_addrs) {
+ invalid_addrs = g_string_new (addr);
+ } else {
+ g_string_append (invalid_addrs, ", ");
+ g_string_append (invalid_addrs, addr);
}
}
recipients_bcc = e_composer_header_table_get_destinations_bcc (table);
if (recipients_bcc) {
- for (i = 0; recipients_bcc[i] != NULL; i++) {
- const gchar *addr = e_destination_get_address (recipients_bcc[i]);
-
- if (addr && addr[0]) {
- camel_address_decode ((CamelAddress *) cia, addr);
- if (camel_address_length ((CamelAddress *) cia) > 0) {
- camel_address_remove ((CamelAddress *) cia, -1);
- num_bcc++;
- }
+ for (ii = 0; recipients_bcc[ii] != NULL; ii++) {
+ const gchar *addr;
+
+ addr = e_destination_get_address (recipients_bcc[ii]);
+ if (addr == NULL || *addr == '\0')
+ continue;
+
+ camel_address_decode (CAMEL_ADDRESS (cia), addr);
+ if (camel_address_length (CAMEL_ADDRESS (cia)) > 0) {
+ camel_address_remove (CAMEL_ADDRESS (cia), -1);
+ num_bcc++;
}
}
@@ -394,24 +263,32 @@ composer_get_message (EMsgComposer *composer, gboolean save_html_object_data)
g_object_unref (cia);
- post_to_header = e_composer_header_table_get_header (table, E_COMPOSER_HEADER_POST_TO);
+ post_to_header = e_composer_header_table_get_header (
+ table, E_COMPOSER_HEADER_POST_TO);
if (e_composer_header_get_visible (post_to_header)) {
GList *postlist;
postlist = e_composer_header_table_get_post_to (table);
num_post = g_list_length (postlist);
- g_list_foreach (postlist, (GFunc)g_free, NULL);
+ g_list_foreach (postlist, (GFunc) g_free, NULL);
g_list_free (postlist);
}
/* I'm sensing a lack of love, er, I mean recipients. */
if (num == 0 && num_post == 0) {
- e_alert_run_dialog_for_args ((GtkWindow *)composer, "mail:send-no-recipients", NULL);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail:send-no-recipients", NULL);
goto finished;
}
if (invalid_addrs) {
- if (e_alert_run_dialog_for_args ((GtkWindow *)composer, strstr (invalid_addrs->str, ", ") ? "mail:ask-send-invalid-recip-multi" : "mail:ask-send-invalid-recip-one", invalid_addrs->str, NULL) == GTK_RESPONSE_CANCEL) {
+ if (e_alert_run_dialog_for_args (
+ GTK_WINDOW (composer),
+ strstr (invalid_addrs->str, ", ") ?
+ "mail:ask-send-invalid-recip-multi" :
+ "mail:ask-send-invalid-recip-one",
+ invalid_addrs->str, NULL) == GTK_RESPONSE_CANCEL) {
g_string_free (invalid_addrs, TRUE);
goto finished;
}
@@ -425,154 +302,277 @@ composer_get_message (EMsgComposer *composer, gboolean save_html_object_data)
goto finished;
}
- html_mode = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer));
- send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL);
- confirm_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL);
+ check_passed = TRUE;
- /* Only show this warning if our default is to send html. If it isn't, we've
- manually switched into html mode in the composer and (presumably) had a good
- reason for doing this. */
- if (html_mode && send_html && confirm_html) {
+finished:
+ if (recipients != NULL)
+ e_destination_freev (recipients);
- gboolean html_problem = FALSE;
+ return check_passed;
+}
- if (recipients) {
- for (i = 0; recipients[i] != NULL && !html_problem; i++) {
- if (!e_destination_get_html_mail_pref (recipients[i]))
- html_problem = TRUE;
- }
- }
+static gboolean
+composer_presend_check_account (EMsgComposer *composer)
+{
+ EComposerHeaderTable *table;
+ EAccount *account;
+ gboolean check_passed;
- if (html_problem) {
- html_problem = !ask_confirm_for_unwanted_html_mail (composer, recipients);
- if (html_problem)
- goto finished;
- }
- }
+ table = e_msg_composer_get_header_table (composer);
+ account = e_composer_header_table_get_account (table);
+ check_passed = (account != NULL && account->enabled);
- /* Check for no subject */
- subject = e_composer_header_table_get_subject (table);
- if (subject == NULL || subject[0] == '\0') {
- if (!ask_confirm_for_empty_subject (composer))
- goto finished;
+ if (!check_passed)
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail:send-no-account-enabled", NULL);
+
+ return check_passed;
+}
+
+static gboolean
+composer_presend_check_downloads (EMsgComposer *composer)
+{
+ EAttachmentView *view;
+ EAttachmentStore *store;
+ gboolean check_passed = TRUE;
+
+ view = e_msg_composer_get_attachment_view (composer);
+ store = e_attachment_view_get_store (view);
+
+ if (e_attachment_store_get_num_loading (store) > 0) {
+ if (!em_utils_prompt_user (GTK_WINDOW (composer), NULL,
+ "mail-composer:ask-send-message-pending-download", NULL))
+ check_passed = FALSE;
}
+ return check_passed;
+}
+
+static gboolean
+composer_presend_check_plugins (EMsgComposer *composer)
+{
+ EMEvent *eme;
+ EMEventTargetComposer *target;
+ gpointer data;
+
/** @Event: composer.presendchecks
* @Title: Composer PreSend Checks
* @Target: EMEventTargetMessage
*
- * composer.presendchecks is emitted during pre-checks for the message just before sending.
- * Since the e-plugin framework doesn't provide a way to return a value from the plugin,
- * use 'presend_check_status' to set whether the check passed / failed.
+ * composer.presendchecks is emitted during pre-checks for the
+ * message just before sending. Since the e-plugin framework
+ * doesn't provide a way to return a value from the plugin,
+ * use 'presend_check_status' to set whether the check passed.
*/
eme = em_event_peek ();
target = em_event_target_new_composer (eme, composer, 0);
- g_object_set_data (G_OBJECT (composer), "presend_check_status", GINT_TO_POINTER(0));
- e_event_emit((EEvent *)eme, "composer.presendchecks", (EEventTarget *)target);
+ e_event_emit (
+ (EEvent *) eme, "composer.presendchecks",
+ (EEventTarget *) target);
- if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (composer), "presend_check_status")))
- goto finished;
+ /* A non-NULL value for this key means the check failed. */
+ data = g_object_get_data (G_OBJECT (composer), "presend_check_status");
- /* actually get the message now, this will sign/encrypt etc */
- message = e_msg_composer_get_message (
- composer, save_html_object_data, NULL, &error);
+ return (data == NULL);
+}
+
+static gboolean
+composer_presend_check_subject (EMsgComposer *composer)
+{
+ EComposerHeaderTable *table;
+ const gchar *subject;
+ gboolean check_passed = TRUE;
+
+ table = e_msg_composer_get_header_table (composer);
+ subject = e_composer_header_table_get_subject (table);
+
+ if (subject == NULL || subject[0] == '\0') {
+ if (!ask_confirm_for_empty_subject (composer))
+ check_passed = FALSE;
+ }
+
+ return check_passed;
+}
+
+static gboolean
+composer_presend_check_unwanted_html (EMsgComposer *composer)
+{
+ EDestination **recipients;
+ EComposerHeaderTable *table;
+ GConfClient *client;
+ gboolean check_passed = TRUE;
+ gboolean html_mode;
+ gboolean send_html;
+ gboolean confirm_html;
+ gint ii;
+
+ client = gconf_client_get_default ();
+
+ table = e_msg_composer_get_header_table (composer);
+ recipients = e_composer_header_table_get_destinations (table);
+ html_mode = gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer));
+
+ send_html = gconf_client_get_bool (
+ client, "/apps/evolution/mail/composer/send_html", NULL);
+ confirm_html = gconf_client_get_bool (
+ client, "/apps/evolution/mail/prompts/unwanted_html", NULL);
+
+ /* Only show this warning if our default is to send html. If it
+ * isn't, we've manually switched into html mode in the composer
+ * and (presumably) had a good reason for doing this. */
+ if (html_mode && send_html && confirm_html && recipients != NULL) {
+ gboolean html_problem = FALSE;
+
+ for (ii = 0; recipients[ii] != NULL; ii++) {
+ if (!e_destination_get_html_mail_pref (recipients[ii]))
+ html_problem = TRUE;
+ break;
+ }
+
+ if (html_problem) {
+ if (!ask_confirm_for_unwanted_html_mail (
+ composer, recipients))
+ check_passed = FALSE;
+ }
+ }
+
+ if (recipients != NULL)
+ e_destination_freev (recipients);
+
+ g_object_unref (client);
+
+ return check_passed;
+}
+
+static void
+composer_send_completed (EMailSession *session,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ GError *error = NULL;
+
+ e_mail_session_send_to_finish (session, result, &error);
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_warn_if_fail (message == NULL);
g_error_free (error);
- goto finished;
+ goto exit;
}
if (error != NULL) {
- g_warn_if_fail (message == NULL);
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
- "mail-composer:no-build-message",
+ e_alert_submit (
+ GTK_WIDGET (context->composer),
+ "mail-composer:send-error",
error->message, NULL);
g_error_free (error);
- goto finished;
- }
-
- g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
-
- /* Add info about the sending account */
- account = e_composer_header_table_get_account (table);
-
- if (account) {
- /* FIXME: Why isn't this crap just in e_msg_composer_get_message? */
- camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Account", account->uid);
- camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Transport", account->transport->url);
- camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Fcc", account->sent_folder_uri);
- if (account->id->organization && *account->id->organization) {
- gchar *org;
-
- org = camel_header_encode_string ((const guchar *)account->id->organization);
- camel_medium_set_header (CAMEL_MEDIUM (message), "Organization", org);
- g_free (org);
- }
+ goto exit;
}
- finished:
+ e_activity_complete (context->activity);
- if (recipients)
- e_destination_freev (recipients);
+ /* Wait for the EActivity's completion message to
+ * time out and then destroy the composer window. */
+ g_object_weak_ref (
+ G_OBJECT (context->activity), (GWeakNotify)
+ gtk_widget_destroy, context->composer);
- return message;
+exit:
+ async_context_free (context);
}
static void
-em_utils_composer_send_cb (EMsgComposer *composer)
+composer_send_appended (CamelFolder *outbox_folder,
+ GAsyncResult *result,
+ AsyncContext *context)
{
- EComposerHeaderTable *table;
- CamelMimeMessage *message;
- CamelMessageInfo *info;
- struct _send_data *send;
- CamelFolder *folder;
- EAccount *account;
+ CamelSession *session;
+ GCancellable *cancellable;
+ gchar *message_uid = NULL;
+ GError *error = NULL;
- table = e_msg_composer_get_header_table (composer);
- account = e_composer_header_table_get_account (table);
- if (!account || !account->enabled) {
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
- "mail:send-no-account-enabled", NULL);
+ e_mail_folder_append_message_finish (
+ outbox_folder, result, &message_uid, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message_uid == NULL);
+ async_context_free (context);
+ g_error_free (error);
return;
}
- if ((message = composer_get_message (composer, FALSE)) == NULL)
+ if (error != NULL) {
+ g_warn_if_fail (message_uid == NULL);
+ e_alert_submit (
+ GTK_WIDGET (context->composer),
+ "mail-composer:append-to-outbox-error",
+ error->message, NULL);
+ g_warning ("%s", error->message);
+ async_context_free (context);
+ g_error_free (error);
return;
+ }
- folder = e_mail_local_get_folder (E_MAIL_FOLDER_OUTBOX);
- g_object_ref (folder);
+ session = e_msg_composer_get_session (context->composer);
+ cancellable = e_activity_get_cancellable (context->activity);
- /* mail the message */
- e_msg_composer_set_mail_sent (composer, TRUE);
- info = camel_message_info_new (NULL);
+ /* If we're online, go ahead and send the message now. */
+ if (camel_session_get_online (session))
+ e_mail_session_send_to (
+ E_MAIL_SESSION (session),
+ outbox_folder, message_uid, NULL,
+ G_PRIORITY_DEFAULT, cancellable, NULL, NULL,
+ (GAsyncReadyCallback) composer_send_completed,
+ context);
- camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+ /* If we're offline, writing the message to the Outbox
+ * folder is as much as we can do. Tell the user. */
+ else {
+ g_object_unref (context->activity);
+ context->activity = NULL;
- send = g_malloc (sizeof (*send));
- send->emcs = g_object_get_data (G_OBJECT (composer), "emcs");
- if (send->emcs)
- emcs_ref (send->emcs);
- send->send = TRUE;
- send->composer = g_object_ref (composer);
- gtk_widget_hide (GTK_WIDGET (composer));
+ e_alert_run_dialog_for_args (
+ GTK_WINDOW (context->composer),
+ "mail-composer:saved-to-outbox", NULL);
- mail_append_mail (
- folder, message, info, composer_send_queued_cb, send);
+ gtk_widget_destroy (GTK_WIDGET (context->composer));
+ async_context_free (context);
+ }
- g_object_unref (folder);
- g_object_unref (message);
+ g_free (message_uid);
}
-struct _save_draft_info {
- struct emcs_t *emcs;
- EMsgComposer *composer;
+static void
+em_utils_composer_send_cb (EMsgComposer *composer,
+ CamelMimeMessage *message,
+ EActivity *activity)
+{
+ AsyncContext *context;
+ CamelFolder *outbox_folder;
CamelMessageInfo *info;
-};
+ GCancellable *cancellable;
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+ context->composer = g_object_ref (composer);
+ context->activity = g_object_ref (activity);
+
+ cancellable = e_activity_get_cancellable (activity);
+ outbox_folder = e_mail_local_get_folder (E_MAIL_FOLDER_OUTBOX);
+
+ info = camel_message_info_new (NULL);
+ camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
+
+ e_mail_folder_append_message (
+ outbox_folder, message, info,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) composer_send_appended,
+ context);
+
+ camel_message_info_free (info);
+}
static void
composer_set_no_change (EMsgComposer *composer)
@@ -588,172 +588,211 @@ composer_set_no_change (EMsgComposer *composer)
}
static void
-save_draft_done (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, gint ok,
- const gchar *appended_uid, gpointer user_data)
+composer_save_draft_complete (EMailSession *session,
+ GAsyncResult *result,
+ AsyncContext *context)
{
- struct _save_draft_info *sdi = user_data;
- struct emcs_t *emcs;
+ GError *error = NULL;
- if (!ok)
- goto done;
+ /* We don't really care if this failed. If something other than
+ * cancellation happened, emit a runtime warning so the error is
+ * not completely lost. */
- if ((emcs = sdi->emcs) == NULL)
- emcs = emcs_new ();
+ e_mail_session_handle_draft_headers_finish (session, result, &error);
- if (emcs->drafts_folder) {
- /* delete the original draft message */
- camel_folder_set_message_flags (
- emcs->drafts_folder, emcs->drafts_uid,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN,
- CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN);
- g_object_unref (emcs->drafts_folder);
- emcs->drafts_folder = NULL;
- g_free (emcs->drafts_uid);
- emcs->drafts_uid = NULL;
- }
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_error_free (error);
- if (emcs->folder) {
- /* set the replied flags etc */
- camel_folder_set_message_flags (
- emcs->folder, emcs->uid,
- emcs->flags, emcs->set);
- g_object_unref (emcs->folder);
- emcs->folder = NULL;
- g_free (emcs->uid);
- emcs->uid = NULL;
+ else if (error != NULL) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
}
- if (appended_uid) {
- g_object_ref (folder);
- emcs->drafts_folder = folder;
- emcs->drafts_uid = g_strdup (appended_uid);
- }
+ /* Encode the draft message we just saved into the EMsgComposer
+ * as X-Evolution-Draft headers. The message will be marked for
+ * deletion if the user saves a newer draft message or sends the
+ * composed message. */
+ e_msg_composer_set_draft_headers (
+ context->composer, context->folder_uri,
+ context->message_uid);
- if (e_msg_composer_is_exiting (sdi->composer))
- gtk_widget_destroy (GTK_WIDGET (sdi->composer));
+ e_activity_complete (context->activity);
- done:
- g_object_unref (sdi->composer);
- if (sdi->emcs)
- emcs_unref (sdi->emcs);
- camel_message_info_free (info);
- g_free (sdi);
+ async_context_free (context);
}
static void
-save_draft_folder (gchar *uri, CamelFolder *folder, gpointer data)
+composer_save_draft_cleanup (CamelFolder *drafts_folder,
+ GAsyncResult *result,
+ AsyncContext *context)
{
- CamelFolder **save = data;
+ CamelSession *session;
+ GCancellable *cancellable;
+ GError *error = NULL;
+
+ e_mail_folder_append_message_finish (
+ drafts_folder, result, &context->message_uid, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (context->message_uid == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
- if (folder) {
- *save = folder;
- g_object_ref (folder);
+ if (error != NULL) {
+ g_warn_if_fail (context->message_uid == NULL);
+ e_alert_submit (
+ GTK_WIDGET (context->composer),
+ "mail-composer:save-draft-error",
+ error->message, NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
}
+
+ session = e_msg_composer_get_session (context->composer);
+ cancellable = e_activity_get_cancellable (context->activity);
+
+ /* Mark the previously saved draft message for deletion.
+ * Note: This is just a nice-to-have; ignore failures. */
+ e_mail_session_handle_draft_headers (
+ E_MAIL_SESSION (session), context->message,
+ G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)
+ composer_save_draft_complete, context);
}
static void
-em_utils_composer_save_draft_cb (EMsgComposer *composer)
+composer_save_draft_append_mail (AsyncContext *context,
+ CamelFolder *drafts_folder)
{
CamelFolder *local_drafts_folder;
- EComposerHeaderTable *table;
- struct _save_draft_info *sdi;
- const gchar *local_drafts_folder_uri;
- CamelFolder *folder = NULL;
- CamelMimeMessage *msg;
+ GCancellable *cancellable;
CamelMessageInfo *info;
- CamelSession *session;
- EAccount *account;
- GError *error = NULL;
-
- /* need to get stuff from the composer here, since it could
- * get destroyed while we're in mail_msg_wait() a little lower
- * down, waiting for the folder to open */
-
- session = e_msg_composer_get_session (composer);
local_drafts_folder =
e_mail_local_get_folder (E_MAIL_FOLDER_DRAFTS);
- local_drafts_folder_uri =
- e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS);
- msg = e_msg_composer_get_message_draft (composer, NULL, &error);
+ if (drafts_folder == NULL)
+ drafts_folder = g_object_ref (local_drafts_folder);
+
+ cancellable = e_activity_get_cancellable (context->activity);
+
+ info = camel_message_info_new (NULL);
+
+ camel_message_info_set_flags (
+ info, CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN, ~0);
+
+ e_mail_folder_append_message (
+ drafts_folder, context->message,
+ info, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) composer_save_draft_cleanup,
+ context);
+
+ camel_message_info_free (info);
+
+ g_object_unref (drafts_folder);
+}
+
+static void
+composer_save_draft_got_folder (EMailSession *session,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelFolder *drafts_folder;
+ GError *error = NULL;
+
+ drafts_folder = e_mail_session_uri_to_folder_finish (
+ session, result, &error);
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_warn_if_fail (msg == NULL);
+ g_warn_if_fail (drafts_folder == NULL);
+ async_context_free (context);
g_error_free (error);
return;
}
if (error != NULL) {
- g_warn_if_fail (msg == NULL);
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
- "mail-composer:no-build-message",
- error->message, NULL);
+ gint response;
+
+ g_warn_if_fail (drafts_folder == NULL);
+
+ /* XXX Not showing the error message in the dialog? */
g_error_free (error);
- return;
+
+ /* If we can't retrieve the Drafts folder for the
+ * selected account, ask the user if he wants to
+ * save to the local Drafts folder instead. */
+ response = e_alert_run_dialog_for_args (
+ GTK_WINDOW (context->composer),
+ "mail:ask-default-drafts", NULL);
+ if (response != GTK_RESPONSE_YES) {
+ async_context_free (context);
+ return;
+ }
}
- g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg));
+ composer_save_draft_append_mail (context, drafts_folder);
+}
+
+static void
+em_utils_composer_save_draft_cb (EMsgComposer *composer,
+ CamelMimeMessage *message,
+ EActivity *activity)
+{
+ AsyncContext *context;
+ EComposerHeaderTable *table;
+ const gchar *drafts_folder_uri = NULL;
+ const gchar *local_drafts_folder_uri;
+ CamelSession *session;
+ EAccount *account;
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+ context->composer = g_object_ref (composer);
+ context->activity = g_object_ref (activity);
+
+ session = e_msg_composer_get_session (composer);
+
+ local_drafts_folder_uri =
+ e_mail_local_get_folder_uri (E_MAIL_FOLDER_DRAFTS);
table = e_msg_composer_get_header_table (composer);
account = e_composer_header_table_get_account (table);
- sdi = g_malloc (sizeof (struct _save_draft_info));
- sdi->composer = g_object_ref (composer);
- sdi->emcs = g_object_get_data (G_OBJECT (composer), "emcs");
- if (sdi->emcs)
- emcs_ref (sdi->emcs);
+ if (account != NULL && account->enabled)
+ drafts_folder_uri = account->drafts_folder_uri;
- if (account && account->drafts_folder_uri &&
- strcmp (account->drafts_folder_uri, local_drafts_folder_uri) != 0) {
- gint id;
+ if (g_strcmp0 (drafts_folder_uri, local_drafts_folder_uri) == 0)
+ drafts_folder_uri = NULL;
- id = mail_get_folder (
- E_MAIL_SESSION (session),
- account->drafts_folder_uri, 0,
- save_draft_folder, &folder,
- mail_msg_unordered_push);
- mail_msg_wait (id);
-
- if (!folder || !account->enabled) {
- if (e_alert_run_dialog_for_args ((GtkWindow *)composer, "mail:ask-default-drafts", NULL) != GTK_RESPONSE_YES) {
- g_object_unref (composer);
- g_object_unref (msg);
- if (sdi->emcs)
- emcs_unref (sdi->emcs);
- g_free (sdi);
- return;
- }
-
- folder = local_drafts_folder;
- g_object_ref (local_drafts_folder);
- }
+ if (drafts_folder_uri == NULL) {
+ composer_save_draft_append_mail (context, NULL);
+ context->folder_uri = g_strdup (local_drafts_folder_uri);
} else {
- folder = local_drafts_folder;
- g_object_ref (folder);
- }
-
- info = camel_message_info_new (NULL);
+ GCancellable *cancellable;
- camel_message_info_set_flags (
- info, CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN, ~0);
+ cancellable = e_activity_get_cancellable (activity);
+ context->folder_uri = g_strdup (drafts_folder_uri);
- mail_append_mail (folder, msg, info, save_draft_done, sdi);
- g_object_unref (folder);
- g_object_unref (msg);
+ e_mail_session_uri_to_folder (
+ E_MAIL_SESSION (session),
+ drafts_folder_uri, 0, G_PRIORITY_DEFAULT,
+ cancellable, (GAsyncReadyCallback)
+ composer_save_draft_got_folder, context);
+ }
}
static void
em_utils_composer_print_cb (EMsgComposer *composer,
- GtkPrintOperationAction action)
+ GtkPrintOperationAction action,
+ CamelMimeMessage *message,
+ EActivity *activity)
{
- CamelMimeMessage *message;
EMFormatHTMLPrint *efhp;
- message = e_msg_composer_get_message_print (composer, 1, NULL);
-
efhp = em_format_html_print_new (NULL, action);
em_format_html_print_raw_message (efhp, message);
g_object_unref (efhp);
@@ -998,14 +1037,14 @@ traverse_parts (GSList *clues, CamelMimeMessage *message, CamelDataWrapper *cont
static GtkWidget *
edit_message (EShell *shell,
+ CamelFolder *folder,
CamelMimeMessage *message,
- CamelFolder *drafts,
- const gchar *uid)
+ const gchar *message_uid)
{
EMsgComposer *composer;
/* Template specific code follows. */
- if (em_utils_folder_is_templates (drafts, NULL) == TRUE) {
+ if (em_utils_folder_is_templates (folder, NULL)) {
GConfClient *gconf;
GSList *clue_list = NULL;
@@ -1022,11 +1061,14 @@ edit_message (EShell *shell,
composer = e_msg_composer_new_with_message (shell, message, NULL);
- if (em_utils_folder_is_drafts (drafts, NULL)) {
- struct emcs_t *emcs;
+ if (message_uid != NULL) {
+ const gchar *folder_uri;
+
+ folder_uri = camel_folder_get_uri (folder);
- emcs = g_object_get_data (G_OBJECT (composer), "emcs");
- emcs_set_drafts_info (emcs, drafts, uid);
+ if (em_utils_folder_is_drafts (folder, folder_uri))
+ e_msg_composer_set_draft_headers (
+ composer, folder_uri, message_uid);
}
composer_set_no_change (composer);
@@ -1039,21 +1081,22 @@ edit_message (EShell *shell,
/**
* em_utils_edit_message:
* @shell: an #EShell
- * @message: message to edit
- * @folder: used to recognize the templates folder
+ * @folder: a #CamelFolder
+ * @message: a #CamelMimeMessage
*
* Opens a composer filled in with the headers/mime-parts/etc of
* @message.
**/
GtkWidget *
em_utils_edit_message (EShell *shell,
- CamelMimeMessage *message,
- CamelFolder *folder)
+ CamelFolder *folder,
+ CamelMimeMessage *message)
{
g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- return edit_message (shell, message, folder, NULL);
+ return edit_message (shell, folder, message, NULL);
}
static void
@@ -1071,7 +1114,7 @@ edit_messages_replace (CamelFolder *folder,
for (ii = 0; ii < msgs->len; ii++) {
camel_medium_remove_header (
CAMEL_MEDIUM (msgs->pdata[ii]), "X-Mailer");
- edit_message (shell, msgs->pdata[ii], folder, uids->pdata[ii]);
+ edit_message (shell, folder, msgs->pdata[ii], uids->pdata[ii]);
}
g_object_unref (shell);
@@ -1092,7 +1135,7 @@ edit_messages_no_replace (CamelFolder *folder,
for (ii = 0; ii < msgs->len; ii++) {
camel_medium_remove_header (
CAMEL_MEDIUM (msgs->pdata[ii]), "X-Mailer");
- edit_message (shell, msgs->pdata[ii], NULL, NULL);
+ edit_message (shell, NULL, msgs->pdata[ii], NULL);
}
g_object_unref (shell);
@@ -1300,6 +1343,7 @@ forward_non_attached (EShell *shell,
{
CamelMimeMessage *message;
EMsgComposer *composer = NULL;
+ const gchar *folder_uri;
gchar *subject, *text;
gint i;
guint32 flags;
@@ -1307,6 +1351,8 @@ forward_non_attached (EShell *shell,
if (messages->len == 0)
return NULL;
+ folder_uri = camel_folder_get_uri (folder);
+
flags = EM_FORMAT_QUOTE_HEADERS | EM_FORMAT_QUOTE_KEEP_SIG;
if (style == MAIL_CONFIG_FORWARD_QUOTED)
flags |= EM_FORMAT_QUOTE_CITE;
@@ -1329,12 +1375,11 @@ forward_non_attached (EShell *shell,
e_msg_composer_set_body_text (composer, text, len);
- if (uids && uids->pdata[i]) {
- struct emcs_t *emcs;
-
- emcs = g_object_get_data (G_OBJECT (composer), "emcs");
- emcs_set_folder_info (emcs, folder, uids->pdata[i], CAMEL_MESSAGE_FORWARDED, CAMEL_MESSAGE_FORWARDED);
- }
+ if (uids && uids->pdata[i])
+ e_msg_composer_set_source_headers (
+ composer, folder_uri,
+ uids->pdata[i],
+ CAMEL_MESSAGE_FORWARDED);
emu_update_composers_security (composer, validity_found);
composer_set_no_change (composer);
@@ -2533,7 +2578,6 @@ em_utils_reply_to_message (EShell *shell,
EMsgComposer *composer;
EAccount *account;
guint32 flags;
- struct emcs_t *emcs;
g_return_val_if_fail (E_IS_SHELL (shell), NULL);
@@ -2599,8 +2643,15 @@ em_utils_reply_to_message (EShell *shell,
composer_set_body (composer, message, source);
g_object_unref (message);
- emcs = g_object_get_data (G_OBJECT (composer), "emcs");
- emcs_set_folder_info (emcs, folder, uid, flags, flags);
+
+ if (folder != NULL) {
+ const gchar *folder_uri;
+
+ folder_uri = camel_folder_get_uri (folder);
+
+ e_msg_composer_set_source_headers (
+ composer, folder_uri, uid, flags);
+ }
composer_set_no_change (composer);
@@ -2679,7 +2730,6 @@ em_configure_new_composer (EMsgComposer *composer)
EComposerHeaderTable *table;
EComposerHeaderType header_type;
EComposerHeader *header;
- struct emcs_t *emcs;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
@@ -2687,11 +2737,29 @@ em_configure_new_composer (EMsgComposer *composer)
table = e_msg_composer_get_header_table (composer);
header = e_composer_header_table_get_header (table, header_type);
- emcs = emcs_new ();
+ g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_recipients), NULL);
- g_object_set_data_full (
- G_OBJECT (composer), "emcs", emcs,
- (GDestroyNotify) emcs_unref);
+ g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_account), NULL);
+
+ g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_downloads), NULL);
+
+ g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_plugins), NULL);
+
+ g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_subject), NULL);
+
+ g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_unwanted_html), NULL);
g_signal_connect (
composer, "send",
diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h
index fa991863f6..81e8cf1eb0 100644
--- a/mail/em-composer-utils.h
+++ b/mail/em-composer-utils.h
@@ -37,8 +37,8 @@ EMsgComposer * em_utils_compose_new_message_with_mailto
const gchar *url,
const gchar *from_uri);
GtkWidget * em_utils_edit_message (EShell *shell,
- CamelMimeMessage *message,
- CamelFolder *folder);
+ CamelFolder *folder,
+ CamelMimeMessage *message);
void em_utils_edit_messages (EShell *shell,
CamelFolder *folder,
GPtrArray *uids,
diff --git a/modules/composer-autosave/e-autosave-utils.c b/modules/composer-autosave/e-autosave-utils.c
index 8dc9285884..70a609e833 100644
--- a/modules/composer-autosave/e-autosave-utils.c
+++ b/modules/composer-autosave/e-autosave-utils.c
@@ -37,6 +37,7 @@ struct _LoadContext {
struct _SaveContext {
GCancellable *cancellable;
+ GOutputStream *output_stream;
};
static void
@@ -54,6 +55,9 @@ save_context_free (SaveContext *context)
if (context->cancellable != NULL)
g_object_unref (context->cancellable);
+ if (context->output_stream != NULL)
+ g_object_unref (context->output_stream);
+
g_slice_free (SaveContext, context);
}
@@ -194,15 +198,12 @@ save_snapshot_splice_cb (GOutputStream *output_stream,
}
static void
-save_snapshot_replace_cb (GFile *snapshot_file,
- GAsyncResult *result,
- GSimpleAsyncResult *simple)
+save_snapshot_get_message_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ GSimpleAsyncResult *simple)
{
- GObject *object;
- EMsgComposer *composer;
SaveContext *context;
CamelMimeMessage *message;
- GFileOutputStream *output_stream;
GInputStream *input_stream;
CamelStream *camel_stream;
GByteArray *buffer;
@@ -210,34 +211,13 @@ save_snapshot_replace_cb (GFile *snapshot_file,
context = g_simple_async_result_get_op_res_gpointer (simple);
- output_stream = g_file_replace_finish (snapshot_file, result, &error);
-
- if (error != NULL) {
- g_warn_if_fail (output_stream == NULL);
- g_simple_async_result_set_from_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- g_error_free (error);
- return;
- }
-
- g_return_if_fail (G_IS_OUTPUT_STREAM (output_stream));
-
- /* g_async_result_get_source_object() returns a new reference. */
- object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
-
- /* Extract a MIME message from the composer. */
- composer = E_MSG_COMPOSER (object);
- message = e_msg_composer_get_message_draft (
- composer, context->cancellable, &error);
-
- g_object_unref (object);
+ message = e_msg_composer_get_message_draft_finish (
+ composer, result, &error);
if (error != NULL) {
g_warn_if_fail (message == NULL);
g_simple_async_result_set_from_error (simple, error);
g_simple_async_result_complete (simple);
- g_object_unref (output_stream);
g_object_unref (simple);
g_error_free (error);
return;
@@ -255,6 +235,7 @@ save_snapshot_replace_cb (GFile *snapshot_file,
camel_data_wrapper_decode_to_stream_sync (
CAMEL_DATA_WRAPPER (message), camel_stream, NULL, NULL);
g_object_unref (camel_stream);
+
g_object_unref (message);
/* Load the buffer into a GMemoryInputStream. */
@@ -268,7 +249,7 @@ save_snapshot_replace_cb (GFile *snapshot_file,
/* Splice the input and output streams. */
g_output_stream_splice_async (
- G_OUTPUT_STREAM (output_stream), input_stream,
+ context->output_stream, input_stream,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
G_PRIORITY_DEFAULT, context->cancellable,
@@ -276,7 +257,45 @@ save_snapshot_replace_cb (GFile *snapshot_file,
simple);
g_object_unref (input_stream);
- g_object_unref (output_stream);
+}
+
+static void
+save_snapshot_replace_cb (GFile *snapshot_file,
+ GAsyncResult *result,
+ GSimpleAsyncResult *simple)
+{
+ GObject *object;
+ SaveContext *context;
+ GFileOutputStream *output_stream;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ /* Output stream might be NULL, so don't use cast macro. */
+ output_stream = g_file_replace_finish (snapshot_file, result, &error);
+ context->output_stream = (GOutputStream *) output_stream;
+
+ if (error != NULL) {
+ g_warn_if_fail (output_stream == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (G_IS_OUTPUT_STREAM (output_stream));
+
+ /* g_async_result_get_source_object() returns a new reference. */
+ object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
+
+ /* Extract a MIME message from the composer. */
+ e_msg_composer_get_message_draft (
+ E_MSG_COMPOSER (object), G_PRIORITY_DEFAULT,
+ context->cancellable, (GAsyncReadyCallback)
+ save_snapshot_get_message_cb, simple);
+
+ g_object_unref (object);
}
static EMsgComposer *
diff --git a/plugins/email-custom-header/email-custom-header.c b/plugins/email-custom-header/email-custom-header.c
index 94cc793d25..f202935c3e 100644
--- a/plugins/email-custom-header/email-custom-header.c
+++ b/plugins/email-custom-header/email-custom-header.c
@@ -471,7 +471,7 @@ epech_append_to_custom_header (CustomHeaderOptionsDialog *dialog, gint state, gp
temp_header_value_ptr = &g_array_index (temp_header_ptr->sub_header_type_value, CustomSubHeader,sub_type_index);
if (sub_type_index == g_array_index (priv->header_index_type, gint, index_subtype)) {
- e_msg_composer_modify_header (composer, (temp_header_ptr->header_type_value)->str,
+ e_msg_composer_set_header (composer, (temp_header_ptr->header_type_value)->str,
(temp_header_value_ptr->sub_header_string_value)->str);
}
}
diff --git a/plugins/face/face.c b/plugins/face/face.c
index 2013f7dac6..57e80832e6 100644
--- a/plugins/face/face.c
+++ b/plugins/face/face.c
@@ -466,7 +466,7 @@ face_handle_send (EPlugin *ep, EMEventTargetComposer *target)
gchar *face = get_face_base64 ();
if (face)
- e_msg_composer_modify_header (target->composer, "Face", face);
+ e_msg_composer_set_header (target->composer, "Face", face);
g_free (face);
}
diff --git a/plugins/templates/templates.c b/plugins/templates/templates.c
index 901f902ae9..a2bac3dec9 100644
--- a/plugins/templates/templates.c
+++ b/plugins/templates/templates.c
@@ -519,7 +519,7 @@ create_new_message (CamelFolder *folder, const gchar *uid, CamelMimeMessage *mes
camel_mime_message_get_recipients (template, CAMEL_RECIPIENT_TYPE_BCC));
/* Create the composer */
- em_utils_edit_message (shell, new, folder);
+ em_utils_edit_message (shell, folder, new);
g_object_unref (new);
}
@@ -549,7 +549,7 @@ build_template_menus_recurse (GtkUIManager *ui_manager,
guint *action_count,
guint merge_id,
CamelFolderInfo *folder_info,
- CamelFolder *message_folder,
+ CamelFolder *message_folder,
const gchar *message_uid)
{
CamelStore *store;
@@ -669,28 +669,26 @@ build_template_menus_recurse (GtkUIManager *ui_manager,
}
static void
-action_template_cb (GtkAction *action,
- EMsgComposer *composer)
+got_message_draft_cb (EMsgComposer *composer,
+ GAsyncResult *result)
{
+ CamelMimeMessage *message;
CamelMessageInfo *info;
- CamelMimeMessage *msg;
CamelFolder *folder;
GError *error = NULL;
- /* Get the templates folder and all UIDs of the messages there. */
- folder = e_mail_local_get_folder (E_MAIL_FOLDER_TEMPLATES);
-
- msg = e_msg_composer_get_message_draft (composer, NULL, &error);
+ message = e_msg_composer_get_message_draft_finish (
+ composer, result, &error);
/* Ignore cancellations. */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_warn_if_fail (msg == NULL);
+ g_warn_if_fail (message == NULL);
g_error_free (error);
return;
}
if (error != NULL) {
- g_warn_if_fail (msg == NULL);
+ g_warn_if_fail (message == NULL);
e_alert_run_dialog_for_args (
GTK_WINDOW (composer),
"mail-composer:no-build-message",
@@ -699,7 +697,10 @@ action_template_cb (GtkAction *action,
return;
}
- g_return_if_fail (CAMEL_IS_MIME_MESSAGE (msg));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ /* Get the templates folder and all UIDs of the messages there. */
+ folder = e_mail_local_get_folder (E_MAIL_FOLDER_TEMPLATES);
info = camel_message_info_new (NULL);
@@ -709,7 +710,19 @@ action_template_cb (GtkAction *action,
camel_message_info_set_flags (
info, CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_DRAFT, ~0);
- mail_append_mail (folder, msg, info, NULL, composer);
+ mail_append_mail (folder, message, info, NULL, composer);
+
+ g_object_unref (message);
+}
+
+static void
+action_template_cb (GtkAction *action,
+ EMsgComposer *composer)
+{
+ /* XXX Pass a GCancellable */
+ e_msg_composer_get_message_draft (
+ composer, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback) got_message_draft_cb, NULL);
}
static GtkActionEntry composer_entries[] = {
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 21faf33517..4ee47766b4 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -9,7 +9,9 @@ widgetsinclude_HEADERS = \
e-account-manager.h \
e-account-tree-view.h \
e-action-combo-box.h \
+ e-activity-bar.h \
e-activity-proxy.h \
+ e-alert-bar.h \
e-attachment.h \
e-attachment-button.h \
e-attachment-dialog.h \
@@ -85,7 +87,9 @@ libemiscwidgets_la_SOURCES = \
e-account-manager.c \
e-account-tree-view.c \
e-action-combo-box.c \
+ e-activity-bar.c \
e-activity-proxy.c \
+ e-alert-bar.c \
e-attachment.c \
e-attachment-button.c \
e-attachment-dialog.c \
diff --git a/widgets/misc/e-activity-bar.c b/widgets/misc/e-activity-bar.c
new file mode 100644
index 0000000000..0337822adc
--- /dev/null
+++ b/widgets/misc/e-activity-bar.c
@@ -0,0 +1,351 @@
+/*
+ * e-activity-bar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-activity-bar.h"
+
+#define E_ACTIVITY_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarPrivate))
+
+#define FEEDBACK_PERIOD 1 /* seconds */
+
+struct _EActivityBarPrivate {
+ EActivity *activity; /* weak reference */
+ GtkWidget *image; /* not referenced */
+ GtkWidget *label; /* not referenced */
+ GtkWidget *cancel; /* not referenced */
+
+ /* If the user clicks the Cancel button, keep the cancelled
+ * EActivity object alive for a short duration so the user
+ * gets some visual feedback that cancellation worked. */
+ guint timeout_id;
+};
+
+enum {
+ PROP_0,
+ PROP_ACTIVITY
+};
+
+G_DEFINE_TYPE (
+ EActivityBar,
+ e_activity_bar,
+ GTK_TYPE_INFO_BAR)
+
+static void
+activity_bar_update (EActivityBar *bar)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+ const gchar *icon_name;
+ gboolean cancelled;
+ gboolean completed;
+ gboolean sensitive;
+ gboolean visible;
+ gchar *description;
+
+ activity = e_activity_bar_get_activity (bar);
+
+ if (activity == NULL) {
+ gtk_widget_hide (GTK_WIDGET (bar));
+ return;
+ }
+
+ cancellable = e_activity_get_cancellable (activity);
+ cancelled = g_cancellable_is_cancelled (cancellable);
+ completed = e_activity_is_completed (activity);
+ icon_name = e_activity_get_icon_name (activity);
+
+ description = e_activity_describe (activity);
+ gtk_label_set_text (GTK_LABEL (bar->priv->label), description);
+
+ if (cancelled) {
+ PangoAttribute *attr;
+ PangoAttrList *attr_list;
+
+ attr_list = pango_attr_list_new ();
+
+ attr = pango_attr_strikethrough_new (TRUE);
+ pango_attr_list_insert (attr_list, attr);
+
+ gtk_label_set_attributes (
+ GTK_LABEL (bar->priv->label), attr_list);
+
+ pango_attr_list_unref (attr_list);
+ } else
+ gtk_label_set_attributes (
+ GTK_LABEL (bar->priv->label), NULL);
+
+ if (cancelled)
+ gtk_image_set_from_stock (
+ GTK_IMAGE (bar->priv->image),
+ GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
+ else {
+ if (completed)
+ icon_name = "emblem-default";
+ gtk_image_set_from_icon_name (
+ GTK_IMAGE (bar->priv->image),
+ icon_name, GTK_ICON_SIZE_BUTTON);
+ }
+
+ visible = (icon_name != NULL);
+ gtk_widget_set_visible (bar->priv->image, visible);
+
+ visible = (cancellable != NULL);
+ gtk_widget_set_visible (bar->priv->cancel, visible);
+
+ sensitive = !cancelled && !completed;
+ gtk_widget_set_sensitive (bar->priv->cancel, sensitive);
+
+ visible = (description != NULL && *description != '\0');
+ gtk_widget_set_visible (GTK_WIDGET (bar), visible);
+
+ g_free (description);
+}
+
+static void
+activity_bar_cancel (EActivityBar *bar)
+{
+ EActivity *activity;
+ GCancellable *cancellable;
+
+ activity = e_activity_bar_get_activity (bar);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ cancellable = e_activity_get_cancellable (activity);
+ g_cancellable_cancel (cancellable);
+}
+
+static void
+activity_bar_feedback (EActivityBar *bar)
+{
+ EActivity *activity;
+
+ activity = e_activity_bar_get_activity (bar);
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+
+ if (bar->priv->timeout_id > 0)
+ g_source_remove (bar->priv->timeout_id);
+
+ /* Hold a reference on the EActivity for a short
+ * period so the activity bar stays visible. */
+ bar->priv->timeout_id = g_timeout_add_seconds_full (
+ G_PRIORITY_LOW, FEEDBACK_PERIOD, (GSourceFunc) gtk_false,
+ g_object_ref (activity), (GDestroyNotify) g_object_unref);
+}
+
+static void
+activity_bar_weak_notify_cb (EActivityBar *bar,
+ GObject *where_the_object_was)
+{
+ bar->priv->activity = NULL;
+ e_activity_bar_set_activity (bar, NULL);
+}
+
+static void
+activity_bar_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ e_activity_bar_set_activity (
+ E_ACTIVITY_BAR (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_bar_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_ACTIVITY:
+ g_value_set_object (
+ value, e_activity_bar_get_activity (
+ E_ACTIVITY_BAR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+activity_bar_dispose (GObject *object)
+{
+ EActivityBarPrivate *priv;
+
+ priv = E_ACTIVITY_BAR_GET_PRIVATE (object);
+
+ if (priv->timeout_id > 0) {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+
+ if (priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, object);
+ g_object_weak_unref (
+ G_OBJECT (priv->activity), (GWeakNotify)
+ activity_bar_weak_notify_cb, object);
+ priv->activity = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_activity_bar_parent_class)->dispose (object);
+}
+
+static void
+e_activity_bar_class_init (EActivityBarClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EActivityBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = activity_bar_set_property;
+ object_class->get_property = activity_bar_get_property;
+ object_class->dispose = activity_bar_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTIVITY,
+ g_param_spec_object (
+ "activity",
+ NULL,
+ NULL,
+ E_TYPE_ACTIVITY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+e_activity_bar_init (EActivityBar *bar)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+
+ bar->priv = E_ACTIVITY_BAR_GET_PRIVATE (bar);
+
+ container = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ bar->priv->image = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ bar->priv->label = widget;
+ gtk_widget_show (widget);
+
+ /* This is only shown if the EActivity has a GCancellable. */
+ widget = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
+ gtk_info_bar_add_action_widget (
+ GTK_INFO_BAR (bar), widget, GTK_RESPONSE_CANCEL);
+ bar->priv->cancel = widget;
+ gtk_widget_hide (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (activity_bar_cancel), bar);
+}
+
+GtkWidget *
+e_activity_bar_new (void)
+{
+ return g_object_new (E_TYPE_ACTIVITY_BAR, NULL);
+}
+
+EActivity *
+e_activity_bar_get_activity (EActivityBar *bar)
+{
+ g_return_val_if_fail (E_IS_ACTIVITY_BAR (bar), NULL);
+
+ return bar->priv->activity;
+}
+
+void
+e_activity_bar_set_activity (EActivityBar *bar,
+ EActivity *activity)
+{
+ g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
+
+ if (activity != NULL) {
+ g_return_if_fail (E_IS_ACTIVITY (activity));
+ g_object_weak_ref (
+ G_OBJECT (activity), (GWeakNotify)
+ activity_bar_weak_notify_cb, bar);
+ }
+
+ if (bar->priv->timeout_id > 0) {
+ g_source_remove (bar->priv->timeout_id);
+ bar->priv->timeout_id = 0;
+ }
+
+ if (bar->priv->activity != NULL) {
+ g_signal_handlers_disconnect_matched (
+ bar->priv->activity, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, bar);
+ g_object_weak_unref (
+ G_OBJECT (bar->priv->activity),
+ (GWeakNotify) activity_bar_weak_notify_cb, bar);
+ }
+
+ bar->priv->activity = activity;
+
+ if (activity != NULL) {
+ g_signal_connect_swapped (
+ activity, "cancelled",
+ G_CALLBACK (activity_bar_feedback), bar);
+
+ g_signal_connect_swapped (
+ activity, "completed",
+ G_CALLBACK (activity_bar_feedback), bar);
+
+ g_signal_connect_swapped (
+ activity, "cancelled",
+ G_CALLBACK (activity_bar_update), bar);
+
+ g_signal_connect_swapped (
+ activity, "completed",
+ G_CALLBACK (activity_bar_update), bar);
+
+ g_signal_connect_swapped (
+ activity, "notify",
+ G_CALLBACK (activity_bar_update), bar);
+ }
+
+ activity_bar_update (bar);
+
+ g_object_notify (G_OBJECT (bar), "activity");
+}
diff --git a/widgets/misc/e-activity-bar.h b/widgets/misc/e-activity-bar.h
new file mode 100644
index 0000000000..24f56eca53
--- /dev/null
+++ b/widgets/misc/e-activity-bar.h
@@ -0,0 +1,67 @@
+/*
+ * e-activity-bar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_ACTIVITY_BAR_H
+#define E_ACTIVITY_BAR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-activity.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ACTIVITY_BAR \
+ (e_activity_bar_get_type ())
+#define E_ACTIVITY_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBar))
+#define E_ACTIVITY_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ACTIVITY_BAR, EActivityBarClass))
+#define E_IS_ACTIVITY_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ACTIVITY_BAR))
+#define E_IS_ACTIVITY_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ACTIVITY_BAR))
+#define E_ACTIVITY_BAR_GET_CLASS(obj) \
+ (G_TYPE_CHECK_INSTANCE_GET_TYPE \
+ ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EActivityBar EActivityBar;
+typedef struct _EActivityBarClass EActivityBarClass;
+typedef struct _EActivityBarPrivate EActivityBarPrivate;
+
+struct _EActivityBar {
+ GtkInfoBar parent;
+ EActivityBarPrivate *priv;
+};
+
+struct _EActivityBarClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType e_activity_bar_get_type (void);
+GtkWidget * e_activity_bar_new (void);
+EActivity * e_activity_bar_get_activity (EActivityBar *bar);
+void e_activity_bar_set_activity (EActivityBar *bar,
+ EActivity *activity);
+
+G_END_DECLS
+
+#endif /* E_ACTIVITY_BAR_H */
diff --git a/widgets/misc/e-activity-proxy.c b/widgets/misc/e-activity-proxy.c
index e96f18ed63..26d5d30ba0 100644
--- a/widgets/misc/e-activity-proxy.c
+++ b/widgets/misc/e-activity-proxy.c
@@ -69,6 +69,7 @@ activity_proxy_update (EActivityProxy *proxy)
gboolean clickable;
gboolean completed;
gboolean sensitive;
+ gboolean visible;
gchar *description;
activity = proxy->priv->activity;
@@ -108,10 +109,8 @@ activity_proxy_update (EActivityProxy *proxy)
gtk_widget_hide (proxy->priv->image);
}
- if (cancellable != NULL)
- gtk_widget_show (proxy->priv->cancel);
- else
- gtk_widget_hide (proxy->priv->cancel);
+ visible = (cancellable != NULL);
+ gtk_widget_set_visible (proxy->priv->cancel, visible);
sensitive = !(cancelled || completed);
gtk_widget_set_sensitive (proxy->priv->cancel, sensitive);
diff --git a/widgets/misc/e-alert-bar.c b/widgets/misc/e-alert-bar.c
new file mode 100644
index 0000000000..7796482689
--- /dev/null
+++ b/widgets/misc/e-alert-bar.c
@@ -0,0 +1,235 @@
+/*
+ * e-alert-bar.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-alert-bar.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#define E_ALERT_BAR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
+
+#define ICON_SIZE GTK_ICON_SIZE_DIALOG
+
+struct _EAlertBarPrivate {
+ GQueue alerts;
+ GtkWidget *image; /* not referenced */
+ GtkWidget *primary_label; /* not referenced */
+ GtkWidget *secondary_label; /* not referenced */
+};
+
+G_DEFINE_TYPE (
+ EAlertBar,
+ e_alert_bar,
+ GTK_TYPE_INFO_BAR)
+
+static void
+alert_bar_show_alert (EAlertBar *alert_bar)
+{
+ GtkImage *image;
+ GtkLabel *label;
+ GtkInfoBar *info_bar;
+ GtkWidget *action_area;
+ EAlertButton *buttons;
+ EAlert *alert;
+ GList *children;
+ GtkMessageType message_type;
+ const gchar *stock_id;
+ const gchar *text;
+ gint response_id;
+
+ info_bar = GTK_INFO_BAR (alert_bar);
+ action_area = gtk_info_bar_get_action_area (info_bar);
+
+ alert = g_queue_peek_head (&alert_bar->priv->alerts);
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ /* Remove all buttons from the previous alert. */
+ children = gtk_container_get_children (GTK_CONTAINER (action_area));
+ while (children != NULL) {
+ GtkWidget *child = GTK_WIDGET (children->data);
+ gtk_container_remove (GTK_CONTAINER (action_area), child);
+ children = g_list_delete_link (children, children);
+ }
+
+ /* Add new buttons. */
+ buttons = e_alert_peek_buttons (alert);
+ if (buttons == NULL) {
+ gtk_info_bar_add_button (
+ info_bar, _("_Dismiss"), GTK_RESPONSE_CLOSE);
+ } else while (buttons != NULL) {
+ const gchar *button_text;
+
+ if (buttons->stock != NULL)
+ button_text = buttons->stock;
+ else
+ button_text = buttons->label;
+
+ gtk_info_bar_add_button (
+ info_bar, button_text, buttons->response);
+
+ buttons = buttons->next;
+ }
+
+ response_id = e_alert_get_default_response (alert);
+ gtk_info_bar_set_default_response (info_bar, response_id);
+
+ message_type = e_alert_get_message_type (alert);
+ gtk_info_bar_set_message_type (info_bar, message_type);
+
+ text = e_alert_get_primary_text (alert);
+ label = GTK_LABEL (alert_bar->priv->primary_label);
+ gtk_label_set_text (label, text);
+
+ text = e_alert_get_secondary_text (alert);
+ label = GTK_LABEL (alert_bar->priv->secondary_label);
+ gtk_label_set_text (label, text);
+
+ stock_id = e_alert_get_stock_id (alert);
+ image = GTK_IMAGE (alert_bar->priv->image);
+ gtk_image_set_from_stock (image, stock_id, ICON_SIZE);
+
+ gtk_widget_show (GTK_WIDGET (alert_bar));
+}
+
+static void
+alert_bar_dispose (GObject *object)
+{
+ EAlertBarPrivate *priv;
+
+ priv = E_ALERT_BAR_GET_PRIVATE (object);
+
+ while (!g_queue_is_empty (&priv->alerts))
+ g_object_unref (g_queue_pop_head (&priv->alerts));
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object);
+}
+
+static void
+alert_bar_response (GtkInfoBar *info_bar,
+ gint response_id)
+{
+ EAlertBar *alert_bar;
+ EAlert *alert;
+
+ alert_bar = E_ALERT_BAR (info_bar);
+
+ alert = g_queue_pop_head (&alert_bar->priv->alerts);
+ e_alert_response (alert, response_id);
+ g_object_unref (alert);
+
+ if (!g_queue_is_empty (&alert_bar->priv->alerts))
+ alert_bar_show_alert (alert_bar);
+ else
+ gtk_widget_hide (GTK_WIDGET (alert_bar));
+}
+
+static void
+e_alert_bar_class_init (EAlertBarClass *class)
+{
+ GObjectClass *object_class;
+ GtkInfoBarClass *info_bar_class;
+
+ g_type_class_add_private (class, sizeof (EAlertBarPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = alert_bar_dispose;
+
+ info_bar_class = GTK_INFO_BAR_CLASS (class);
+ info_bar_class->response = alert_bar_response;
+}
+
+static void
+e_alert_bar_init (EAlertBar *alert_bar)
+{
+ GtkWidget *container;
+ GtkWidget *widget;
+ PangoAttribute *attr;
+ PangoAttrList *attr_list;
+
+ alert_bar->priv = E_ALERT_BAR_GET_PRIVATE (alert_bar);
+
+ g_queue_init (&alert_bar->priv->alerts);
+
+ container = gtk_info_bar_get_content_area (GTK_INFO_BAR (alert_bar));
+
+ widget = gtk_hbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ widget = gtk_image_new ();
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ alert_bar->priv->image = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_vbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
+ attr_list = pango_attr_list_new ();
+ attr = pango_attr_scale_new (PANGO_SCALE_LARGE);
+ pango_attr_list_insert (attr_list, attr);
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ pango_attr_list_insert (attr_list, attr);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ alert_bar->priv->primary_label = widget;
+ gtk_widget_show (widget);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ alert_bar->priv->secondary_label = widget;
+ gtk_widget_show (widget);
+
+ pango_attr_list_unref (attr_list);
+}
+
+GtkWidget *
+e_alert_bar_new (void)
+{
+ return g_object_new (E_TYPE_ALERT_BAR, NULL);
+}
+
+void
+e_alert_bar_add_alert (EAlertBar *alert_bar,
+ EAlert *alert)
+{
+ gboolean show_it_now;
+
+ g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
+ g_return_if_fail (E_IS_ALERT (alert));
+
+ show_it_now = g_queue_is_empty (&alert_bar->priv->alerts);
+ g_queue_push_tail (&alert_bar->priv->alerts, g_object_ref (alert));
+
+ if (show_it_now)
+ alert_bar_show_alert (alert_bar);
+}
diff --git a/widgets/misc/e-alert-bar.h b/widgets/misc/e-alert-bar.h
new file mode 100644
index 0000000000..fc23dec8bf
--- /dev/null
+++ b/widgets/misc/e-alert-bar.h
@@ -0,0 +1,66 @@
+/*
+ * e-alert-bar.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_ALERT_BAR_H
+#define E_ALERT_BAR_H
+
+#include <gtk/gtk.h>
+#include <e-util/e-alert.h>
+
+/* Standard GObject macros */
+#define E_TYPE_ALERT_BAR \
+ (e_alert_bar_get_type ())
+#define E_ALERT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBar))
+#define E_ALERT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_ALERT_BAR, EAlertBarClass))
+#define E_IS_ALERT_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_ALERT_BAR))
+#define E_IS_ALERT_BAR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_ALERT_BAR))
+#define E_ALERT_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_ALERT_BAR, EAlertBarClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EAlertBar EAlertBar;
+typedef struct _EAlertBarClass EAlertBarClass;
+typedef struct _EAlertBarPrivate EAlertBarPrivate;
+
+struct _EAlertBar {
+ GtkInfoBar parent;
+ EAlertBarPrivate *priv;
+};
+
+struct _EAlertBarClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType e_alert_bar_get_type (void);
+GtkWidget * e_alert_bar_new (void);
+void e_alert_bar_add_alert (EAlertBar *alert_bar,
+ EAlert *alert);
+
+G_END_DECLS
+
+#endif /* E_ALERT_BAR_H */