[evolution] Bug 255032 - Be able to write a note on a mail



commit 4c0c362180d0eed448000ac35b887961c1ef3430
Author: Milan Crha <mcrha redhat com>
Date:   Mon Sep 21 16:17:02 2015 +0200

    Bug 255032 - Be able to write a note on a mail

 e-util/e-attachment.c                    |   23 +-
 e-util/e-attachment.h                    |    1 +
 mail/Makefile.am                         |    2 +
 mail/e-mail-display.c                    |   24 +
 mail/e-mail-notes.c                      | 1095 ++++++++++++++++++++++++++++++
 mail/e-mail-notes.h                      |   42 ++
 mail/e-mail-reader-utils.c               |    4 +
 mail/e-mail-reader.c                     |  155 +++++
 mail/e-mail-reader.h                     |    3 +-
 mail/e-mail.h                            |    1 +
 mail/filtertypes.xml.in                  |   18 +
 mail/mail.error.xml                      |   20 +
 mail/message-list.c                      |    6 +-
 mail/searchtypes.xml.in                  |   18 +
 mail/vfoldertypes.xml.in                 |   18 +
 modules/mail/e-mail-shell-view-actions.c |    7 +
 modules/mail/e-mail-shell-view-actions.h |    2 +
 modules/mail/e-mail-shell-view-private.h |    5 +-
 modules/mail/e-mail-shell-view.c         |    7 +
 po/POTFILES.in                           |    1 +
 ui/evolution-mail-reader.ui              |    8 +
 ui/evolution-mail.ui                     |    5 +
 22 files changed, 1460 insertions(+), 5 deletions(-)
---
diff --git a/e-util/e-attachment.c b/e-util/e-attachment.c
index 31014c5..64fa890 100644
--- a/e-util/e-attachment.c
+++ b/e-util/e-attachment.c
@@ -386,8 +386,12 @@ attachment_update_icon_column_idle_cb (gpointer weak_ref)
                        file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
        }
 
+       if (e_attachment_is_mail_note (attachment)) {
+               g_clear_object (&icon);
+               icon = g_themed_icon_new ("evolution-memos");
+
        /* Prefer the thumbnail if we have one. */
-       if (thumbnail_path != NULL && *thumbnail_path != '\0') {
+       } else if (thumbnail_path != NULL && *thumbnail_path != '\0') {
                GFile *file;
 
                file = g_file_new_for_path (thumbnail_path);
@@ -1336,6 +1340,23 @@ e_attachment_cancel (EAttachment *attachment)
 }
 
 gboolean
+e_attachment_is_mail_note (EAttachment *attachment)
+{
+       CamelContentType *ct;
+
+       g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+       if (!attachment->priv->mime_part)
+               return FALSE;
+
+       ct = camel_mime_part_get_content_type (attachment->priv->mime_part);
+       if (!ct || !camel_content_type_is (ct, "message", "rfc822"))
+               return FALSE;
+
+       return camel_medium_get_header (CAMEL_MEDIUM (attachment->priv->mime_part), "X-Evolution-Note") != 
NULL;
+}
+
+gboolean
 e_attachment_get_can_show (EAttachment *attachment)
 {
        g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
diff --git a/e-util/e-attachment.h b/e-util/e-attachment.h
index 172c5fc..b7505c9 100644
--- a/e-util/e-attachment.h
+++ b/e-util/e-attachment.h
@@ -71,6 +71,7 @@ void          e_attachment_add_to_multipart   (EAttachment *attachment,
                                                 CamelMultipart *multipart,
                                                 const gchar *default_charset);
 void           e_attachment_cancel             (EAttachment *attachment);
+gboolean       e_attachment_is_mail_note       (EAttachment *attachment);
 gboolean       e_attachment_get_can_show       (EAttachment *attachment);
 void           e_attachment_set_can_show       (EAttachment *attachment,
                                                 gboolean can_show);
diff --git a/mail/Makefile.am b/mail/Makefile.am
index c0fab7b..06aa259 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -95,6 +95,7 @@ mailinclude_HEADERS =                                 \
        e-mail-label-tree-view.h                        \
        e-mail-message-pane.h                           \
        e-mail-migrate.h                                \
+       e-mail-notes.h                                  \
        e-mail-paned-view.h                             \
        e-mail-print-config-headers.h                   \
        e-mail-printer.h                                \
@@ -175,6 +176,7 @@ libevolution_mail_la_SOURCES =                              \
        e-mail-label-tree-view.c                        \
        e-mail-message-pane.c                           \
        e-mail-migrate.c                                \
+       e-mail-notes.c                                  \
        e-mail-paned-view.c                             \
        e-mail-print-config-headers.c                   \
        e-mail-printer.c                                \
diff --git a/mail/e-mail-display.c b/mail/e-mail-display.c
index 9b4b628..5fa29a5 100644
--- a/mail/e-mail-display.c
+++ b/mail/e-mail-display.c
@@ -36,6 +36,7 @@
 
 #include "e-http-request.h"
 #include "e-mail-display-popup-extension.h"
+#include "e-mail-notes.h"
 #include "e-mail-request.h"
 #include "em-composer-utils.h"
 #include "em-utils.h"
@@ -758,6 +759,7 @@ mail_display_plugin_widget_requested (WebKitWebView *web_view,
         * that URI identifies the attachment itself */
        if (E_IS_ATTACHMENT_BUTTON (widget)) {
                EMailPartAttachment *empa = (EMailPartAttachment *) part;
+               EAttachment *attachment;
                gchar *attachment_part_id;
 
                if (empa->attachment_view_part_id)
@@ -771,6 +773,28 @@ mail_display_plugin_widget_requested (WebKitWebView *web_view,
                        G_OBJECT (widget), "attachment_id",
                        g_strdup (attachment_part_id),
                        (GDestroyNotify) g_free);
+
+               attachment = e_mail_part_attachment_ref_attachment (empa);
+               if (attachment && e_attachment_is_mail_note (attachment)) {
+                       CamelFolder *folder;
+                       const gchar *message_uid;
+
+                       folder = e_mail_part_list_get_folder (display->priv->part_list);
+                       message_uid = e_mail_part_list_get_message_uid (display->priv->part_list);
+
+                       if (folder && message_uid) {
+                               CamelMessageInfo *info;
+
+                               info = camel_folder_get_message_info (folder, message_uid);
+                               if (info) {
+                                       if (!camel_message_info_user_flag (info, E_MAIL_NOTES_USER_FLAG))
+                                               camel_message_info_set_user_flag (info, 
E_MAIL_NOTES_USER_FLAG, TRUE);
+                                       camel_message_info_unref (info);
+                               }
+                       }
+               }
+
+               g_clear_object (&attachment);
        } else {
                object_uri = g_strdup (part_id);
        }
diff --git a/mail/e-mail-notes.c b/mail/e-mail-notes.c
new file mode 100644
index 0000000..7d69cb9
--- /dev/null
+++ b/mail/e-mail-notes.c
@@ -0,0 +1,1095 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <camel/camel.h>
+#include <e-util/e-util.h>
+#include <libemail-engine/libemail-engine.h>
+
+#include "e-mail-notes.h"
+
+#define E_TYPE_MAIL_NOTES_EDITOR \
+       (e_mail_notes_editor_get_type ())
+#define E_MAIL_NOTES_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_MAIL_NOTES_EDITOR, EMailNotesEditor))
+#define E_IS_MAIL_NOTES_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_MAIL_NOTES_EDITOR))
+
+typedef struct _EMailNotesEditor EMailNotesEditor;
+typedef struct _EMailNotesEditorClass EMailNotesEditorClass;
+
+struct _EMailNotesEditor {
+       GtkWindow parent;
+
+       EHTMLEditor *editor; /* not referenced */
+       EAttachmentPaned *attachment_paned; /* not referenced */
+       EFocusTracker *focus_tracker;
+       GtkActionGroup *action_group;
+
+       gboolean had_message;
+       CamelMimeMessage *message;
+       CamelFolder *folder;
+       gchar *uid;
+};
+
+struct _EMailNotesEditorClass {
+       GtkWindowClass parent_class;
+};
+
+GType e_mail_notes_editor_get_type (void);
+
+G_DEFINE_TYPE (EMailNotesEditor, e_mail_notes_editor, GTK_TYPE_WINDOW)
+
+static gchar *
+e_mail_notes_extract_text_content (CamelMimePart *part)
+{
+       CamelDataWrapper *content;
+       CamelStream *stream;
+       GByteArray *byte_array;
+       gchar *text = NULL;
+
+       g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
+
+       content = camel_medium_get_content (CAMEL_MEDIUM (part));
+       g_return_val_if_fail (content != NULL, NULL);
+
+       stream = camel_stream_mem_new ();
+       camel_data_wrapper_decode_to_stream_sync (content, stream, NULL, NULL);
+       camel_stream_close (stream, NULL, NULL);
+
+       byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+
+       if (byte_array->data)
+               text = g_strndup ((const gchar *) byte_array->data, byte_array->len);
+
+       g_object_unref (stream);
+
+       return text;
+}
+
+static void
+e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditorView *view,
+                                                     CamelMultipart *in_multipart)
+{
+       guint ii, nparts;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+       g_return_if_fail (CAMEL_IS_MULTIPART (in_multipart));
+
+       nparts = camel_multipart_get_number (in_multipart);
+
+       for (ii = 0; ii < nparts; ii++) {
+               CamelMimePart *part;
+               CamelContentType *ct;
+
+               /* Traverse from the end, where the best format available is stored */
+               part = camel_multipart_get_part (in_multipart, nparts - ii - 1);
+               if (!part)
+                       continue;
+
+               ct = camel_mime_part_get_content_type (part);
+               if (!ct)
+                       continue;
+
+               if (camel_content_type_is (ct, "text", "html")) {
+                       gchar *text;
+
+                       text = e_mail_notes_extract_text_content (part);
+                       if (text) {
+                               e_html_editor_view_set_html_mode (view, TRUE);
+                               e_html_editor_view_set_text_html (view, text);
+                               g_free (text);
+                               break;
+                       }
+               } else if (camel_content_type_is (ct, "text", "plain")) {
+                       gchar *text;
+
+                       text = e_mail_notes_extract_text_content (part);
+                       if (text) {
+                               e_html_editor_view_set_text_plain (view, text);
+                               g_free (text);
+                       }
+                       break;
+               }
+       }
+}
+
+static void
+e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes_editor,
+                                                        CamelMultipart *multipart)
+{
+       EHTMLEditorView *view;
+       guint ii, nparts;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+       g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
+
+       view = e_html_editor_get_view (notes_editor->editor);
+       nparts = camel_multipart_get_number (multipart);
+
+       for (ii = 0; ii < nparts; ii++) {
+               CamelMimePart *part;
+               CamelContentType *ct;
+               CamelDataWrapper *content;
+
+               part = camel_multipart_get_part (multipart, ii);
+               if (!part)
+                       continue;
+
+               ct = camel_mime_part_get_content_type (part);
+               if (!ct)
+                       continue;
+
+               if (camel_content_type_is (ct, "image", "*")) {
+                       e_html_editor_view_add_inline_image_from_mime_part (view, part);
+               } else if (camel_content_type_is (ct, "multipart", "alternative")) {
+                       content = camel_medium_get_content (CAMEL_MEDIUM (part));
+                       if (CAMEL_IS_MULTIPART (content)) {
+                               e_mail_notes_extract_text_from_multipart_alternative (view, CAMEL_MULTIPART 
(content));
+                       }
+               }
+       }
+}
+
+static void
+e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
+                                           CamelMimePart *part)
+{
+       CamelContentType *ct;
+       CamelDataWrapper *content;
+       EHTMLEditorView *view;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+       g_return_if_fail (CAMEL_IS_MIME_PART (part));
+
+       content = camel_medium_get_content (CAMEL_MEDIUM (part));
+       ct = camel_data_wrapper_get_mime_type_field (content);
+
+       g_return_if_fail (content != NULL);
+       g_return_if_fail (ct != NULL);
+
+       view = e_html_editor_get_view (notes_editor->editor);
+
+       if (camel_content_type_is (ct, "multipart", "related")) {
+               g_return_if_fail (CAMEL_IS_MULTIPART (content));
+
+               e_mail_notes_editor_extract_text_from_multipart_related (notes_editor, CAMEL_MULTIPART 
(content));
+       } else if (camel_content_type_is (ct, "multipart", "alternative")) {
+               if (CAMEL_IS_MULTIPART (content)) {
+                       e_mail_notes_extract_text_from_multipart_alternative (view, CAMEL_MULTIPART 
(content));
+               }
+       } else if (camel_content_type_is (ct, "text", "plain")) {
+               gchar *text;
+
+               text = e_mail_notes_extract_text_content (part);
+               if (text) {
+                       e_html_editor_view_set_text_plain (view, text);
+                       g_free (text);
+               }
+       }
+}
+
+static void
+e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
+                                              CamelMimeMessage *message)
+{
+       CamelContentType *ct;
+       CamelDataWrapper *content;
+       EHTMLEditorView *view;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+       g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+       content = camel_medium_get_content (CAMEL_MEDIUM (message));
+       ct = camel_data_wrapper_get_mime_type_field (content);
+
+       g_return_if_fail (content != NULL);
+       g_return_if_fail (ct != NULL);
+
+       view = e_html_editor_get_view (notes_editor->editor);
+
+       if (camel_content_type_is (ct, "multipart", "mixed")) {
+               EAttachmentStore *attachment_store;
+               CamelMultipart *multipart;
+               guint ii, nparts;
+
+               g_return_if_fail (CAMEL_IS_MULTIPART (content));
+
+               attachment_store = e_attachment_view_get_store (E_ATTACHMENT_VIEW 
(notes_editor->attachment_paned));
+               multipart = CAMEL_MULTIPART (content);
+               nparts = camel_multipart_get_number (multipart);
+
+               /* The first part is the note text, the rest are attachments */
+               for (ii = 0; ii < nparts; ii++) {
+                       CamelMimePart *part;
+
+                       part = camel_multipart_get_part (multipart, ii);
+                       if (!part)
+                               continue;
+
+                       ct = camel_mime_part_get_content_type (part);
+                       if (!ct)
+                               continue;
+
+                       if (ii == 0) {
+                               e_mail_notes_editor_extract_text_from_part (notes_editor, part);
+                       } else {
+                               EAttachment *attachment;
+
+                               attachment = e_attachment_new ();
+
+                               e_attachment_set_mime_part (attachment, part);
+                               e_attachment_store_add_attachment (attachment_store, attachment);
+                               e_attachment_load_async (attachment, (GAsyncReadyCallback)
+                                       e_attachment_load_handle_error, notes_editor);
+
+                               g_object_unref (attachment);
+                       }
+               }
+       } else {
+               e_mail_notes_editor_extract_text_from_part (notes_editor, CAMEL_MIME_PART (message));
+       }
+
+       e_html_editor_view_set_changed (view, FALSE);
+}
+
+static CamelMimeMessage *
+e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
+{
+       EHTMLEditorView *view;
+       EAttachmentStore *attachment_store;
+       CamelMimeMessage *message = NULL;
+       gchar *message_uid;
+       const gchar *username;
+       CamelInternetAddress *address;
+       gboolean has_text = FALSE, has_attachments;
+
+       g_return_val_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor), NULL);
+       g_return_val_if_fail (notes_editor->editor, NULL);
+
+       view = e_html_editor_get_view (notes_editor->editor);
+       g_return_val_if_fail (E_IS_HTML_EDITOR_VIEW (view), NULL);
+
+       message = camel_mime_message_new ();
+       username = g_get_user_name ();
+       if (!username || !*username)
+               username = g_get_real_name ();
+       address = camel_internet_address_new ();
+       camel_internet_address_add (address, NULL, username);
+
+       message_uid = camel_header_msgid_generate (g_get_host_name ());
+
+       camel_mime_message_set_from (message, address);
+       camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
+       camel_mime_message_set_subject (message, _("Message Note"));
+       camel_mime_message_set_message_id (message, message_uid);
+
+       g_object_unref (address);
+       g_free (message_uid);
+
+       attachment_store = e_attachment_view_get_store (E_ATTACHMENT_VIEW (notes_editor->attachment_paned));
+       has_attachments = e_attachment_store_get_num_attachments (attachment_store) > 0;
+
+       if (e_html_editor_view_get_html_mode (view)) {
+               CamelMultipart *multipart_alternative;
+               CamelMultipart *multipart_body;
+               CamelMimePart *part;
+               GList *inline_images = NULL;
+               gchar *text;
+
+               multipart_alternative = camel_multipart_new ();
+               camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_alternative), 
"multipart/alternative");
+               camel_multipart_set_boundary (multipart_alternative, NULL);
+
+               text = e_html_editor_view_get_text_plain (view);
+               if (text && *text) {
+                       part = camel_mime_part_new ();
+                       camel_mime_part_set_content (part, text, strlen (text), "text/plain");
+                       camel_multipart_add_part (multipart_alternative, part);
+
+                       g_object_unref (part);
+
+                       has_text = TRUE;
+               }
+
+               g_free (text);
+
+               text = e_html_editor_view_get_text_html (view, g_get_host_name (), &inline_images);
+               if (has_attachments && !has_text && (!text || !*text)) {
+                       /* Text is required, thus if there are attachments,
+                          but no text, then store at least a space. */
+                       g_free (text);
+                       text = g_strdup (" ");
+               }
+
+               if (text && *text) {
+                       part = camel_mime_part_new ();
+                       camel_mime_part_set_content (part, text, strlen (text), "text/html");
+                       camel_multipart_add_part (multipart_alternative, part);
+
+                       g_object_unref (part);
+
+                       has_text = TRUE;
+               } else {
+                       g_list_free_full (inline_images, g_object_unref);
+                       inline_images = NULL;
+               }
+
+               g_free (text);
+
+               if (inline_images) {
+                       GList *link;
+
+                       multipart_body = camel_multipart_new ();
+                       camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_body), 
"multipart/related");
+                       camel_multipart_set_boundary (multipart_body, NULL);
+
+                       part = camel_mime_part_new ();
+                       camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER 
(multipart_alternative));
+                       camel_multipart_add_part (multipart_body, part);
+                       g_object_unref (part);
+
+                       for (link = inline_images; link; link = g_list_next (link)) {
+                               CamelMimePart *part = link->data;
+
+                               if (!part)
+                                       continue;
+
+                               camel_multipart_add_part (multipart_body, part);
+                       }
+               } else {
+                       multipart_body = multipart_alternative;
+                       multipart_alternative = NULL;
+               }
+
+               if (has_attachments) {
+                       CamelMultipart *multipart;
+
+                       multipart = camel_multipart_new ();
+                       camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
+                       camel_multipart_set_boundary (multipart, NULL);
+
+                       part = camel_mime_part_new ();
+                       camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (multipart_body));
+                       camel_multipart_add_part (multipart, part);
+                       g_object_unref (part);
+
+                       e_attachment_store_add_to_multipart (attachment_store, multipart, "UTF-8");
+
+                       g_object_unref (multipart_body);
+                       multipart_body = multipart;
+               }
+
+               camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart_body));
+
+               g_list_free_full (inline_images, g_object_unref);
+               g_clear_object (&multipart_alternative);
+               g_clear_object (&multipart_body);
+       } else {
+               gchar *text;
+
+               text = e_html_editor_view_get_text_plain (view);
+
+               if (has_attachments && !has_text && (!text || !*text)) {
+                       /* Text is required, thus if there are attachments,
+                          but no text, then store at least a space. */
+                       g_free (text);
+                       text = g_strdup (" ");
+               }
+
+               if (text && *text) {
+                       if (has_attachments) {
+                               CamelMultipart *multipart;
+                               CamelMimePart *part;
+
+                               multipart = camel_multipart_new ();
+                               camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), 
"multipart/mixed");
+                               camel_multipart_set_boundary (multipart, NULL);
+
+                               part = camel_mime_part_new ();
+                               camel_mime_part_set_content (part, text, strlen (text), "text/plain");
+                               camel_multipart_add_part (multipart, part);
+                               g_object_unref (part);
+
+                               e_attachment_store_add_to_multipart (attachment_store, multipart, "UTF-8");
+
+                               camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER 
(multipart));
+
+                               g_object_unref (multipart);
+                       } else {
+                               camel_mime_part_set_content (CAMEL_MIME_PART (message), text, strlen (text), 
"text/plain");
+                       }
+                       has_text = TRUE;
+               }
+
+               g_free (text);
+       }
+
+       if (has_text) {
+               camel_mime_message_encode_8bit_parts (message);
+       } else {
+               g_clear_object (&message);
+       }
+
+       return message;
+}
+
+static void
+e_mail_notes_retrieve_message_thread (EAlertSinkThreadJobData *job_data,
+                                     gpointer user_data,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+       EMailNotesEditor *notes_editor = user_data;
+       CamelMimeMessage *message;
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+
+       message = camel_folder_get_message_sync (notes_editor->folder, notes_editor->uid, cancellable, error);
+       if (!g_cancellable_is_cancelled (cancellable))
+               notes_editor->message = message;
+       else
+               g_clear_object (&message);
+}
+
+static void
+e_mail_notes_retrieve_message_done (gpointer ptr)
+{
+       EMailNotesEditor *notes_editor = ptr;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+
+       if (notes_editor->message) {
+               EActivityBar *activity_bar;
+               CamelDataWrapper *content;
+               CamelContentType *ct;
+
+               content = camel_medium_get_content (CAMEL_MEDIUM (notes_editor->message));
+               ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (notes_editor->message));
+
+               if (ct && camel_content_type_is (ct, "multipart", "mixed") && CAMEL_IS_MULTIPART (content)) {
+                       CamelMultipart *multipart = CAMEL_MULTIPART (content);
+                       guint nparts, ii;
+
+                       nparts = camel_multipart_get_number (multipart);
+                       for (ii = 0; ii < nparts; ii++) {
+                               CamelMimePart *part;
+                               CamelContentType *ct;
+                               const gchar *x_evolution_note;
+
+                               part = camel_multipart_get_part (multipart, ii);
+                               if (!part)
+                                       continue;
+
+                               ct = camel_mime_part_get_content_type (part);
+                               if (!ct || !camel_content_type_is (ct, "message", "rfc822"))
+                                       continue;
+
+                               x_evolution_note = camel_medium_get_header (CAMEL_MEDIUM (part), 
E_MAIL_NOTES_HEADER);
+                               if (x_evolution_note) {
+                                       content = camel_medium_get_content (CAMEL_MEDIUM (part));
+                                       if (CAMEL_IS_MIME_MESSAGE (content)) {
+                                               e_mail_notes_editor_extract_text_from_message (notes_editor,
+                                                       CAMEL_MIME_MESSAGE (content));
+                                       }
+                                       break;
+                               }
+                       }
+               }
+
+               g_clear_object (&notes_editor->message);
+               notes_editor->had_message = TRUE;
+
+               activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+               e_activity_bar_set_activity (activity_bar, NULL);
+       } else {
+               GtkAction *action;
+
+               action = gtk_action_group_get_action (notes_editor->action_group, "save-and-close");
+               gtk_action_set_sensitive (action, FALSE);
+       }
+
+       g_object_unref (notes_editor);
+}
+
+static gboolean
+mail_notes_editor_delete_event_cb (EMailNotesEditor *notes_editor,
+                                  GdkEvent *event)
+{
+       GtkActionGroup *action_group;
+       GtkAction *action;
+
+       action_group = notes_editor->action_group;
+       action = gtk_action_group_get_action (action_group, "close");
+       gtk_action_activate (action);
+
+       return TRUE;
+}
+
+static void
+notes_editor_activity_notify_cb (EActivityBar *activity_bar,
+                                GParamSpec *param,
+                                EMailNotesEditor *notes_editor)
+{
+       EHTMLEditorView *view;
+       GtkAction *action;
+       gboolean can_edit;
+
+       g_return_if_fail (E_IS_ACTIVITY_BAR (activity_bar));
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+
+       view = e_html_editor_get_view (notes_editor->editor);
+       can_edit = notes_editor->had_message && !e_activity_bar_get_activity (activity_bar);
+
+       webkit_web_view_set_editable (WEBKIT_WEB_VIEW (view), can_edit);
+
+       action = gtk_action_group_get_action (notes_editor->action_group, "save-and-close");
+       gtk_action_set_sensitive (action, can_edit);
+}
+
+static gboolean
+e_mail_notes_replace_message_in_folder_sync (CamelFolder *folder,
+                                            const gchar *uid,
+                                            CamelMimeMessage *message,
+                                            gboolean has_note,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+       CamelMessageInfo *mi;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+       mi = camel_folder_get_message_info (folder, uid);
+       if (mi) {
+               CamelMessageInfo *clone;
+               gchar *appended_uid = NULL;
+
+               clone = camel_message_info_clone (mi);
+               camel_message_info_set_user_flag (clone, E_MAIL_NOTES_USER_FLAG, has_note);
+
+               success = camel_folder_append_message_sync (folder, message, clone,
+                       &appended_uid, cancellable, error);
+
+               if (success)
+                       camel_message_info_set_flags (mi, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
+
+               camel_message_info_unref (clone);
+               camel_message_info_unref (mi);
+               g_free (appended_uid);
+       } else {
+               g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot find message in its 
folder summary"));
+       }
+
+       return success;
+}
+
+static gboolean
+e_mail_notes_replace_note (CamelMimeMessage *message,
+                          CamelMimeMessage *note)
+{
+       CamelMultipart *multipart;
+       CamelMimePart *part;
+       CamelDataWrapper *orig_content;
+       CamelContentType *ct;
+
+       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+       if (note)
+               g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (note), FALSE);
+
+       orig_content = camel_medium_get_content (CAMEL_MEDIUM (message));
+       ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (message));
+       if (ct && camel_content_type_is (ct, "multipart", "mixed") && CAMEL_IS_MULTIPART (orig_content)) {
+               CamelMimePart *content_adept = NULL;
+               CamelMultipart *multipart = CAMEL_MULTIPART (orig_content);
+               guint nparts, ii;
+
+               nparts = camel_multipart_get_number (multipart);
+               for (ii = 0; ii < nparts; ii++) {
+                       CamelMimePart *part;
+                       CamelContentType *ct;
+                       const gchar *x_evolution_note;
+
+                       part = camel_multipart_get_part (multipart, ii);
+                       if (!part)
+                               continue;
+
+                       ct = camel_mime_part_get_content_type (part);
+                       if (!ct || !camel_content_type_is (ct, "message", "rfc822")) {
+                               if (content_adept) {
+                                       content_adept = NULL;
+                                       break;
+                               }
+                               content_adept = part;
+                               continue;
+                       }
+
+                       x_evolution_note = camel_medium_get_header (CAMEL_MEDIUM (part), E_MAIL_NOTES_HEADER);
+                       if (x_evolution_note)
+                               break;
+
+                       if (content_adept) {
+                               content_adept = NULL;
+                               break;
+                       }
+                       content_adept = part;
+               }
+
+               if (content_adept)
+                       orig_content = camel_medium_get_content (CAMEL_MEDIUM (content_adept));
+       }
+
+       if (!orig_content)
+               return FALSE;
+
+       g_object_ref (orig_content);
+
+       if (note) {
+               multipart = camel_multipart_new ();
+               camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
+               camel_multipart_set_boundary (multipart, NULL);
+
+               part = camel_mime_part_new ();
+               camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (orig_content));
+               camel_multipart_add_part (multipart, part);
+               g_object_unref (part);
+
+               part = camel_mime_part_new ();
+               /* Value doesn't matter, it's checked for an existence only */
+               camel_medium_add_header (CAMEL_MEDIUM (part), E_MAIL_NOTES_HEADER, "True");
+               camel_mime_part_set_disposition (CAMEL_MIME_PART (part), "inline");
+               camel_mime_part_set_description (CAMEL_MIME_PART (part), _("Message Note"));
+               camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (note));
+
+               camel_mime_part_set_content_type (part, "message/rfc822");
+
+               camel_multipart_add_part (multipart, part);
+               g_object_unref (part);
+
+               camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart));
+       } else {
+               camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (orig_content));
+       }
+
+       g_clear_object (&orig_content);
+
+       return TRUE;
+}
+
+static void
+action_close_cb (GtkAction *action,
+                EMailNotesEditor *notes_editor)
+{
+       EHTMLEditorView *view;
+       gboolean something_changed = FALSE;
+
+       view = e_html_editor_get_view (notes_editor->editor);
+
+       something_changed = webkit_web_view_can_undo (WEBKIT_WEB_VIEW (view));
+
+       if (something_changed) {
+               gint response;
+
+               response = e_alert_run_dialog_for_args (
+                       GTK_WINDOW (notes_editor),
+                       "mail:ask-mail-note-changed", NULL);
+               if (response == GTK_RESPONSE_YES) {
+                       GtkActionGroup *action_group;
+
+                       action_group = notes_editor->action_group;
+                       action = gtk_action_group_get_action (
+                               action_group, "save-and-close");
+                       gtk_action_activate (action);
+                       return;
+               } else if (response == GTK_RESPONSE_CANCEL)
+                       return;
+       }
+
+       gtk_widget_destroy (GTK_WIDGET (notes_editor));
+}
+
+typedef struct {
+       EMailNotesEditor *notes_editor;
+       CamelMimeMessage *inner_message;
+       gboolean success;
+} SaveAndCloseData;
+
+static void
+save_and_close_data_free (gpointer ptr)
+{
+       SaveAndCloseData *scd = ptr;
+
+       if (scd) {
+               if (scd->success)
+                       gtk_widget_destroy (GTK_WIDGET (scd->notes_editor));
+               else
+                       g_clear_object (&scd->notes_editor);
+               g_clear_object (&scd->inner_message);
+               g_free (scd);
+       }
+}
+
+static void
+e_mail_notes_store_changes_thread (EAlertSinkThreadJobData *job_data,
+                                  gpointer user_data,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+       CamelMimeMessage *message;
+       SaveAndCloseData *scd = user_data;
+
+       g_return_if_fail (scd != NULL);
+
+       if (g_cancellable_set_error_if_cancelled (cancellable, error))
+               return;
+
+       if (!scd->inner_message) {
+               scd->success = e_mail_notes_remove_sync (scd->notes_editor->folder,
+                       scd->notes_editor->uid, cancellable, error);
+               return;
+       }
+
+       message = camel_folder_get_message_sync (scd->notes_editor->folder, scd->notes_editor->uid, 
cancellable, error);
+       if (!message)
+               return;
+
+       e_mail_notes_replace_note (message, scd->inner_message);
+
+       scd->success = e_mail_notes_replace_message_in_folder_sync (scd->notes_editor->folder,
+               scd->notes_editor->uid, message, TRUE, cancellable, error);
+
+       g_clear_object (&message);
+}
+
+static void
+action_save_and_close_cb (GtkAction *action,
+                         EMailNotesEditor *notes_editor)
+{
+       SaveAndCloseData *scd;
+       gchar *full_display_name;
+       EActivityBar *activity_bar;
+       EActivity *activity;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+
+       scd = g_new0 (SaveAndCloseData, 1);
+       scd->notes_editor = g_object_ref (notes_editor);
+       scd->inner_message = e_mail_notes_editor_encode_text_to_message (notes_editor);
+       scd->success = FALSE;
+
+       full_display_name = e_mail_folder_to_full_display_name (notes_editor->folder, NULL);
+
+       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
+               _("Storing changes..."), "mail:failed-store-note",
+               full_display_name ? full_display_name : camel_folder_get_display_name (notes_editor->folder),
+               e_mail_notes_store_changes_thread,
+               scd, save_and_close_data_free);
+       e_activity_bar_set_activity (activity_bar, activity);
+       g_clear_object (&activity);
+
+       g_free (full_display_name);
+}
+
+static void
+e_mail_notes_editor_dispose (GObject *object)
+{
+       EMailNotesEditor *notes_editor = E_MAIL_NOTES_EDITOR (object);
+
+       if (notes_editor->editor) {
+               EActivityBar *activity_bar;
+
+               activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+               g_signal_handlers_disconnect_by_func (activity_bar,
+                       G_CALLBACK (notes_editor_activity_notify_cb), notes_editor);
+
+               notes_editor->editor = NULL;
+       }
+
+       g_clear_object (&notes_editor->focus_tracker);
+       g_clear_object (&notes_editor->action_group);
+
+       /* Chain up to parent's method */
+       G_OBJECT_CLASS (e_mail_notes_editor_parent_class)->dispose (object);
+}
+
+static void
+e_mail_notes_editor_finalize (GObject *object)
+{
+       EMailNotesEditor *notes_editor = E_MAIL_NOTES_EDITOR (object);
+
+       g_clear_object (&notes_editor->focus_tracker);
+       g_clear_object (&notes_editor->folder);
+       g_clear_object (&notes_editor->message);
+       g_free (notes_editor->uid);
+
+       /* Chain up to parent's method */
+       G_OBJECT_CLASS (e_mail_notes_editor_parent_class)->finalize (object);
+}
+
+static void
+e_mail_notes_editor_class_init (EMailNotesEditorClass *klass)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->dispose = e_mail_notes_editor_dispose;
+       object_class->finalize = e_mail_notes_editor_finalize;
+}
+
+static void
+e_mail_notes_editor_init (EMailNotesEditor *notes_editor)
+{
+}
+
+static EMailNotesEditor *
+e_mail_notes_editor_new (GtkWindow *parent,
+                        CamelFolder *folder,
+                        const gchar *uid)
+{
+       const gchar *ui =
+               "<ui>\n"
+               "  <menubar name='main-menu'>\n"
+               "    <placeholder name='pre-edit-menu'>\n"
+               "      <menu action='file-menu'>\n"
+               "        <menuitem action='save-and-close'/>\n"
+               "        <separator/>"
+               "        <menuitem action='close'/>\n"
+               "      </menu>\n"
+               "    </placeholder>\n"
+               "  </menubar>\n"
+               "  <toolbar name='main-toolbar'>\n"
+               "    <placeholder name='pre-main-toolbar'>\n"
+               "      <toolitem action='save-and-close'/>\n"
+               "    </placeholder>\n"
+               "  </toolbar>\n"
+               "</ui>";
+
+       GtkActionEntry entries[] = {
+
+               { "close",
+                 "window-close",
+                 N_("_Close"),
+                 "<Control>w",
+                 N_("Close"),
+                 G_CALLBACK (action_close_cb) },
+
+               { "save-and-close",
+                 "document-save",
+                 N_("_Save and Close"),
+                 "<Control>Return",
+                 N_("Save and Close"),
+                 G_CALLBACK (action_save_and_close_cb) },
+
+               { "file-menu",
+                 NULL,
+                 N_("_File"),
+                 NULL,
+                 NULL,
+                 NULL }
+       };
+
+       EMailNotesEditor *notes_editor;
+       EHTMLEditorView *view;
+       EFocusTracker *focus_tracker;
+       EActivityBar *activity_bar;
+       GtkUIManager *ui_manager;
+       GtkWidget *widget, *content;
+       GtkActionGroup *action_group;
+       GtkAction *action;
+       GSettings *settings;
+       GError *local_error = NULL;
+
+       notes_editor = g_object_new (E_TYPE_MAIL_NOTES_EDITOR, NULL);
+
+       g_object_set (G_OBJECT (notes_editor),
+               "transient-for", parent,
+               "destroy-with-parent", TRUE,
+               "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
+               "title", _("Edit Message Note"),
+               NULL);
+
+       gtk_window_set_default_size (GTK_WINDOW (notes_editor), 600, 440);
+
+       widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+       gtk_container_add (GTK_CONTAINER (notes_editor), widget);
+       gtk_widget_show (widget);
+
+       content = widget;
+
+       widget = e_html_editor_new ();
+
+       notes_editor->editor = E_HTML_EDITOR (widget);
+       view = e_html_editor_get_view (notes_editor->editor);
+       ui_manager = e_html_editor_get_ui_manager (notes_editor->editor);
+
+       /* Because we are loading from a hard-coded string, there is
+        * no chance of I/O errors.  Failure here implies a malformed
+        * UI definition.  Full stop. */
+       gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &local_error);
+       if (local_error != NULL)
+               g_error ("%s: Failed to load built-in UI definition: %s", G_STRFUNC, local_error->message);
+
+       action_group = gtk_action_group_new ("notes");
+       gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+       gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), notes_editor);
+       gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+       notes_editor->action_group = g_object_ref (action_group);
+
+       /* Hide page properties because it is not inherited in the mail. */
+       action = e_html_editor_get_action (notes_editor->editor, "properties-page");
+       gtk_action_set_visible (action, FALSE);
+
+       action = e_html_editor_get_action (notes_editor->editor, "context-properties-page");
+       gtk_action_set_visible (action, FALSE);
+
+       gtk_ui_manager_ensure_update (ui_manager);
+
+       /* Construct the window content. */
+
+       widget = e_html_editor_get_managed_widget (notes_editor->editor, "/main-menu");
+       gtk_box_pack_start (GTK_BOX (content), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       widget = e_html_editor_get_managed_widget (notes_editor->editor, "/main-toolbar");
+       gtk_box_pack_start (GTK_BOX (content), widget, FALSE, FALSE, 0);
+       gtk_widget_show (widget);
+
+       widget = GTK_WIDGET (notes_editor->editor);
+       gtk_box_pack_start (GTK_BOX (content), widget, TRUE, TRUE, 0);
+       gtk_widget_show (widget);
+
+       widget = e_attachment_paned_new ();
+       gtk_box_pack_start (GTK_BOX (content), widget, FALSE, FALSE, 0);
+       notes_editor->attachment_paned = E_ATTACHMENT_PANED (widget);
+       gtk_widget_show (widget);
+
+       e_binding_bind_property (
+               view, "editable",
+               widget, "sensitive",
+               G_BINDING_SYNC_CREATE);
+
+       /* Configure an EFocusTracker to manage selection actions. */
+       focus_tracker = e_focus_tracker_new (GTK_WINDOW (notes_editor));
+
+       action = e_html_editor_get_action (notes_editor->editor, "cut");
+       e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
+
+       action = e_html_editor_get_action (notes_editor->editor, "copy");
+       e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
+
+       action = e_html_editor_get_action (notes_editor->editor, "paste");
+       e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
+
+       action = e_html_editor_get_action (notes_editor->editor, "select-all");
+       e_focus_tracker_set_select_all_action (focus_tracker, action);
+
+       notes_editor->focus_tracker = focus_tracker;
+
+       gtk_widget_grab_focus (GTK_WIDGET (view));
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       e_html_editor_view_set_html_mode (view, g_settings_get_boolean (settings, "composer-send-html"));
+       g_object_unref (settings);
+
+       g_signal_connect (
+               notes_editor, "delete-event",
+               G_CALLBACK (mail_notes_editor_delete_event_cb), NULL);
+
+       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+
+       g_signal_connect (activity_bar, "notify::activity",
+               G_CALLBACK (notes_editor_activity_notify_cb), notes_editor);
+
+       notes_editor->folder = g_object_ref (folder);
+       notes_editor->uid = g_strdup (uid);
+       notes_editor->had_message = FALSE;
+
+       return notes_editor;
+}
+
+void
+e_mail_notes_edit (GtkWindow *parent,
+                  CamelFolder *folder,
+                  const gchar *uid)
+{
+       EMailNotesEditor *notes_editor;
+       EActivityBar *activity_bar;
+       EActivity *activity;
+
+       g_return_if_fail (CAMEL_IS_FOLDER (folder));
+       g_return_if_fail (uid != NULL);
+
+       notes_editor = e_mail_notes_editor_new (parent, folder, uid);
+
+       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
+               _("Retrieving message..."), "mail:no-retrieve-message", NULL,
+               e_mail_notes_retrieve_message_thread,
+               g_object_ref (notes_editor), e_mail_notes_retrieve_message_done);
+       e_activity_bar_set_activity (activity_bar, activity);
+       g_clear_object (&activity);
+
+       gtk_widget_show (GTK_WIDGET (notes_editor));
+}
+
+gboolean
+e_mail_notes_remove_sync (CamelFolder *folder,
+                         const gchar *uid,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       CamelMimeMessage *message;
+       gboolean success;
+
+       g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+       g_return_val_if_fail (uid != NULL, FALSE);
+
+       message = camel_folder_get_message_sync (folder, uid, cancellable, error);
+       if (!message)
+               return FALSE;
+
+       success = e_mail_notes_replace_note (message, NULL);
+       if (success) {
+               success = e_mail_notes_replace_message_in_folder_sync (folder,
+                       uid, message, FALSE, cancellable, error);
+       } else {
+               /* There was no note found in the message, thus it was successfully removed */
+               success = TRUE;
+       }
+
+       g_clear_object (&message);
+
+       return success;
+}
diff --git a/mail/e-mail-notes.h b/mail/e-mail-notes.h
new file mode 100644
index 0000000..fdfbf02
--- /dev/null
+++ b/mail/e-mail-notes.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef E_MAIL_NOTES_H
+#define E_MAIL_NOTES_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include <camel/camel.h>
+
+#define E_MAIL_NOTES_USER_FLAG "$has_note"
+#define E_MAIL_NOTES_HEADER "X-Evolution-Note"
+
+G_BEGIN_DECLS
+
+void           e_mail_notes_edit               (GtkWindow *parent,
+                                                CamelFolder *folder,
+                                                const gchar *uid);
+gboolean       e_mail_notes_remove_sync        (CamelFolder *folder,
+                                                const gchar *uid,
+                                                GCancellable *cancellable,
+                                                GError **error);
+
+G_END_DECLS
+
+#endif /* E_MAIL_NOTES_H */
diff --git a/mail/e-mail-reader-utils.c b/mail/e-mail-reader-utils.c
index f154f1e..179f147 100644
--- a/mail/e-mail-reader-utils.c
+++ b/mail/e-mail-reader-utils.c
@@ -1193,6 +1193,10 @@ e_mail_reader_mark_selected_ignore_thread (EMailReader *reader,
                                camel_folder_get_full_name (folder), 
mail_reader_utils_mark_ignore_thread_thread,
                                mit, mark_ignore_thread_data_free);
 
+
+                       if (activity)
+                               e_shell_backend_add_activity (E_SHELL_BACKEND (e_mail_reader_get_backend 
(reader)), activity);
+
                        g_clear_object (&activity);
                }
 
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 33e49c9..8e6b970 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -42,6 +42,7 @@
 #include "e-mail-backend.h"
 #include "e-mail-browser.h"
 #include "e-mail-enumtypes.h"
+#include "e-mail-notes.h"
 #include "e-mail-reader-utils.h"
 #include "e-mail-ui-session.h"
 #include "e-mail-view.h"
@@ -409,6 +410,100 @@ action_mail_copy_cb (GtkAction *action,
 }
 
 static void
+action_mail_edit_note_cb (GtkAction *action,
+                         EMailReader *reader)
+{
+       CamelFolder *folder;
+       GPtrArray *uids;
+
+       folder = e_mail_reader_ref_folder (reader);
+       uids = e_mail_reader_get_selected_uids (reader);
+
+       if (uids && uids->len == 1) {
+               e_mail_notes_edit (e_mail_reader_get_window (reader), folder, uids->pdata[0]);
+       } else {
+               g_warn_if_reached ();
+       }
+
+       g_clear_object (&folder);
+       g_ptr_array_unref (uids);
+}
+
+typedef struct {
+       CamelFolder *folder;
+       gchar *uid;
+} DeleteNoteData;
+
+static void
+delete_note_data_free (gpointer ptr)
+{
+       DeleteNoteData *dnd = ptr;
+
+       if (dnd) {
+               g_clear_object (&dnd->folder);
+               g_free (dnd->uid);
+               g_free (dnd);
+       }
+}
+
+static void
+mail_delete_note_thread (EAlertSinkThreadJobData *job_data,
+                        gpointer user_data,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       DeleteNoteData *dnd = user_data;
+
+       g_return_if_fail (dnd != NULL);
+       g_return_if_fail (CAMEL_IS_FOLDER (dnd->folder));
+       g_return_if_fail (dnd->uid != NULL);
+
+       e_mail_notes_remove_sync (dnd->folder, dnd->uid, cancellable, error);
+}
+
+static void
+action_mail_delete_note_cb (GtkAction *action,
+                           EMailReader *reader)
+{
+       CamelFolder *folder;
+       GPtrArray *uids;
+
+       folder = e_mail_reader_ref_folder (reader);
+       uids = e_mail_reader_get_selected_uids (reader);
+
+       if (uids && uids->len == 1) {
+               DeleteNoteData *dnd;
+               EAlertSink *alert_sink;
+               EActivity *activity;
+               gchar *full_display_name;
+
+               dnd = g_new0 (DeleteNoteData, 1);
+               dnd->folder = g_object_ref (folder);
+               dnd->uid = g_strdup (uids->pdata[0]);
+
+               full_display_name = e_mail_folder_to_full_display_name (folder, NULL);
+               alert_sink = e_mail_reader_get_alert_sink (reader);
+
+               activity = e_alert_sink_submit_thread_job (alert_sink,
+                       _("Deleting message note..."),
+                       "mail:failed-delete-note",
+                       full_display_name ? full_display_name : camel_folder_get_full_name (folder),
+                       mail_delete_note_thread, dnd, delete_note_data_free);
+
+               if (activity)
+                       e_shell_backend_add_activity (E_SHELL_BACKEND (e_mail_reader_get_backend (reader)), 
activity);
+
+               g_clear_object (&activity);
+               g_free (full_display_name);
+       } else {
+               g_warn_if_reached ();
+       }
+
+       g_clear_object (&folder);
+       g_ptr_array_unref (uids);
+}
+
+static void
 action_mail_delete_cb (GtkAction *action,
                        EMailReader *reader)
 {
@@ -1924,6 +2019,27 @@ static GtkActionEntry mail_reader_entries[] = {
          N_("Mark the selected messages for deletion"),
          G_CALLBACK (action_mail_delete_cb) },
 
+       { "mail-add-note",
+         "evolution-memos",
+         N_("_Add note..."),
+         NULL,
+         N_("Add a note for the selected message"),
+         G_CALLBACK (action_mail_edit_note_cb) },
+
+       { "mail-delete-note",
+         NULL,
+         N_("Delete no_te"),
+         NULL,
+         N_("Delete the note for the selected message"),
+         G_CALLBACK (action_mail_delete_note_cb) },
+
+       { "mail-edit-note",
+         "evolution-memos",
+         N_("_Edit note..."),
+         NULL,
+         N_("Edit a note for the selected message"),
+         G_CALLBACK (action_mail_edit_note_cb) },
+
        { "mail-filter-rule-for-mailing-list",
          NULL,
          N_("Create a Filter Rule for Mailing _List..."),
@@ -2399,6 +2515,18 @@ static EPopupActionEntry mail_reader_popup_entries[] = {
          NULL,
          "mail-delete" },
 
+       { "mail-popup-add-note",
+         NULL,
+         "mail-add-note" },
+
+       { "mail-popup-delete-note",
+         NULL,
+         "mail-delete-note" },
+
+       { "mail-popup-edit-note",
+         NULL,
+         "mail-edit-note" },
+
        { "mail-popup-flag-clear",
          NULL,
          "mail-flag-clear" },
@@ -3495,6 +3623,7 @@ mail_reader_update_actions (EMailReader *reader,
        gboolean selection_has_undeleted_messages;
        gboolean selection_has_unimportant_messages;
        gboolean selection_has_unread_messages;
+       gboolean selection_has_mail_note;
        gboolean selection_is_mailing_list;
        gboolean single_message_selected;
        gboolean first_message_selected = FALSE;
@@ -3535,6 +3664,8 @@ mail_reader_update_actions (EMailReader *reader,
                (state & E_MAIL_READER_SELECTION_HAS_UNIMPORTANT);
        selection_has_unread_messages =
                (state & E_MAIL_READER_SELECTION_HAS_UNREAD);
+       selection_has_mail_note =
+               (state & E_MAIL_READER_SELECTION_HAS_MAIL_NOTE);
        selection_is_mailing_list =
                (state & E_MAIL_READER_SELECTION_IS_MAILING_LIST);
 
@@ -3600,6 +3731,24 @@ mail_reader_update_actions (EMailReader *reader,
        action = e_mail_reader_get_action (reader, action_name);
        gtk_action_set_sensitive (action, sensitive);
 
+       action_name = "mail-add-note";
+       sensitive = single_message_selected && !selection_has_mail_note;
+       action = e_mail_reader_get_action (reader, action_name);
+       gtk_action_set_sensitive (action, sensitive);
+       gtk_action_set_visible (action, sensitive);
+
+       action_name = "mail-edit-note";
+       sensitive = single_message_selected && selection_has_mail_note;
+       action = e_mail_reader_get_action (reader, action_name);
+       gtk_action_set_sensitive (action, sensitive);
+       gtk_action_set_visible (action, sensitive);
+
+       action_name = "mail-delete-note";
+       sensitive = single_message_selected && selection_has_mail_note;
+       action = e_mail_reader_get_action (reader, action_name);
+       gtk_action_set_sensitive (action, sensitive);
+       gtk_action_set_visible (action, sensitive);
+
        action_name = "mail-filters-apply";
        sensitive = any_messages_selected;
        action = e_mail_reader_get_action (reader, action_name);
@@ -4353,6 +4502,7 @@ e_mail_reader_check_state (EMailReader *reader)
        gboolean has_undeleted = FALSE;
        gboolean has_unimportant = FALSE;
        gboolean has_unread = FALSE;
+       gboolean has_mail_note = FALSE;
        gboolean have_enabled_account = FALSE;
        gboolean drafts_or_outbox = FALSE;
        gboolean store_supports_vjunk = FALSE;
@@ -4408,6 +4558,9 @@ e_mail_reader_check_state (EMailReader *reader)
                if (info == NULL)
                        continue;
 
+               if (camel_message_info_user_flag (info, E_MAIL_NOTES_USER_FLAG))
+                       has_mail_note = TRUE;
+
                flags = camel_message_info_flags (info);
 
                if (flags & CAMEL_MESSAGE_SEEN)
@@ -4529,6 +4682,8 @@ e_mail_reader_check_state (EMailReader *reader)
                state |= E_MAIL_READER_FOLDER_IS_VTRASH;
        if (archive_folder_set)
                state |= E_MAIL_READER_FOLDER_ARCHIVE_FOLDER_SET;
+       if (has_mail_note)
+               state |= E_MAIL_READER_SELECTION_HAS_MAIL_NOTE;
 
        g_clear_object (&folder);
        g_ptr_array_unref (uids);
diff --git a/mail/e-mail-reader.h b/mail/e-mail-reader.h
index 7dde175..a2cebaf 100644
--- a/mail/e-mail-reader.h
+++ b/mail/e-mail-reader.h
@@ -90,7 +90,8 @@ enum {
        E_MAIL_READER_FOLDER_IS_VTRASH = 1 << 18,
        E_MAIL_READER_FOLDER_ARCHIVE_FOLDER_SET = 1 << 19,
        E_MAIL_READER_SELECTION_HAS_IGNORE_THREAD = 1 << 20,
-       E_MAIL_READER_SELECTION_HAS_NOTIGNORE_THREAD = 1 << 21
+       E_MAIL_READER_SELECTION_HAS_NOTIGNORE_THREAD = 1 << 21,
+       E_MAIL_READER_SELECTION_HAS_MAIL_NOTE = 1 << 22
 };
 
 struct _EMailReaderInterface {
diff --git a/mail/e-mail.h b/mail/e-mail.h
index fe982c7..b579e88 100644
--- a/mail/e-mail.h
+++ b/mail/e-mail.h
@@ -29,6 +29,7 @@
 #include <mail/e-mail-label-tree-view.h>
 #include <mail/e-mail-message-pane.h>
 #include <mail/e-mail-migrate.h>
+#include <mail/e-mail-notes.h>
 #include <mail/e-mail-paned-view.h>
 #include <mail/e-mail-reader.h>
 #include <mail/e-mail-reader-utils.h>
diff --git a/mail/filtertypes.xml.in b/mail/filtertypes.xml.in
index 829874c..3f703e6 100644
--- a/mail/filtertypes.xml.in
+++ b/mail/filtertypes.xml.in
@@ -790,6 +790,24 @@
   </input>
  </part>
 
+ <part name="notes">
+  <_title>Notes</_title>
+  <input type="optionlist" name="match-type">
+   <option value="exist">
+    <_title>Exist</_title>
+    <code>
+     (match-all (user-flag "$has_note"))
+    </code>
+   </option>
+   <option value="not exist">
+    <_title>Do Not Exist</_title>
+    <code>
+     (match-all (not (user-flag "$has_note")))
+    </code>
+   </option>
+  </input>
+ </part>
+
  <part name="mlist">
   <_title>Mailing list</_title>
    <input type="optionlist" name="mlist-type">
diff --git a/mail/mail.error.xml b/mail/mail.error.xml
index 020ddb8..259ece2 100644
--- a/mail/mail.error.xml
+++ b/mail/mail.error.xml
@@ -599,4 +599,24 @@ in the folder will be available in offline mode.</_secondary>
     <!-- Translators: This constructs a string like "Overdue: Follow-up by Tuesday, January 13, 2009" -->
     <_primary>Overdue: {0} by {1}</_primary>
   </error>
+
+  <error id="ask-mail-note-changed" type="question" default="GTK_RESPONSE_YES">
+    <_primary>Do you wish to save your changes?</_primary>
+    <_secondary xml:space="preserve">This message note has been changed, but has not been saved.</_secondary>
+    <button _label="_Discard changes" response="GTK_RESPONSE_NO"/>
+    <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+    <button stock="gtk-save" response="GTK_RESPONSE_YES"/>
+  </error>
+
+  <error id="failed-delete-note" type="error">
+    <!-- Translators: {0} is the name of the folder where the delete of the message note failed. -->
+    <_primary>Failed to delete message note in folder '{0}'</_primary>
+    <secondary>{1}</secondary>
+  </error>
+
+  <error id="failed-store-note" type="error">
+    <!-- Translators: {0} is the name of the folder where the store of the message note failed. -->
+    <_primary>Failed to store message note in folder '{0}'</_primary>
+    <secondary>{1}</secondary>
+  </error>
 </error-list>
diff --git a/mail/message-list.c b/mail/message-list.c
index e89a865..d7a43bd 100644
--- a/mail/message-list.c
+++ b/mail/message-list.c
@@ -34,6 +34,7 @@
 #include <glib/gstdio.h>
 
 #include "e-mail-label-list-store.h"
+#include "e-mail-notes.h"
 #include "e-mail-ui-session.h"
 #include "em-utils.h"
 
@@ -328,7 +329,8 @@ static const gchar *score_icons[] = {
 static const gchar *attachment_icons[] = {
        NULL,  /* empty icon */
        "mail-attachment",
-       "stock_new-meeting"
+       "stock_new-meeting",
+       "evolution-memos"
 };
 
 static const gchar *flagged_icons[] = {
@@ -1818,6 +1820,8 @@ ml_tree_value_at_ex (ETreeModel *etm,
                str = camel_message_info_user_tag (msg_info, "follow-up");
                return (gpointer)(str ? str : "");
        case COL_ATTACHMENT:
+               if (camel_message_info_user_flag (msg_info, E_MAIL_NOTES_USER_FLAG))
+                       return GINT_TO_POINTER (3);
                if (camel_message_info_user_flag (msg_info, "$has_cal"))
                        return GINT_TO_POINTER (2);
                return GINT_TO_POINTER ((camel_message_info_flags (msg_info) & CAMEL_MESSAGE_ATTACHMENTS) != 
0);
diff --git a/mail/searchtypes.xml.in b/mail/searchtypes.xml.in
index 88f4f8d..64fa7ce 100644
--- a/mail/searchtypes.xml.in
+++ b/mail/searchtypes.xml.in
@@ -875,6 +875,24 @@
   </input>
  </part>
 
+ <part name="notes">
+  <_title>Notes</_title>
+  <input type="optionlist" name="match-type">
+   <option value="exist">
+    <_title>Exist</_title>
+    <code>
+     (match-all (user-flag "$has_note"))
+    </code>
+   </option>
+   <option value="not exist">
+    <_title>Do Not Exist</_title>
+    <code>
+     (match-all (not (user-flag "$has_note")))
+    </code>
+   </option>
+  </input>
+ </part>
+
  <part name="mlist">
   <_title>Mailing list</_title>
    <input type="optionlist" name="mlist-type">
diff --git a/mail/vfoldertypes.xml.in b/mail/vfoldertypes.xml.in
index 142387c..617c881 100644
--- a/mail/vfoldertypes.xml.in
+++ b/mail/vfoldertypes.xml.in
@@ -874,6 +874,24 @@
   </input>
  </part>
 
+ <part name="notes">
+  <_title>Notes</_title>
+  <input type="optionlist" name="match-type">
+   <option value="exist">
+    <_title>Exist</_title>
+    <code>
+     (match-all (user-flag "$has_note"))
+    </code>
+   </option>
+   <option value="not exist">
+    <_title>Do Not Exist</_title>
+    <code>
+     (match-all (not (user-flag "$has_note")))
+    </code>
+   </option>
+  </input>
+ </part>
+
  <part name="mlist">
   <_title>Mailing list</_title>
    <input type="optionlist" name="mlist-type">
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index 08c0749..71a29c3 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -2079,6 +2079,13 @@ static GtkRadioActionEntry mail_filter_entries[] = {
          NULL,  /* XXX Add a tooltip! */
          MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS },
 
+       { "mail-filter-messages-with-notes",
+         "evolution-memos",
+         N_("Messages with Notes"),
+         NULL,
+         NULL,  /* XXX Add a tooltip! */
+         MAIL_FILTER_MESSAGES_WITH_NOTES },
+
        { "mail-filter-no-label",
          NULL,
          N_("No Label"),
diff --git a/modules/mail/e-mail-shell-view-actions.h b/modules/mail/e-mail-shell-view-actions.h
index aa75a2b..ed1d58d 100644
--- a/modules/mail/e-mail-shell-view-actions.h
+++ b/modules/mail/e-mail-shell-view-actions.h
@@ -230,6 +230,8 @@
        E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-not-junk")
 #define E_SHELL_WINDOW_ACTION_MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS(window) \
        E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-with-attachments")
+#define E_SHELL_WINDOW_ACTION_MAIL_FILTER_MESSAGES_WITH_NOTES(window) \
+       E_SHELL_WINDOW_ACTION ((window), "mail-filter-messages-with-notes")
 #define E_SHELL_WINDOW_ACTION_MAIL_FILTER_NO_LABEL(window) \
        E_SHELL_WINDOW_ACTION ((window), "mail-filter-no-label")
 #define E_SHELL_WINDOW_ACTION_MAIL_FILTER_READ_MESSAGES(window) \
diff --git a/modules/mail/e-mail-shell-view-private.h b/modules/mail/e-mail-shell-view-private.h
index b0a7933..106562a 100644
--- a/modules/mail/e-mail-shell-view-private.h
+++ b/modules/mail/e-mail-shell-view-private.h
@@ -83,8 +83,9 @@ enum {
        MAIL_FILTER_READ_MESSAGES = 5000,
        MAIL_FILTER_LAST_5_DAYS_MESSAGES = 5001,
        MAIL_FILTER_MESSAGES_WITH_ATTACHMENTS = 5002,
-       MAIL_FILTER_IMPORTANT_MESSAGES = 5003,
-       MAIL_FILTER_MESSAGES_NOT_JUNK = 5004
+       MAIL_FILTER_MESSAGES_WITH_NOTES = 5003,
+       MAIL_FILTER_IMPORTANT_MESSAGES = 5004,
+       MAIL_FILTER_MESSAGES_NOT_JUNK = 5005
 };
 
 /* Search items are displayed in ascending order. */
diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c
index bb67bfd..1deb5b0 100644
--- a/modules/mail/e-mail-shell-view.c
+++ b/modules/mail/e-mail-shell-view.c
@@ -553,6 +553,13 @@ filter:
                        query = temp;
                        break;
 
+               case MAIL_FILTER_MESSAGES_WITH_NOTES:
+                       temp = g_strdup_printf (
+                               "(and %s (match-all (user-flag \"$has_note\")))", query);
+                       g_free (query);
+                       query = temp;
+                       break;
+
                case MAIL_FILTER_IMPORTANT_MESSAGES:
                        temp = g_strdup_printf (
                                "(and %s (match-all "
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 596a294..01d845e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -355,6 +355,7 @@ mail/e-mail-label-list-store.c
 mail/e-mail-label-manager.c
 mail/e-mail-label-tree-view.c
 mail/e-mail-migrate.c
+mail/e-mail-notes.c
 mail/e-mail-print-config-headers.c
 mail/e-mail-printer.c
 mail/e-mail-reader.c
diff --git a/ui/evolution-mail-reader.ui b/ui/evolution-mail-reader.ui
index 041c295..edc9fd1 100644
--- a/ui/evolution-mail-reader.ui
+++ b/ui/evolution-mail-reader.ui
@@ -98,6 +98,10 @@
         <menuitem action='mail-remove-attachments'/>
         <menuitem action='mail-remove-duplicates'/>
         <separator/>
+        <menuitem action='mail-add-note'/>
+        <menuitem action='mail-edit-note'/>
+        <menuitem action='mail-delete-note'/>
+        <separator/>
         <menu action='mail-create-menu'>
           <placeholder action='mail-conversion-actions'/>
           <separator/>
@@ -169,6 +173,10 @@
     <menuitem action='mail-popup-save-as'/>
     <menuitem action='mail-popup-print'/>
     <separator/>
+    <menuitem action='mail-add-note'/>
+    <menuitem action='mail-edit-note'/>
+    <menuitem action='mail-delete-note'/>
+    <separator/>
     <placeholder name='mail-message-popup-actions'/>
   </popup>
 </ui>
diff --git a/ui/evolution-mail.ui b/ui/evolution-mail.ui
index c7cec4f..dac6a13 100644
--- a/ui/evolution-mail.ui
+++ b/ui/evolution-mail.ui
@@ -114,6 +114,11 @@
     <menuitem action='mail-popup-flag-for-followup'/>
     <menuitem action="mail-popup-flag-clear"/>
     <menuitem action="mail-popup-flag-completed"/>
+    <separator/>
+    <menuitem action='mail-popup-add-note'/>
+    <menuitem action='mail-popup-edit-note'/>
+    <menuitem action='mail-popup-delete-note'/>
+    <separator/>
     <menuitem action='mail-popup-mark-ignore-thread-whole'/>
     <menuitem action='mail-popup-mark-ignore-thread-sub'/>
     <menuitem action='mail-popup-mark-unignore-thread-whole'/>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]