[evolution/449-support-markdown-in-composer: 3/3] I#449 - Support markdown in composer




commit 7153d6d2c03d340ea0c1e68021ab4696cb1e7562
Author: Milan Crha <mcrha redhat com>
Date:   Thu Feb 3 14:49:44 2022 +0100

    I#449 - Support markdown in composer
    
    Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/449

 data/evolution.convert                             |    1 -
 data/icons/CMakeLists.txt                          |    2 +
 ...icolor_actions_scalable_markdown-emoji-dark.svg |   25 +
 .../hicolor_actions_scalable_markdown-emoji.svg    |   25 +
 data/org.gnome.evolution.mail.gschema.xml.in       |   27 +-
 src/composer/e-composer-actions.c                  |   44 +-
 src/composer/e-composer-private.c                  |   37 +-
 src/composer/e-msg-composer.c                      |  284 +++-
 src/e-util/CMakeLists.txt                          |    2 +
 src/e-util/e-content-editor.c                      |  154 +-
 src/e-util/e-content-editor.h                      |   22 +-
 src/e-util/e-html-editor-actions.c                 |  276 ++-
 src/e-util/e-html-editor-find-dialog.c             |   77 +-
 src/e-util/e-html-editor-manager.ui                |    3 +
 src/e-util/e-html-editor-private.h                 |   24 +-
 src/e-util/e-html-editor-replace-dialog.c          |   60 +-
 src/e-util/e-html-editor.c                         |  639 ++++++-
 src/e-util/e-html-editor.h                         |   13 +
 src/e-util/e-mail-signature-combo-box.c            |   22 +-
 src/e-util/e-mail-signature-combo-box.h            |    3 +-
 src/e-util/e-mail-signature-editor.c               |   61 +-
 src/e-util/e-mail-signature-manager.c              |   50 +-
 src/e-util/e-mail-signature-manager.h              |    8 +-
 src/e-util/e-markdown-editor.c                     | 1779 ++++++++++++++++++--
 src/e-util/e-markdown-editor.h                     |   26 +-
 src/e-util/e-markdown-utils.c                      |  608 +++++++
 src/e-util/e-markdown-utils.h                      |   28 +
 src/e-util/e-spell-text-view.c                     |   82 +
 src/e-util/e-spell-text-view.h                     |    5 +
 src/e-util/e-util-enums.h                          |   38 +
 src/e-util/e-util.h                                |    1 +
 src/e-util/test-html-editor-units-utils.c          |   18 +-
 src/e-util/test-html-editor.c                      |   22 +-
 src/e-util/test-mail-signatures.c                  |    6 +-
 src/e-util/test-markdown-editor.c                  |  311 +++-
 src/em-format/e-mail-formatter-text-markdown.c     |    2 +-
 src/mail/e-mail-enums.h                            |   27 +-
 src/mail/e-mail-notes.c                            |  244 ++-
 src/mail/em-composer-utils.c                       |   96 +-
 src/mail/mail-config.ui                            |   26 +-
 .../composer-to-meeting/e-meeting-to-composer.c    |   13 +-
 src/modules/mail/e-mail-shell-backend.c            |   16 +-
 src/modules/mail/em-composer-prefs.c               |   65 +-
 src/modules/webkit-editor/e-webkit-editor.c        |  137 +-
 44 files changed, 4646 insertions(+), 763 deletions(-)
---
diff --git a/data/evolution.convert b/data/evolution.convert
index c434d52952..470a6cf56c 100644
--- a/data/evolution.convert
+++ b/data/evolution.convert
@@ -121,7 +121,6 @@ composer-no-signature-delim = /apps/evolution/mail/composer/no_signature_delim
 composer-outlook-filenames = /apps/evolution/mail/composer/outlook_filenames
 composer-reply-start-bottom = /apps/evolution/mail/composer/reply_start_bottom
 composer-request-receipt = /apps/evolution/mail/composer/request_receipt
-composer-send-html = /apps/evolution/mail/composer/send_html
 composer-show-bcc = /apps/evolution/mail/composer/show_mail_bcc
 composer-show-cc = /apps/evolution/mail/composer/show_mail_cc
 composer-show-post-from = /apps/evolution/mail/composer/show_post_from
diff --git a/data/icons/CMakeLists.txt b/data/icons/CMakeLists.txt
index d6cbfb2b3f..64d2395b32 100644
--- a/data/icons/CMakeLists.txt
+++ b/data/icons/CMakeLists.txt
@@ -98,6 +98,8 @@ set(private_icons
        hicolor_actions_scalable_markdown-bullets-dark.svg
        hicolor_actions_scalable_markdown-code.svg
        hicolor_actions_scalable_markdown-code-dark.svg
+       hicolor_actions_scalable_markdown-emoji.svg
+       hicolor_actions_scalable_markdown-emoji-dark.svg
        hicolor_actions_scalable_markdown-header.svg
        hicolor_actions_scalable_markdown-header-dark.svg
        hicolor_actions_scalable_markdown-help.svg
diff --git a/data/icons/hicolor_actions_scalable_markdown-emoji-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-emoji-dark.svg
new file mode 100644
index 0000000000..a80c068f3b
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-emoji-dark.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg33406"
+   xml:space="preserve"
+   width="20"
+   height="20"
+   viewBox="0 0 20 20"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="m 1.9005051,10.0005 c -6.75e-4,-4.050675 4.049325,-8.100675 8.1000409,-8.0999595 4.050715,7.155e-4 
8.100634,4.0506345 8.099919,8.1000005 -7.16e-4,4.049365 -4.050635,8.099284 -8.1,8.099919 -4.0493659,6.34e-4 
-8.0992849,-4.049285 -8.0999599,-8.09996 z"
+     id="path11753" /><path
+     
style="fill:#e6e6e6;fill-rule:evenodd;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+     d="m 7.0712862,5.7713633 c 1.0005,5e-4 2.0004999,1.0005001 1.9999899,2.00001 -5.1e-4,0.9995099 
-1.00049,1.9994899 -1.9999999,1.9999799 -0.9995099,4.9e-4 -1.9994899,-0.99949 -1.99998,-1.9999999 
-4.9e-4,-1.00051 0.99949,-2.00049 1.99999,-1.99999 z"
+     id="path12190" /><path
+     
style="fill:#e6e6e6;fill-rule:evenodd;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+     d="m 12.96911,5.728406 c 1.0005,5e-4 2.0005,1.0005 1.99999,2.00001 -5.1e-4,0.99951 -1.00049,1.99949 
-2,1.99998 -0.99951,4.899e-4 -1.99949,-0.9994902 -1.99998,-2 -4.9e-4,-1.0005098 0.99949,-2.0004899 
1.99999,-1.99999 z"
+     id="path12190-3" /><path
+     
style="fill:#e6e6e6;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="m 5.1486969,12.08349 5.0469071,2.521786 4.953093,-2.521786 -5,1 z"
+     id="path12608" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-emoji.svg 
b/data/icons/hicolor_actions_scalable_markdown-emoji.svg
new file mode 100644
index 0000000000..10eec0364b
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-emoji.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg33406"
+   xml:space="preserve"
+   width="20"
+   height="20"
+   viewBox="0 0 20 20"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="m 1.9005051,10.0005 c -6.75e-4,-4.050675 4.049325,-8.100675 8.1000409,-8.0999595 4.050715,7.155e-4 
8.100634,4.0506345 8.099919,8.1000005 -7.16e-4,4.049365 -4.050635,8.099284 -8.1,8.099919 -4.0493659,6.34e-4 
-8.0992849,-4.049285 -8.0999599,-8.09996 z"
+     id="path11753" /><path
+     
style="fill:#1a1a1a;fill-rule:evenodd;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+     d="m 7.0712862,5.7713633 c 1.0005,5e-4 2.0004999,1.0005001 1.9999899,2.00001 -5.1e-4,0.9995099 
-1.00049,1.9994899 -1.9999999,1.9999799 -0.9995099,4.9e-4 -1.9994899,-0.99949 -1.99998,-1.9999999 
-4.9e-4,-1.00051 0.99949,-2.00049 1.99999,-1.99999 z"
+     id="path12190" /><path
+     
style="fill:#1a1a1a;fill-rule:evenodd;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+     d="m 12.96911,5.728406 c 1.0005,5e-4 2.0005,1.0005 1.99999,2.00001 -5.1e-4,0.99951 -1.00049,1.99949 
-2,1.99998 -0.99951,4.899e-4 -1.99949,-0.9994902 -1.99998,-2 -4.9e-4,-1.0005098 0.99949,-2.0004899 
1.99999,-1.99999 z"
+     id="path12190-3" /><path
+     
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="m 5.1486969,12.08349 5.0469071,2.521786 4.953093,-2.521786 -5,1 z"
+     id="path12608" /></svg>
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index 11e233f982..f97b99f9ce 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -44,6 +44,16 @@
     <value nick='books'         value='2'/>
   </enum>
 
+  <!-- Keep this synchronized with EContentEditorMode. -->
+  <enum id="org.gnome.evolution.mail.ContentEditorMode">
+    <value nick='unknown'             value='-1'/>
+    <value nick='plain-text'          value='0'/>
+    <value nick='html'                value='1'/>
+    <value nick='markdown'            value='2'/>
+    <value nick='markdown-plain-text' value='3'/>
+    <value nick='markdown-html'       value='4'/>
+  </enum>
+
   <schema gettext-domain="evolution" id="org.gnome.evolution.mail" path="/org/gnome/evolution/mail/">
     <key name="prompt-check-if-default-mailer" type="b">
       <default>true</default>
@@ -57,8 +67,8 @@
     </key>
     <key name="composer-editor" type="s">
       <default>''</default>
-      <_summary>Name of the editor to prefer in the message composer</_summary>
-      <_description>If the name doesn’t correspond to any known editor, then the built-in WebKit editor is 
used.</_description>
+      <_summary>Comma-separated names of the editor to prefer in the message composer</_summary>
+      <_description>If the name doesn’t correspond to any known editor, then the built-in WebKit editor is 
used. The mode is optional, in which case the editor is used for all the modes it supports. Modes are 
“plain”, “html”, "markdown-plain", "markdown-html" and "markdown". Example values: “webkit” (to use WebKit 
for plain and html), “plain:first-editor,html:second-editor” (to use “first-editor” for the “plain” and 
“second-editor” for “html”)</_description>
     </key>
     <key name="composer-gallery-path" type="s">
       <default>''</default>
@@ -115,10 +125,9 @@
       <_summary>Always request read receipt</_summary>
       <_description>Whether a read receipt request gets added to every message by default.</_description>
     </key>
-    <key name="composer-send-html" type="b">
-      <default>false</default>
-      <_summary>Send HTML mail by default</_summary>
-      <_description>Send HTML mail by default.</_description>
+    <key name="composer-mode" enum="org.gnome.evolution.mail.ContentEditorMode">
+      <default>'plain-text'</default>
+      <_summary>What mode open the composer with</_summary>
     </key>
     <key name="composer-spell-languages" type="as">
       <default>[]</default>
@@ -800,9 +809,9 @@
       <default>'quoted'</default>
       <_summary>Alternative reply style</_summary>
     </key>
-    <key name="alt-reply-html-format" enum="org.gnome.evolution.mail.ThreeState">
-      <default>'inconsistent'</default>
-      <_summary>Format message in HTML</_summary>
+    <key name="alt-reply-format-mode" enum="org.gnome.evolution.mail.ContentEditorMode">
+      <default>'unknown'</default>
+      <_summary>Composer mode to use.</_summary>
     </key>
     <key name="alt-reply-start-bottom" enum="org.gnome.evolution.mail.ThreeState">
       <default>'inconsistent'</default>
diff --git a/src/composer/e-composer-actions.c b/src/composer/e-composer-actions.c
index 5cb03cab94..67adb5e15d 100644
--- a/src/composer/e-composer-actions.c
+++ b/src/composer/e-composer-actions.c
@@ -558,6 +558,35 @@ static GtkToggleActionEntry toggle_entries[] = {
          FALSE }
 };
 
+static gboolean
+eca_transform_mode_html_to_boolean_cb (GBinding *binding,
+                                      const GValue *source_value,
+                                      GValue *target_value,
+                                      gpointer not_used)
+{
+       g_value_set_boolean (target_value, g_value_get_enum (source_value) == E_CONTENT_EDITOR_MODE_HTML);
+
+       return TRUE;
+}
+
+static gboolean
+eca_mode_to_bool_hide_in_markdown_cb (GBinding *binding,
+                                     const GValue *from_value,
+                                     GValue *to_value,
+                                     gpointer user_data)
+{
+       EContentEditorMode mode;
+
+       mode = g_value_get_enum (from_value);
+
+       g_value_set_boolean (to_value,
+               mode != E_CONTENT_EDITOR_MODE_MARKDOWN &&
+               mode != E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT &&
+               mode != E_CONTENT_EDITOR_MODE_MARKDOWN_HTML);
+
+       return TRUE;
+}
+
 void
 e_composer_actions_init (EMsgComposer *composer)
 {
@@ -676,10 +705,12 @@ e_composer_actions_init (EMsgComposer *composer)
                g_object_unref (gcr_gnupg_icon);
        }
 
-       e_binding_bind_property (
-               cnt_editor, "html-mode",
+       e_binding_bind_property_full (
+               editor, "mode",
                ACTION (PICTURE_GALLERY), "sensitive",
-               G_BINDING_SYNC_CREATE);
+               G_BINDING_SYNC_CREATE,
+               eca_transform_mode_html_to_boolean_cb,
+               NULL, NULL, NULL);
 
        e_binding_bind_property (
                cnt_editor, "editable",
@@ -711,6 +742,13 @@ e_composer_actions_init (EMsgComposer *composer)
                e_html_editor_get_action (editor, "visually-wrap-long-lines"), "active",
                G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
+       e_binding_bind_property_full (
+               editor, "mode",
+               e_html_editor_get_action (editor, "visually-wrap-long-lines"), "visible",
+               G_BINDING_SYNC_CREATE,
+               eca_mode_to_bool_hide_in_markdown_cb,
+               NULL, NULL, NULL);
+
 #if defined (ENABLE_SMIME)
        visible = TRUE;
 #else
diff --git a/src/composer/e-composer-private.c b/src/composer/e-composer-private.c
index f23b1d9b8f..020ae6c442 100644
--- a/src/composer/e-composer-private.c
+++ b/src/composer/e-composer-private.c
@@ -62,14 +62,12 @@ static void
 composer_update_gallery_visibility (EMsgComposer *composer)
 {
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
        GtkToggleAction *toggle_action;
        gboolean gallery_active;
        gboolean is_html;
 
        editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
-       is_html = e_content_editor_get_html_mode (cnt_editor);
+       is_html = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
 
        toggle_action = GTK_TOGGLE_ACTION (ACTION (PICTURE_GALLERY));
        gallery_active = gtk_toggle_action_get_active (toggle_action);
@@ -261,23 +259,7 @@ e_composer_private_constructed (EMsgComposer *composer)
 
        focus_tracker = e_focus_tracker_new (GTK_WINDOW (composer));
 
-       action = e_html_editor_get_action (editor, "cut");
-       e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "copy");
-       e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "paste");
-       e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "select-all");
-       e_focus_tracker_set_select_all_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "undo");
-       e_focus_tracker_set_undo_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "redo");
-       e_focus_tracker_set_redo_action (focus_tracker, action);
+       e_html_editor_connect_focus_tracker (editor, focus_tracker);
 
        priv->focus_tracker = focus_tracker;
 
@@ -351,12 +333,7 @@ e_composer_private_constructed (EMsgComposer *composer)
        priv->gallery_scrolled_window = g_object_ref (widget);
        gtk_widget_show (widget);
 
-       widget = GTK_WIDGET (cnt_editor);
-       if (GTK_IS_SCROLLABLE (cnt_editor)) {
-               /* Scrollables are packed in a scrolled window */
-               widget = gtk_widget_get_parent (widget);
-               g_warn_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
-       }
+       widget = e_html_editor_get_content_box (editor);
        gtk_widget_reparent (widget, container);
 
        /* Construct the picture gallery. */
@@ -372,7 +349,7 @@ e_composer_private_constructed (EMsgComposer *composer)
        g_free (gallery_path);
 
        e_signal_connect_notify_swapped (
-               cnt_editor, "notify::html-mode",
+               editor, "notify::mode",
                G_CALLBACK (composer_update_gallery_visibility), composer);
 
        g_signal_connect_swapped (
@@ -757,13 +734,13 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
        EMsgComposer *composer = usd->composer;
        gchar *contents = NULL, *new_signature_id;
        gsize length = 0;
-       gboolean is_html;
+       EContentEditorMode editor_mode = E_CONTENT_EDITOR_MODE_UNKNOWN;
        GError *error = NULL;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
 
        e_mail_signature_combo_box_load_selected_finish (
-               combo_box, result, &contents, &length, &is_html, &error);
+               combo_box, result, &contents, &length, &editor_mode, &error);
 
        /* FIXME Use an EAlert here. */
        if (error != NULL) {
@@ -788,7 +765,7 @@ composer_load_signature_cb (EMailSignatureComboBox *combo_box,
        new_signature_id = e_content_editor_insert_signature (
                cnt_editor,
                contents,
-               is_html,
+               editor_mode,
                usd->can_reposition_caret,
                gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo_box)),
                &composer->priv->set_signature_from_message,
diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c
index aaa35c4652..b8ba281d1e 100644
--- a/src/composer/e-msg-composer.c
+++ b/src/composer/e-msg-composer.c
@@ -86,8 +86,7 @@ typedef enum {
        COMPOSER_FLAG_PGP_ENCRYPT               = 1 << 5,
        COMPOSER_FLAG_SMIME_SIGN                = 1 << 6,
        COMPOSER_FLAG_SMIME_ENCRYPT             = 1 << 7,
-       COMPOSER_FLAG_HTML_MODE                 = 1 << 8,
-       COMPOSER_FLAG_SAVE_DRAFT                = 1 << 9
+       COMPOSER_FLAG_SAVE_DRAFT                = 1 << 8
 } ComposerFlags;
 
 enum {
@@ -217,7 +216,7 @@ e_msg_composer_dec_soft_busy (EMsgComposer *composer)
                g_object_notify (G_OBJECT (composer), "soft-busy");
 }
 
-/**
+/*
  * emcu_part_to_html:
  * @part:
  *
@@ -227,7 +226,7 @@ e_msg_composer_dec_soft_busy (EMsgComposer *composer)
  * will be performed.
  *
  * Return Value: The part in displayable html format.
- **/
+ */
 static gchar *
 emcu_part_to_html (EMsgComposer *composer,
                    CamelMimePart *part,
@@ -306,6 +305,45 @@ emcu_part_to_html (EMsgComposer *composer,
        return text;
 }
 
+static gchar *
+emcu_part_as_text (EMsgComposer *composer,
+                   CamelMimePart *part,
+                   gssize *out_len,
+                   GCancellable *cancellable)
+{
+       CamelDataWrapper *dw;
+       gchar *text;
+       gssize length;
+
+       dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+       if (dw) {
+               CamelStream *mem = camel_stream_mem_new ();
+               GByteArray *bytes;
+
+               camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
+               camel_stream_close (mem, cancellable, NULL);
+
+               bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
+               if (bytes && bytes->len) {
+                       text = g_strndup ((const gchar *) bytes->data, bytes->len);
+                       length = bytes->len;
+               } else {
+                       text = g_strdup ("");
+                       length = 0;
+               }
+
+               g_object_unref (mem);
+       } else {
+               text = g_strdup ("");
+               length = 0;
+       }
+
+       if (out_len)
+               *out_len = length;
+
+       return text;
+}
+
 static EDestination **
 destination_list_to_vector_sized (GList *list,
                                   gint n)
@@ -1203,27 +1241,45 @@ composer_build_message_thread (GSimpleAsyncResult *simple,
 #endif /* ENABLE_SMIME */
 }
 
+static const gchar *
+composer_get_editor_mode_format_text (EContentEditorMode mode)
+{
+       switch (mode) {
+       case E_CONTENT_EDITOR_MODE_UNKNOWN:
+               g_warn_if_reached ();
+               break;
+       case E_CONTENT_EDITOR_MODE_PLAIN_TEXT:
+               return "text/plain";
+       case E_CONTENT_EDITOR_MODE_HTML:
+               return "text/html";
+       case E_CONTENT_EDITOR_MODE_MARKDOWN:
+               return "text/markdown";
+       case E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT:
+               return "text/markdown-plain";
+       case E_CONTENT_EDITOR_MODE_MARKDOWN_HTML:
+               return "text/markdown-html";
+       }
+
+       return "text/plain";
+}
+
 static void
 composer_add_evolution_composer_mode_header (CamelMedium *medium,
                                              EMsgComposer *composer)
 {
-       gboolean html_mode;
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
+       const gchar *mode;
 
        editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
-       html_mode = e_content_editor_get_html_mode (cnt_editor);
+       mode = composer_get_editor_mode_format_text (e_html_editor_get_mode (editor));
 
-       camel_medium_add_header (
-               medium,
-               "X-Evolution-Composer-Mode",
-               html_mode ? "text/html" : "text/plain");
+       camel_medium_add_header (medium, "X-Evolution-Composer-Mode", mode);
 }
 
 static void
 composer_add_evolution_format_header (CamelMedium *medium,
-                                      ComposerFlags flags)
+                                     ComposerFlags flags,
+                                     EContentEditorMode mode)
 {
        GString *string;
 
@@ -1460,7 +1516,10 @@ composer_build_message (EMsgComposer *composer,
                if (!g_str_has_suffix (text, "\r\n") && !g_str_has_suffix (text, "\n"))
                        g_byte_array_append (data, (const guint8 *) "\r\n", 2);
 
-               type = camel_content_type_new ("text", "plain");
+               if (e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_MARKDOWN)
+                       type = camel_content_type_new ("text", "markdown");
+               else
+                       type = camel_content_type_new ("text", "plain");
                charset = best_charset (
                        data, priv->charset, &context->plain_encoding);
                if (charset != NULL) {
@@ -1468,6 +1527,12 @@ composer_build_message (EMsgComposer *composer,
                        iconv_charset = camel_iconv_charset_name (charset);
                        g_free (charset);
                }
+
+               if ((flags & COMPOSER_FLAG_SAVE_DRAFT) == 0 && (
+                   e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_MARKDOWN ||
+                   e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT ||
+                   e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML))
+                       composer_add_evolution_composer_mode_header (CAMEL_MEDIUM (context->message), 
composer);
        }
 
        mem_stream = camel_stream_mem_new_with_byte_array (data);
@@ -1527,7 +1592,7 @@ composer_build_message (EMsgComposer *composer,
                if ((flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) {
                        /* X-Evolution-Format */
                        composer_add_evolution_format_header (
-                               CAMEL_MEDIUM (context->message), flags);
+                               CAMEL_MEDIUM (context->message), flags, e_html_editor_get_mode 
(composer->priv->editor));
 
                        /* X-Evolution-Composer-Mode */
                        composer_add_evolution_composer_mode_header (
@@ -1968,7 +2033,7 @@ msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard,
        editor = e_msg_composer_get_editor (composer);
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (!e_content_editor_get_html_mode (cnt_editor) &&
+       if (e_html_editor_get_mode (editor) != E_CONTENT_EDITOR_MODE_HTML &&
            gtk_targets_include_image (targets, n_targets, TRUE)) {
                e_composer_paste_image (composer, clipboard);
                return;
@@ -2052,7 +2117,7 @@ msg_composer_drag_data_received_cb (GtkWidget *widget,
 
        editor = e_msg_composer_get_editor (composer);
        cnt_editor = e_html_editor_get_content_editor (editor);
-       html_mode = e_content_editor_get_html_mode (cnt_editor);
+       html_mode = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
 
        g_signal_handler_disconnect (cnt_editor, composer->priv->drag_data_received_handler_id);
        composer->priv->drag_data_received_handler_id = 0;
@@ -2716,7 +2781,7 @@ msg_composer_map (GtkWidget *widget)
 
        /* Jump to the editor as a last resort. */
        cnt_editor = e_html_editor_get_content_editor (editor);
-       gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
+       e_content_editor_grab_focus (cnt_editor);
 }
 
 static gboolean
@@ -2750,11 +2815,11 @@ msg_composer_key_press_event (GtkWidget *widget,
        }
 
        if (event->keyval == GDK_KEY_Tab && gtk_widget_is_focus (input_widget)) {
-               gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
+               e_content_editor_grab_focus (cnt_editor);
                return TRUE;
        }
 
-       if (gtk_widget_is_focus (GTK_WIDGET (cnt_editor))) {
+       if (e_content_editor_is_focus (cnt_editor)) {
                if (event->keyval == GDK_KEY_ISO_Left_Tab) {
                        gboolean view_processed = FALSE;
 
@@ -3404,7 +3469,7 @@ e_msg_composer_check_inline_attachments (EMsgComposer *composer)
        editor = e_msg_composer_get_editor (composer);
        content_editor = e_html_editor_get_content_editor (editor);
 
-       if (e_content_editor_get_html_mode (content_editor)) {
+       if (e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML) {
                e_content_editor_get_content (content_editor, E_CONTENT_EDITOR_GET_INLINE_IMAGES,
                        "localhost", NULL, e_mg_composer_got_used_inline_images_cb, g_object_ref (composer));
        } else {
@@ -3412,6 +3477,23 @@ e_msg_composer_check_inline_attachments (EMsgComposer *composer)
        }
 }
 
+static gboolean
+emcu_format_as_plain_text (EMsgComposer *composer,
+                          CamelContentType *content_type)
+{
+       EContentEditorMode mode;
+
+       if (!camel_content_type_is (content_type, "text", "plain") &&
+           !camel_content_type_is (content_type, "text", "markdown"))
+               return FALSE;
+
+       mode = e_html_editor_get_mode (e_msg_composer_get_editor (composer));
+
+       return mode == E_CONTENT_EDITOR_MODE_MARKDOWN ||
+               mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT ||
+               mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+}
+
 static void
 handle_multipart_signed (EMsgComposer *composer,
                          CamelMultipart *multipart,
@@ -3488,7 +3570,14 @@ handle_multipart_signed (EMsgComposer *composer,
                        handle_multipart (
                                composer, multipart, parent_part, keep_signature, cancellable, depth);
                }
+       } else if (camel_content_type_is (content_type, "text", "markdown") ||
+                  emcu_format_as_plain_text (composer, content_type)) {
+               gchar *text;
+               gssize length;
 
+               text = emcu_part_as_text (composer, mime_part, &length, cancellable);
+               if (text)
+                       e_msg_composer_set_pending_body (composer, text, length, FALSE);
        } else if (camel_content_type_is (content_type, "text", "*")) {
                gchar *html;
                gssize length;
@@ -3591,6 +3680,14 @@ handle_multipart_encrypted (EMsgComposer *composer,
                                composer, content_multipart, multipart, keep_signature, cancellable, depth);
                }
 
+       } else if (camel_content_type_is (content_type, "text", "markdown") ||
+                  emcu_format_as_plain_text (composer, content_type)) {
+               gchar *text;
+               gssize length;
+
+               text = emcu_part_as_text (composer, mime_part, &length, cancellable);
+               if (text)
+                       e_msg_composer_set_pending_body (composer, text, length, FALSE);
        } else if (camel_content_type_is (content_type, "text", "*")) {
                gchar *html;
                gssize length;
@@ -3663,6 +3760,17 @@ handle_multipart_alternative (EMsgComposer *composer,
                        /* text/html is preferable, so once we find it we're done... */
                        text_part = mime_part;
                        break;
+               } else if (camel_content_type_is (content_type, "text", "markdown") ||
+                          emcu_format_as_plain_text (composer, content_type)) {
+                       gchar *text;
+                       gssize length;
+
+                       text = emcu_part_as_text (composer, mime_part, &length, cancellable);
+                       if (text) {
+                               e_msg_composer_set_pending_body (composer, text, length, FALSE);
+                               text_part = NULL;
+                               break;
+                       }
                } else if (camel_content_type_is (content_type, "text", "*")) {
                        /* anyt text part not text/html is second rate so the first
                         * text part we find isn't necessarily the one we'll use. */
@@ -3682,11 +3790,33 @@ handle_multipart_alternative (EMsgComposer *composer,
                gchar *html;
                gssize length;
 
-               html = emcu_part_to_html (
-                       composer, text_part, &length, keep_signature, cancellable);
-               if (!html && fallback_text_part)
-                       html = emcu_part_to_html (
-                               composer, fallback_text_part, &length, keep_signature, cancellable);
+               if (emcu_format_as_plain_text (composer, camel_mime_part_get_content_type (text_part))) {
+                       gchar *text;
+                       gssize length;
+
+                       text = emcu_part_as_text (composer, text_part, &length, cancellable);
+                       if (text) {
+                               e_msg_composer_set_pending_body (composer, text, length, FALSE);
+                               return;
+                       }
+               }
+
+               html = emcu_part_to_html (composer, text_part, &length, keep_signature, cancellable);
+               if (!html && fallback_text_part) {
+                       if (emcu_format_as_plain_text (composer, camel_mime_part_get_content_type 
(fallback_text_part))) {
+                               gchar *text;
+                               gssize length;
+
+                               text = emcu_part_as_text (composer, fallback_text_part, &length, cancellable);
+                               if (text) {
+                                       e_msg_composer_set_pending_body (composer, text, length, FALSE);
+                                       return;
+                               }
+                       }
+
+                       html = emcu_part_to_html (composer, fallback_text_part, &length, keep_signature, 
cancellable);
+               }
+
                if (html)
                        e_msg_composer_set_pending_body (composer, html, length, TRUE);
        }
@@ -3748,15 +3878,26 @@ handle_multipart (EMsgComposer *composer,
                        }
 
                } else if (depth == 0 && i == 0) {
-                       gchar *html = NULL;
-                       gssize length = 0;
-
                        /* Since the first part is not multipart/alternative,
                         * this must be the body. */
-                       html = emcu_part_to_html (
-                               composer, mime_part, &length, keep_signature, cancellable);
 
-                       e_msg_composer_set_pending_body (composer, html, length, TRUE);
+                       if (camel_content_type_is (content_type, "text", "markdown") ||
+                           emcu_format_as_plain_text (composer, content_type)) {
+                               gchar *text;
+                               gssize length;
+
+                               text = emcu_part_as_text (composer, mime_part, &length, cancellable);
+                               if (text)
+                                       e_msg_composer_set_pending_body (composer, text, length, FALSE);
+                       } else {
+                               gchar *html = NULL;
+                               gssize length = 0;
+
+                               html = emcu_part_to_html (
+                                       composer, mime_part, &length, keep_signature, cancellable);
+
+                               e_msg_composer_set_pending_body (composer, html, length, TRUE);
+                       }
 
                } else if (camel_content_type_is (content_type, "image", "*") && (
                           camel_mime_part_get_content_id (mime_part) ||
@@ -4108,7 +4249,7 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
        composer_mode = camel_medium_get_header (
                CAMEL_MEDIUM (message), "X-Evolution-Composer-Mode");
 
-       if (composer_mode && *composer_mode)
+       if (format && *format && composer_mode && *composer_mode)
                is_message_from_draft = TRUE;
 
        if (format != NULL) {
@@ -4121,10 +4262,24 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
                for (i = 0; flags[i]; i++) {
                        if (g_ascii_strcasecmp (flags[i], "text/html") == 0 ||
                            g_ascii_strcasecmp (flags[i], "text/plain") == 0) {
-                               gboolean html_mode;
+                               EContentEditorMode mode = E_CONTENT_EDITOR_MODE_HTML;
+
+                               if (composer_mode) {
+                                       if (!g_ascii_strcasecmp (composer_mode, "text/plain"))
+                                               mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+                                       else if (!g_ascii_strcasecmp (composer_mode, "text/html"))
+                                               mode = E_CONTENT_EDITOR_MODE_HTML;
+                                       else if (!g_ascii_strcasecmp (composer_mode, "text/markdown"))
+                                               mode = E_CONTENT_EDITOR_MODE_MARKDOWN;
+                                       else if (!g_ascii_strcasecmp (composer_mode, "text/markdown-plain"))
+                                               mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+                                       else if (!g_ascii_strcasecmp (composer_mode, "text/markdown-html"))
+                                               mode = E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+                               } else if (!g_ascii_strcasecmp (flags[i], "text/plain")) {
+                                       mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+                               }
 
-                               html_mode = composer_mode && !g_ascii_strcasecmp (composer_mode, "text/html");
-                               e_content_editor_set_html_mode (cnt_editor, html_mode);
+                               e_html_editor_set_mode (editor, mode);
                        } else if (g_ascii_strcasecmp (flags[i], "pgp-sign") == 0) {
                                action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
                                gtk_toggle_action_set_active (action, TRUE);
@@ -4140,6 +4295,21 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
                        }
                }
                g_strfreev (flags);
+       } else if (composer_mode != NULL) {
+               EContentEditorMode mode = E_CONTENT_EDITOR_MODE_HTML;
+
+               if (!g_ascii_strcasecmp (composer_mode, "text/plain"))
+                       mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+               else if (!g_ascii_strcasecmp (composer_mode, "text/html"))
+                       mode = E_CONTENT_EDITOR_MODE_HTML;
+               else if (!g_ascii_strcasecmp (composer_mode, "text/markdown"))
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN;
+               else if (!g_ascii_strcasecmp (composer_mode, "text/markdown-plain"))
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+               else if (!g_ascii_strcasecmp (composer_mode, "text/markdown-html"))
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+
+               e_html_editor_set_mode (editor, mode);
        }
 
        if (is_message_from_draft || (
@@ -4250,7 +4420,6 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
                /* If we are opening message from Drafts */
                if (is_message_from_draft) {
                        /* Extract the body */
-                       CamelDataWrapper *dw;
 
                        #ifdef ENABLE_SMIME
                        if (is_smime_encrypted) {
@@ -4279,28 +4448,11 @@ e_msg_composer_setup_with_message (EMsgComposer *composer,
                        }
                        #endif
 
-                       dw = camel_medium_get_content ((CamelMedium *) mime_part);
-                       if (dw) {
-                               CamelStream *mem = camel_stream_mem_new ();
-                               GByteArray *bytes;
-
-                               camel_data_wrapper_decode_to_stream_sync (dw, mem, cancellable, NULL);
-                               camel_stream_close (mem, cancellable, NULL);
-
-                               bytes = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
-                               if (bytes && bytes->len) {
-                                       html = g_strndup ((const gchar *) bytes->data, bytes->len);
-                                       length = bytes->len;
-                               } else {
-                                       html = g_strdup ("");
-                                       length = 0;
-                               }
-
-                               g_object_unref (mem);
-                       } else {
-                               html = g_strdup ("");
-                               length = 0;
-                       }
+                       html = emcu_part_as_text (composer, mime_part, &length, cancellable);
+               } else if (camel_content_type_is (content_type, "text", "markdown") ||
+                          emcu_format_as_plain_text (composer, content_type)) {
+                       is_html = FALSE;
+                       html = emcu_part_as_text (composer, CAMEL_MIME_PART (message), &length, cancellable);
                } else {
                        is_html = TRUE;
                        html = emcu_part_to_html (
@@ -5493,7 +5645,7 @@ e_msg_composer_set_body (EMsgComposer *composer,
        content = _("The composer contains a non-text message body, which cannot be edited.");
        set_editor_text (composer, content, TRUE, FALSE);
 
-       e_content_editor_set_html_mode (cnt_editor, FALSE);
+       e_html_editor_set_mode (editor, E_CONTENT_EDITOR_MODE_PLAIN_TEXT);
        e_content_editor_set_editable (cnt_editor, FALSE);
 
        g_free (priv->mime_body);
@@ -5872,12 +6024,10 @@ e_msg_composer_get_message (EMsgComposer *composer,
        GtkAction *action;
        ComposerFlags flags = 0;
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
 
        simple = g_simple_async_result_new (
                G_OBJECT (composer), callback,
@@ -5885,7 +6035,8 @@ e_msg_composer_get_message (EMsgComposer *composer,
 
        g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       if (e_content_editor_get_html_mode (cnt_editor))
+       if (e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML ||
+           e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML)
                flags |= COMPOSER_FLAG_HTML_CONTENT;
 
        action = ACTION (PRIORITIZE_MESSAGE);
@@ -5996,8 +6147,6 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data)
 {
-       EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
        GSimpleAsyncResult *simple;
        ComposerFlags flags = COMPOSER_FLAG_SAVE_DRAFT;
        GtkAction *action;
@@ -6010,11 +6159,6 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
 
        g_simple_async_result_set_check_cancellable (simple, cancellable);
 
-       editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
-       /* We need to remember composer mode */
-       if (e_content_editor_get_html_mode (cnt_editor))
-               flags |= COMPOSER_FLAG_HTML_MODE;
        /* We want to save HTML content everytime when we save as draft */
        flags |= COMPOSER_FLAG_SAVE_DRAFT;
 
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index e81570ad3c..cc8c06c2b0 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -169,6 +169,7 @@ set(SOURCES
        e-mail-signature-tree-view.c
        e-map.c
        e-markdown-editor.c
+       e-markdown-utils.c
        e-marshal.c
        e-menu-tool-action.c
        e-menu-tool-button.c
@@ -445,6 +446,7 @@ set(HEADERS
        e-mail-signature-tree-view.h
        e-map.h
        e-markdown-editor.h
+       e-markdown-utils.h
        e-menu-tool-action.h
        e-menu-tool-button.h
        e-misc-utils.h
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index dd01adb92a..f8b1722639 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -174,17 +174,20 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                        G_PARAM_STATIC_STRINGS));
 
        /**
-        * EContentEditor:html-mode
+        * EContentEditor:mode
         *
-        * Determines whether HTML or plain text mode is enabled.
+        * Determines the mode of the content editor, as one of the #EContentEditorMode.
+        *
+        * Since: 3.44
         **/
        g_object_interface_install_property (
                iface,
-               g_param_spec_boolean (
-                       "html-mode",
-                       "HTML Mode",
-                       "Edit HTML or plain text",
-                       TRUE,
+               g_param_spec_enum (
+                       "mode",
+                       "Mode",
+                       "Editor mode",
+                       E_TYPE_CONTENT_EDITOR_MODE,
+                       E_CONTENT_EDITOR_MODE_PLAIN_TEXT,
                        G_PARAM_READWRITE |
                        G_PARAM_STATIC_STRINGS));
 
@@ -639,6 +642,82 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                G_TYPE_STRING);
 }
 
+/**
+ * e_content_editor_supports_mode:
+ * @editor: an #EContentEditor
+ * @mode: an #EContentEditorMode to check
+ *
+ * Returns: whether the @editor supports @mode
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_content_editor_supports_mode (EContentEditor *editor,
+                               EContentEditorMode mode)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+
+       return iface->supports_mode != NULL &&
+               iface->supports_mode (editor, mode);
+}
+
+/**
+ * e_content_editor_grab_focus:
+ * @editor: an #EContentEditor
+ *
+ * A method to grab focus on the @editor. This is an optional method,
+ * the default implementation calls gtk_widget_grab_focus().
+ *
+ * Since: 3.44
+ **/
+void
+e_content_editor_grab_focus (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+
+       if (iface->grab_focus)
+               iface->grab_focus (editor);
+       else
+               gtk_widget_grab_focus (GTK_WIDGET (editor));
+}
+
+/**
+ * e_content_editor_is_focus:
+ * @editor: an #EContentEditor
+ *
+ * Returns, whether the @editor is focused. This is an optional method,
+ * the default implementation calls gtk_widget_is_focus().
+ *
+ * Returns: whether the @editor is focused
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_content_editor_is_focus (EContentEditor *editor)
+{
+       EContentEditorInterface *iface;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_val_if_fail (iface != NULL, FALSE);
+
+       if (iface->is_focus)
+               return iface->is_focus (editor);
+       else
+               return gtk_widget_is_focus (GTK_WIDGET (editor));
+}
+
 ESpellChecker *
 e_content_editor_ref_spell_checker (EContentEditor *editor)
 {
@@ -809,27 +888,6 @@ e_content_editor_set_changed (EContentEditor *editor,
        g_object_set (G_OBJECT (editor), "changed", changed, NULL);
 }
 
-gboolean
-e_content_editor_get_html_mode (EContentEditor *editor)
-{
-       gboolean value = FALSE;
-
-       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
-
-       g_object_get (G_OBJECT (editor), "html-mode", &value, NULL);
-
-       return value;
-}
-
-void
-e_content_editor_set_html_mode (EContentEditor *editor,
-                               gboolean html_mode)
-{
-       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
-
-       g_object_set (G_OBJECT (editor), "html-mode", html_mode, NULL);
-}
-
 /**
  * e_content_editor_set_alignment:
  * @editor: an #EContentEditor
@@ -2399,7 +2457,7 @@ e_content_editor_take_last_error (EContentEditor *editor,
 gchar *
 e_content_editor_insert_signature (EContentEditor *editor,
                                    const gchar *content,
-                                   gboolean is_html,
+                                   EContentEditorMode editor_mode,
                                   gboolean can_reposition_caret,
                                    const gchar *signature_id,
                                    gboolean *set_signature_from_message,
@@ -2417,7 +2475,7 @@ e_content_editor_insert_signature (EContentEditor *editor,
        return iface->insert_signature (
                editor,
                content,
-               is_html,
+               editor_mode,
                can_reposition_caret,
                signature_id,
                set_signature_from_message,
@@ -3915,3 +3973,39 @@ e_content_editor_delete_image (EContentEditor *editor)
 
        iface->delete_image (editor);
 }
+
+/**
+ * e_content_editor_util_three_state_to_bool:
+ * @value: an #EThreeState value
+ * @mail_key: (nullable): a key into 'org.gnome.evolution.mail'
+ *
+ * Converts the three-state @value into boolean, using the @mail_key
+ * boolean key from 'org.gnome.evolution.mail' in case the @value
+ * in %E_THREE_STATE_INCONSISTENT as a fallback, when non-%NULL.
+ *
+ * Returns: @value converted to boolean, optionally depending on @mail_key setting
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_content_editor_util_three_state_to_bool (EThreeState value,
+                                          const gchar *mail_key)
+{
+       gboolean res = FALSE;
+
+       if (value == E_THREE_STATE_ON)
+               return TRUE;
+
+       if (value == E_THREE_STATE_OFF)
+               return FALSE;
+
+       if (mail_key && *mail_key) {
+               GSettings *settings;
+
+               settings = e_util_ref_settings ("org.gnome.evolution.mail");
+               res = g_settings_get_boolean (settings, mail_key);
+               g_clear_object (&settings);
+       }
+
+       return res;
+}
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index a377f5f1c0..8f376e6b2b 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -144,7 +144,7 @@ struct _EContentEditorInterface {
 
        gchar *         (*insert_signature)             (EContentEditor *editor,
                                                         const gchar *content,
-                                                        gboolean is_html,
+                                                        EContentEditorMode editor_mode,
                                                         gboolean can_reposition_caret,
                                                         const gchar *signature_id,
                                                         gboolean *set_signature_from_message,
@@ -426,14 +426,23 @@ struct _EContentEditorInterface {
        void            (*delete_h_rule)                (EContentEditor *editor);
        void            (*delete_image)                 (EContentEditor *editor);
 
+       gboolean        (*supports_mode)                (EContentEditor *editor,
+                                                        EContentEditorMode mode);
+       void            (*grab_focus)                   (EContentEditor *editor);
+       gboolean        (*is_focus)                     (EContentEditor *editor);
+
        /* padding for future expansion */
-       gpointer reserved[20];
+       gpointer reserved[17];
 };
 
 /* Properties */
 
 ESpellChecker *        e_content_editor_ref_spell_checker
                                                (EContentEditor *editor);
+gboolean       e_content_editor_supports_mode  (EContentEditor *editor,
+                                                EContentEditorMode mode);
+void           e_content_editor_grab_focus     (EContentEditor *editor);
+gboolean       e_content_editor_is_focus       (EContentEditor *editor);
 gboolean       e_content_editor_is_malfunction (EContentEditor *editor);
 gboolean       e_content_editor_can_cut        (EContentEditor *editor);
 gboolean       e_content_editor_can_copy       (EContentEditor *editor);
@@ -452,9 +461,6 @@ void                e_content_editor_set_editable   (EContentEditor *editor,
 gboolean       e_content_editor_get_changed    (EContentEditor *editor);
 void           e_content_editor_set_changed    (EContentEditor *editor,
                                                 gboolean changed);
-gboolean       e_content_editor_get_html_mode  (EContentEditor *editor);
-void           e_content_editor_set_html_mode  (EContentEditor *editor,
-                                                gboolean html_mode);
 void           e_content_editor_set_alignment  (EContentEditor *editor,
                                                 EContentEditorAlignment value);
 EContentEditorAlignment
@@ -649,7 +655,7 @@ void                e_content_editor_take_last_error(EContentEditor *editor,
 gchar *                e_content_editor_insert_signature
                                                (EContentEditor *editor,
                                                 const gchar *content,
-                                                gboolean is_html,
+                                                EContentEditorMode editor_mode,
                                                 gboolean can_reposition_caret,
                                                 const gchar *signature_id,
                                                 gboolean *set_signature_from_message,
@@ -1011,6 +1017,10 @@ CamelMimePart *  e_content_editor_emit_ref_mime_part
                                                (EContentEditor *editor,
                                                 const gchar *uri);
 
+gboolean       e_content_editor_util_three_state_to_bool
+                                               (EThreeState value,
+                                                const gchar *mail_key);
+
 G_END_DECLS
 
 #endif /* E_CONTENT_EDITOR_H */
diff --git a/src/e-util/e-html-editor-actions.c b/src/e-util/e-html-editor-actions.c
index 061baf6f9f..aa6f664597 100644
--- a/src/e-util/e-html-editor-actions.c
+++ b/src/e-util/e-html-editor-actions.c
@@ -607,21 +607,17 @@ update_mode_combobox (gpointer data)
 {
        GWeakRef *weak_ref = data;
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
+       EContentEditorMode mode;
        GtkAction *action;
-       gboolean is_html;
 
        editor = g_weak_ref_get (weak_ref);
        if (!editor)
                return FALSE;
 
-       cnt_editor = e_html_editor_get_content_editor (editor);
-       is_html = e_content_editor_get_html_mode (cnt_editor);
+       mode = e_html_editor_get_mode (editor);
 
-       action = gtk_action_group_get_action (
-               editor->priv->core_editor_actions, "mode-html");
-       gtk_radio_action_set_current_value (
-               GTK_RADIO_ACTION (action), (is_html ? 1 : 0));
+       action = gtk_action_group_get_action (editor->priv->core_editor_actions, "mode-html");
+       gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), mode);
 
        g_object_unref (editor);
 
@@ -629,18 +625,17 @@ update_mode_combobox (gpointer data)
 }
 
 static void
-html_editor_actions_notify_html_mode_cb (EContentEditor *cnt_editor,
-                                        GParamSpec *param,
-                                        EHTMLEditor *editor)
+html_editor_actions_notify_mode_cb (EHTMLEditor *editor,
+                                   GParamSpec *param,
+                                   gpointer user_data)
 {
        GtkActionGroup *action_group;
        GtkWidget *style_combo_box;
        gboolean is_html;
 
-       g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
        g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
-       is_html = e_content_editor_get_html_mode (cnt_editor);
+       is_html = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
 
        g_object_set (G_OBJECT (editor->priv->html_actions), "sensitive", is_html, NULL);
 
@@ -793,7 +788,7 @@ action_paste_quote_cb (GtkAction *action,
                gdk_display_get_default (),
                GDK_SELECTION_CLIPBOARD);
 
-       if (e_content_editor_get_html_mode (cnt_editor)) {
+       if (e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML) {
                if (e_clipboard_wait_is_html_available (clipboard))
                        e_clipboard_request_html (clipboard, clipboard_html_received_for_paste_quote, editor);
                else if (gtk_clipboard_wait_is_text_available (clipboard))
@@ -1319,14 +1314,35 @@ static GtkRadioActionEntry core_mode_entries[] = {
          N_("_HTML"),
          NULL,
          N_("HTML editing mode"),
-         TRUE },       /* e_content_editor_set_html_mode */
+         E_CONTENT_EDITOR_MODE_HTML },
 
        { "mode-plain",
          NULL,
          N_("Plain _Text"),
          NULL,
          N_("Plain text editing mode"),
-         FALSE }       /* e_content_editor_set_html_mode */
+         E_CONTENT_EDITOR_MODE_PLAIN_TEXT },
+
+       { "mode-markdown",
+         NULL,
+         N_("_Markdown"),
+         NULL,
+         N_("Markdown editing mode"),
+         E_CONTENT_EDITOR_MODE_MARKDOWN },
+
+       { "mode-markdown-plain",
+         NULL,
+         N_("Ma_rkdown as Plain Text"),
+         NULL,
+         N_("Markdown editing mode, exported as Plain Text"),
+         E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT },
+
+       { "mode-markdown-html",
+         NULL,
+         N_("Mar_kdown as HTML"),
+         NULL,
+         N_("Markdown editing mode, exported as HTML"),
+         E_CONTENT_EDITOR_MODE_MARKDOWN_HTML }
 };
 
 static GtkRadioActionEntry core_style_entries[] = {
@@ -2131,12 +2147,13 @@ editor_actions_setup_spell_check_menu (EHTMLEditor *editor)
 }
 
 void
-editor_actions_init (EHTMLEditor *editor)
+e_html_editor_actions_init (EHTMLEditor *editor)
 {
        GtkAction *action;
        GtkActionGroup *action_group;
        GtkUIManager *manager;
        const gchar *domain;
+       guint ii;
 
        g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
@@ -2164,7 +2181,7 @@ editor_actions_init (EHTMLEditor *editor)
        gtk_action_group_add_radio_actions (
                action_group, core_mode_entries,
                G_N_ELEMENTS (core_mode_entries),
-               TRUE,
+               E_CONTENT_EDITOR_MODE_HTML,
                G_CALLBACK (action_mode_cb), editor);
        gtk_action_group_add_radio_actions (
                action_group, core_style_entries,
@@ -2264,6 +2281,26 @@ editor_actions_init (EHTMLEditor *editor)
 
        gtk_action_set_sensitive (ACTION (UNINDENT), FALSE);
        gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
+
+       g_signal_connect_object (ACTION (SUBSCRIPT), "toggled",
+               G_CALLBACK (html_editor_actions_subscript_toggled_cb), editor, 0);
+       g_signal_connect_object (ACTION (SUPERSCRIPT), "toggled",
+               G_CALLBACK (html_editor_actions_superscript_toggled_cb), editor, 0);
+       g_signal_connect (editor, "notify::mode",
+               G_CALLBACK (html_editor_actions_notify_mode_cb), NULL);
+
+       action_group = editor->priv->core_editor_actions;
+       action = gtk_action_group_get_action (action_group, "mode-html");
+       e_binding_bind_property (
+               editor, "mode",
+               action, "current-value",
+               G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+       for (ii = 0; ii < G_N_ELEMENTS (core_mode_entries); ii++) {
+               action = gtk_action_group_get_action (action_group, core_mode_entries[ii].name);
+
+               gtk_action_set_visible (action, e_html_editor_has_editor_for_mode (editor, 
core_mode_entries[ii].value));
+       }
 }
 
 static gboolean
@@ -2302,137 +2339,216 @@ e_html_editor_indent_level_to_bool_unindent_cb (GBinding *binding,
        return TRUE;
 }
 
-void
-editor_actions_bind (EHTMLEditor *editor)
+static gboolean
+e_html_editor_sensitize_html_actions_cb (GBinding *binding,
+                                        const GValue *from_value,
+                                        GValue *to_value,
+                                        gpointer user_data)
+{
+       /* It should be editable... */
+       if (g_value_get_boolean (from_value)) {
+               EHTMLEditor *editor = user_data;
+
+               /* ... and in the HTML mode */
+               g_value_set_boolean (to_value, e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML);
+       } else {
+               g_value_set_boolean (to_value, FALSE);
+       }
+
+       return TRUE;
+}
+
+/**
+ * e_html_editor_util_new_mode_combobox:
+ *
+ * Creates a new combo box containing all composer modes.
+ *
+ * It's a descendant of #EActionComboBox, thus use e_action_combo_box_get_current_value()
+ * and e_action_combo_box_set_current_value() to get the currently selected mode.
+ *
+ * Returns: (transfer full): a new #EActionComboBox with composer modes
+ *
+ * Since: 3.44
+ **/
+EActionComboBox *
+e_html_editor_util_new_mode_combobox (void)
 {
-       GtkAction *action;
        GtkActionGroup *action_group;
+       GtkAction *action;
+       GtkWidget *widget;
+
+       action_group = gtk_action_group_new ("core-mode-entries");
+
+       gtk_action_group_add_radio_actions (
+               action_group, core_mode_entries,
+               G_N_ELEMENTS (core_mode_entries),
+               E_CONTENT_EDITOR_MODE_HTML,
+               NULL, NULL);
+
+       action = gtk_action_group_get_action (action_group, "mode-html");
+
+       widget = e_action_combo_box_new_with_action (GTK_RADIO_ACTION (action));
+       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+       gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
+
+       g_object_set_data_full (G_OBJECT (widget), "core-mode-entries-action-group", action_group, 
g_object_unref);
+
+       return E_ACTION_COMBO_BOX (widget);
+}
+
+void
+e_html_editor_actions_bind (EHTMLEditor *editor)
+{
        EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       action_group = editor->priv->core_editor_actions;
-       action = gtk_action_group_get_action (action_group, "mode-html");
-       e_binding_bind_property (
-               cnt_editor, "html-mode",
-               action, "current-value",
-               G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+       /* 'rb' for 'remember binding' */
+       #define rb(x) editor->priv->content_editor_bindings = g_slist_prepend 
(editor->priv->content_editor_bindings, g_object_ref (x))
 
-       /* Synchronize widget mode with the buttons */
-       e_content_editor_set_html_mode (cnt_editor, TRUE);
+       rb (e_binding_bind_property (
+               editor->priv->fg_color_combo_box, "current-color",
+               cnt_editor, "font-color",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
+               cnt_editor, "editable",
+               editor->priv->fg_color_combo_box, "sensitive",
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
+               editor->priv->bg_color_combo_box, "current-color",
+               cnt_editor, "background-color",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
+               cnt_editor, "editable",
+               editor->priv->bg_color_combo_box, "sensitive",
+               G_BINDING_SYNC_CREATE));
 
-       e_binding_bind_property (
+       rb (e_binding_bind_property (
                cnt_editor, "can-redo",
                ACTION (REDO), "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
                cnt_editor, "can-undo",
                ACTION (UNDO), "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
                cnt_editor, "can-copy",
                ACTION (COPY), "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
                cnt_editor, "can-cut",
                ACTION (CUT), "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
                cnt_editor, "can-paste",
                ACTION (PASTE), "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
                cnt_editor, "can-paste",
                ACTION (PASTE_QUOTE), "sensitive",
-               G_BINDING_SYNC_CREATE);
+               G_BINDING_SYNC_CREATE));
 
        /* This is connected to JUSTIFY_LEFT action only, but
         * it automatically applies on all actions in the group. */
-       e_binding_bind_property (
+       rb (e_binding_bind_property (
                cnt_editor, "alignment",
                ACTION (JUSTIFY_LEFT), "current-value",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
                cnt_editor, "bold",
                ACTION (BOLD), "active",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
                cnt_editor, "font-size",
                ACTION (FONT_SIZE_GROUP), "current-value",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
                cnt_editor, "block-format",
                ACTION (STYLE_NORMAL), "current-value",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property_full (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property_full (
                cnt_editor, "indent-level",
                ACTION (INDENT), "sensitive",
                G_BINDING_SYNC_CREATE,
                e_html_editor_indent_level_to_bool_indent_cb,
-               NULL, NULL, NULL);
-       e_binding_bind_property_full (
+               NULL, NULL, NULL));
+       rb (e_binding_bind_property_full (
                cnt_editor, "indent-level",
                ACTION (UNINDENT), "sensitive",
                G_BINDING_SYNC_CREATE,
                e_html_editor_indent_level_to_bool_unindent_cb,
-               NULL, NULL, NULL);
-       e_binding_bind_property (
+               NULL, NULL, NULL));
+       rb (e_binding_bind_property (
                cnt_editor, "italic",
                ACTION (ITALIC), "active",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
                cnt_editor, "strikethrough",
                ACTION (STRIKETHROUGH), "active",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property (
                cnt_editor, "underline",
                ACTION (UNDERLINE), "active",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property_full (
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL));
+       rb (e_binding_bind_property_full (
                cnt_editor, "font-name",
                editor->priv->font_name_combo_box, "active-id",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
                e_html_editor_content_editor_font_name_to_combo_box,
                NULL,
-               NULL, NULL);
+               NULL, NULL));
 
        /* Cannot use binding, due to subscript and superscript being mutually exclusive */
-       g_signal_connect_object (ACTION (SUBSCRIPT), "toggled",
-               G_CALLBACK (html_editor_actions_subscript_toggled_cb), editor, 0);
-       g_signal_connect_object (cnt_editor, "notify::subscript",
+       editor->priv->subscript_notify_id = g_signal_connect_object (cnt_editor, "notify::subscript",
                G_CALLBACK (html_editor_actions_notify_subscript_cb), editor, 0);
-       g_signal_connect_object (ACTION (SUPERSCRIPT), "toggled",
-               G_CALLBACK (html_editor_actions_superscript_toggled_cb), editor, 0);
-       g_signal_connect_object (cnt_editor, "notify::superscript",
+       editor->priv->superscript_notify_id = g_signal_connect_object (cnt_editor, "notify::superscript",
                G_CALLBACK (html_editor_actions_notify_superscript_cb), editor, 0);
 
-       g_signal_connect_object (cnt_editor, "notify::html-mode",
-               G_CALLBACK (html_editor_actions_notify_html_mode_cb), editor, 0);
-
        /* Disable all actions and toolbars when editor is not editable */
-       e_binding_bind_property (
+       rb (e_binding_bind_property (
                cnt_editor, "editable",
                editor->priv->core_editor_actions, "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property_full (
                cnt_editor, "editable",
                editor->priv->html_actions, "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_sensitize_html_actions_cb,
+               NULL, editor, NULL));
+       rb (e_binding_bind_property (
                cnt_editor, "editable",
                editor->priv->spell_check_actions, "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
+               G_BINDING_SYNC_CREATE));
+       rb (e_binding_bind_property (
                cnt_editor, "editable",
                editor->priv->suggestion_actions, "sensitive",
-               G_BINDING_SYNC_CREATE);
+               G_BINDING_SYNC_CREATE));
+
+       #undef rb
+}
+
+void
+e_html_editor_actions_unbind (EHTMLEditor *editor)
+{
+       EContentEditor *cnt_editor;
+
+       g_slist_foreach (editor->priv->content_editor_bindings, (GFunc) g_binding_unbind, NULL);
+       g_slist_free_full (editor->priv->content_editor_bindings, g_object_unref);
+       editor->priv->content_editor_bindings = NULL;
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       if (cnt_editor) {
+               e_signal_disconnect_notify_handler (cnt_editor, &editor->priv->subscript_notify_id);
+               e_signal_disconnect_notify_handler (cnt_editor, &editor->priv->superscript_notify_id);
+       }
 }
 
 void
-editor_actions_update_spellcheck_languages_menu (EHTMLEditor *editor,
-                                                const gchar * const *languages)
+e_html_editor_actions_update_spellcheck_languages_menu (EHTMLEditor *editor,
+                                                       const gchar * const *languages)
 {
        GHashTable *active;
        GList *actions, *link;
diff --git a/src/e-util/e-html-editor-find-dialog.c b/src/e-util/e-html-editor-find-dialog.c
index fe063ccc70..f38e3883c4 100644
--- a/src/e-util/e-html-editor-find-dialog.c
+++ b/src/e-util/e-html-editor-find-dialog.c
@@ -56,13 +56,39 @@ reset_dialog (EHTMLEditorFindDialog *dialog)
        gtk_widget_hide (dialog->priv->result_label);
 }
 
+static void
+content_editor_find_done_cb (EContentEditor *cnt_editor,
+                            guint match_count,
+                            EHTMLEditorFindDialog *dialog)
+{
+       if (match_count) {
+               gtk_widget_hide (dialog->priv->result_label);
+       } else {
+               gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), _("No match found"));
+               gtk_widget_show (dialog->priv->result_label);
+       }
+
+       gtk_widget_set_sensitive (dialog->priv->find_button, match_count > 0);
+}
+
 static void
 html_editor_find_dialog_hide (GtkWidget *widget)
 {
        EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget);
 
+       g_warn_if_fail (dialog->priv->cnt_editor != NULL);
+
        e_content_editor_on_dialog_close (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_FIND);
 
+       if (dialog->priv->find_done_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       dialog->priv->cnt_editor,
+                       dialog->priv->find_done_handler_id);
+               dialog->priv->find_done_handler_id = 0;
+       }
+
+       dialog->priv->cnt_editor = NULL;
+
        /* Chain up to parent's implementation */
        GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->hide (widget);
 }
@@ -71,6 +97,19 @@ static void
 html_editor_find_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorFindDialog *dialog = E_HTML_EDITOR_FIND_DIALOG (widget);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+
+       g_warn_if_fail (dialog->priv->cnt_editor == NULL);
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       dialog->priv->find_done_handler_id = g_signal_connect (
+               cnt_editor, "find-done",
+               G_CALLBACK (content_editor_find_done_cb), dialog);
+
+       dialog->priv->cnt_editor = cnt_editor;
 
        reset_dialog (dialog);
        gtk_widget_grab_focus (dialog->priv->entry);
@@ -81,21 +120,6 @@ html_editor_find_dialog_show (GtkWidget *widget)
        GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->show (widget);
 }
 
-static void
-content_editor_find_done_cb (EContentEditor *cnt_editor,
-                            guint match_count,
-                            EHTMLEditorFindDialog *dialog)
-{
-       if (match_count) {
-               gtk_widget_hide (dialog->priv->result_label);
-       } else {
-               gtk_label_set_label (GTK_LABEL (dialog->priv->result_label), _("No match found"));
-               gtk_widget_show (dialog->priv->result_label);
-       }
-
-       gtk_widget_set_sensitive (dialog->priv->find_button, match_count > 0);
-}
-
 static void
 html_editor_find_dialog_find_cb (EHTMLEditorFindDialog *dialog)
 {
@@ -151,28 +175,6 @@ html_editor_find_dialog_dispose (GObject *object)
        G_OBJECT_CLASS (e_html_editor_find_dialog_parent_class)->dispose (object);
 }
 
-static void
-html_editor_find_dialog_constructed (GObject *object)
-{
-       EHTMLEditor *editor;
-       EHTMLEditorFindDialog *dialog;
-       EContentEditor *cnt_editor;
-
-       dialog = E_HTML_EDITOR_FIND_DIALOG (object);
-       dialog->priv = E_HTML_EDITOR_FIND_DIALOG_GET_PRIVATE (dialog);
-
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       cnt_editor = e_html_editor_get_content_editor (editor);
-
-       dialog->priv->find_done_handler_id = g_signal_connect (
-               cnt_editor, "find-done",
-               G_CALLBACK (content_editor_find_done_cb), dialog);
-
-       dialog->priv->cnt_editor = cnt_editor;
-
-       G_OBJECT_CLASS (e_html_editor_find_dialog_parent_class)->constructed (object);
-}
-
 static void
 e_html_editor_find_dialog_class_init (EHTMLEditorFindDialogClass *class)
 {
@@ -182,7 +184,6 @@ e_html_editor_find_dialog_class_init (EHTMLEditorFindDialogClass *class)
        g_type_class_add_private (class, sizeof (EHTMLEditorFindDialogPrivate));
 
        object_class = G_OBJECT_CLASS (class);
-       object_class->constructed = html_editor_find_dialog_constructed;
        object_class->dispose = html_editor_find_dialog_dispose;
 
        widget_class = GTK_WIDGET_CLASS (class);
diff --git a/src/e-util/e-html-editor-manager.ui b/src/e-util/e-html-editor-manager.ui
index 3f7468d4de..885108bde2 100644
--- a/src/e-util/e-html-editor-manager.ui
+++ b/src/e-util/e-html-editor-manager.ui
@@ -51,6 +51,9 @@
       <placeholder name='format-menu-top'/>
       <menuitem action='mode-html'/>
       <menuitem action='mode-plain'/>
+      <menuitem action='mode-markdown-plain'/>
+      <menuitem action='mode-markdown-html'/>
+      <menuitem action='mode-markdown'/>
       <separator/>
       <menu action='font-style-menu'>
         <menuitem action='bold'/>
diff --git a/src/e-util/e-html-editor-private.h b/src/e-util/e-html-editor-private.h
index 032b77cdb3..909a603011 100644
--- a/src/e-util/e-html-editor-private.h
+++ b/src/e-util/e-html-editor-private.h
@@ -36,6 +36,7 @@
 #include <e-util/e-html-editor-spell-check-dialog.h>
 #include <e-util/e-html-editor-table-dialog.h>
 #include <e-util/e-html-editor-text-dialog.h>
+#include <e-util/e-util-enumtypes.h>
 
 #ifdef HAVE_XFREE
 #include <X11/XF86keysym.h>
@@ -47,6 +48,10 @@
 G_BEGIN_DECLS
 
 struct _EHTMLEditorPrivate {
+       EContentEditorMode mode;
+
+       GtkWidget *content_editors_box;
+
        GtkUIManager *manager;
        GtkActionGroup *core_actions;
        GtkActionGroup *core_editor_actions;
@@ -64,6 +69,7 @@ struct _EHTMLEditorPrivate {
        GtkWidget *activity_bar;
        GtkWidget *alert_bar;
        GtkWidget *edit_area;
+       GtkWidget *markdown_editor;
 
        GtkWidget *find_dialog;
        GtkWidget *replace_dialog;
@@ -80,6 +86,7 @@ struct _EHTMLEditorPrivate {
        GtkWidget *fg_color_combo_box;
        GtkWidget *bg_color_combo_box;
        GtkWidget *mode_combo_box;
+       GtkToolItem *mode_tool_item;
        GtkWidget *size_combo_box;
        GtkWidget *style_combo_box;
        GtkWidget *font_name_combo_box;
@@ -88,10 +95,15 @@ struct _EHTMLEditorPrivate {
        GtkWidget *emoji_chooser;
 
        GHashTable *cid_parts; /* gchar *cid: URI ~> CamelMimePart * */
-       GHashTable *content_editors;
+       GHashTable *content_editors; /* gchar *name ~> EContentEditor * */
+       GHashTable *content_editors_for_mode; /* EContentEditorMode ~> EContentEditor *; pointers borrowed 
from content_editors */
        EContentEditor *use_content_editor;
+       GCancellable *mode_change_content_cancellable;
 
        gchar *filename;
+       GSList *content_editor_bindings; /* reffed GBinding-s related to the EContentEditor */
+       gulong subscript_notify_id;
+       gulong superscript_notify_id;
 
        guint spell_suggestions_merge_id;
        guint recent_spell_languages_merge_id;
@@ -101,9 +113,10 @@ struct _EHTMLEditorPrivate {
        gboolean paste_plain_prefer_pre;
 };
 
-void           editor_actions_init             (EHTMLEditor *editor);
-void           editor_actions_bind             (EHTMLEditor *editor);
-void           editor_actions_update_spellcheck_languages_menu
+void           e_html_editor_actions_init      (EHTMLEditor *editor);
+void           e_html_editor_actions_bind      (EHTMLEditor *editor);
+void           e_html_editor_actions_unbind    (EHTMLEditor *editor);
+void           e_html_editor_actions_update_spellcheck_languages_menu
                                                (EHTMLEditor *editor,
                                                 const gchar * const *languages);
 const gchar *  e_html_editor_get_content_editor_name
@@ -112,6 +125,9 @@ GtkWidget * e_html_editor_util_create_font_name_combo
                                                (void);
 gchar *                e_html_editor_util_dup_font_id  (GtkComboBox *combo_box,
                                                 const gchar *font_name);
+gboolean       e_html_editor_has_editor_for_mode
+                                               (EHTMLEditor *editor,
+                                                EContentEditorMode mode);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-html-editor-replace-dialog.c b/src/e-util/e-html-editor-replace-dialog.c
index e33e7dfcbc..55f1af62ac 100644
--- a/src/e-util/e-html-editor-replace-dialog.c
+++ b/src/e-util/e-html-editor-replace-dialog.c
@@ -168,6 +168,23 @@ static void
 html_editor_replace_dialog_show (GtkWidget *widget)
 {
        EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget);
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+
+       g_warn_if_fail (dialog->priv->cnt_editor == NULL);
+
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       dialog->priv->find_done_handler_id = g_signal_connect (
+               cnt_editor, "find-done",
+               G_CALLBACK (content_editor_find_done_cb), dialog);
+
+       dialog->priv->replace_all_done_handler_id = g_signal_connect (
+               cnt_editor, "replace-all-done",
+               G_CALLBACK (content_editor_replace_all_done_cb), dialog);
+
+       dialog->priv->cnt_editor = cnt_editor;
 
        e_content_editor_on_dialog_open (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_REPLACE);
 
@@ -183,36 +200,28 @@ html_editor_replace_dialog_hide (GtkWidget *widget)
 {
        EHTMLEditorReplaceDialog *dialog = E_HTML_EDITOR_REPLACE_DIALOG (widget);
 
-       e_content_editor_on_dialog_close (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_REPLACE);
-
-       /* Chain up to parent implementation */
-       GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->hide (widget);
-}
-
-static void
-html_editor_replace_dialog_constructed (GObject *object)
-{
-       EContentEditor *cnt_editor;
-       EHTMLEditor *editor;
-       EHTMLEditorReplaceDialog *dialog;
-
-       dialog = E_HTML_EDITOR_REPLACE_DIALOG (object);
-       dialog->priv = E_HTML_EDITOR_REPLACE_DIALOG_GET_PRIVATE (dialog);
+       g_warn_if_fail (dialog->priv->cnt_editor != NULL);
 
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       cnt_editor = e_html_editor_get_content_editor (editor);
+       e_content_editor_on_dialog_close (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_REPLACE);
 
-       dialog->priv->find_done_handler_id = g_signal_connect (
-               cnt_editor, "find-done",
-               G_CALLBACK (content_editor_find_done_cb), dialog);
+       if (dialog->priv->find_done_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       dialog->priv->cnt_editor,
+                       dialog->priv->find_done_handler_id);
+               dialog->priv->find_done_handler_id = 0;
+       }
 
-       dialog->priv->replace_all_done_handler_id = g_signal_connect (
-               cnt_editor, "replace-all-done",
-               G_CALLBACK (content_editor_replace_all_done_cb), dialog);
+       if (dialog->priv->replace_all_done_handler_id > 0) {
+               g_signal_handler_disconnect (
+                       dialog->priv->cnt_editor,
+                       dialog->priv->replace_all_done_handler_id);
+               dialog->priv->replace_all_done_handler_id = 0;
+       }
 
-       dialog->priv->cnt_editor = cnt_editor;
+       dialog->priv->cnt_editor = NULL;
 
-       G_OBJECT_CLASS (e_html_editor_replace_dialog_parent_class)->constructed (object);
+       /* Chain up to parent implementation */
+       GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->hide (widget);
 }
 
 static void
@@ -249,7 +258,6 @@ e_html_editor_replace_dialog_class_init (EHTMLEditorReplaceDialogClass *class)
        g_type_class_add_private (class, sizeof (EHTMLEditorReplaceDialogPrivate));
 
        object_class = G_OBJECT_CLASS (class);
-       object_class->constructed = html_editor_replace_dialog_constructed;
        object_class->dispose = html_editor_replace_dialog_dispose;
 
        widget_class = GTK_WIDGET_CLASS (class);
diff --git a/src/e-util/e-html-editor.c b/src/e-util/e-html-editor.c
index 04a6434a6e..91fca9b100 100644
--- a/src/e-util/e-html-editor.c
+++ b/src/e-util/e-html-editor.c
@@ -34,8 +34,12 @@
 #include "e-alert-sink.h"
 #include "e-html-editor-private.h"
 #include "e-content-editor.h"
+#include "e-markdown-editor.h"
 #include "e-misc-utils.h"
 #include "e-simple-async-result.h"
+#include "e-util-enumtypes.h"
+
+#define MARKDOWN_EDITOR_NAME "markdown"
 
 #define E_HTML_EDITOR_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -70,6 +74,7 @@
 
 enum {
        PROP_0,
+       PROP_MODE,
        PROP_FILENAME,
        PROP_PASTE_PLAIN_PREFER_PRE
 };
@@ -605,7 +610,7 @@ html_editor_spell_languages_changed (EHTMLEditor *editor)
                        E_HTML_EDITOR_SPELL_CHECK_DIALOG (
                        editor->priv->spell_check_dialog));
 
-       editor_actions_update_spellcheck_languages_menu (editor, (const gchar * const *) languages);
+       e_html_editor_actions_update_spellcheck_languages_menu (editor, (const gchar * const *) languages);
        g_clear_object (&spell_checker);
        g_strfreev (languages);
 }
@@ -760,6 +765,24 @@ html_editor_get_paste_plain_prefer_pre (EHTMLEditor *editor)
        return editor->priv->paste_plain_prefer_pre;
 }
 
+static gboolean
+e_html_editor_mode_to_bool_hide_in_markdown_cb (GBinding *binding,
+                                               const GValue *from_value,
+                                               GValue *to_value,
+                                               gpointer user_data)
+{
+       EContentEditorMode mode;
+
+       mode = g_value_get_enum (from_value);
+
+       g_value_set_boolean (to_value,
+               mode != E_CONTENT_EDITOR_MODE_MARKDOWN &&
+               mode != E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT &&
+               mode != E_CONTENT_EDITOR_MODE_MARKDOWN_HTML);
+
+       return TRUE;
+}
+
 static void
 html_editor_set_property (GObject *object,
                           guint property_id,
@@ -767,6 +790,12 @@ html_editor_set_property (GObject *object,
                           GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_MODE:
+                       e_html_editor_set_mode (
+                               E_HTML_EDITOR (object),
+                               g_value_get_enum (value));
+                       return;
+
                case PROP_FILENAME:
                        e_html_editor_set_filename (
                                E_HTML_EDITOR (object),
@@ -790,6 +819,12 @@ html_editor_get_property (GObject *object,
                           GParamSpec *pspec)
 {
        switch (property_id) {
+               case PROP_MODE:
+                       g_value_set_enum (
+                               value, e_html_editor_get_mode (
+                               E_HTML_EDITOR (object)));
+                       return;
+
                case PROP_FILENAME:
                        g_value_set_string (
                                value, e_html_editor_get_filename (
@@ -822,7 +857,23 @@ html_editor_constructed (GObject *object)
 
        e_extensible_load_extensions (E_EXTENSIBLE (object));
 
-       editor_actions_init (editor);
+       /* Register the markdown editor */
+       priv->markdown_editor = g_object_ref_sink (e_markdown_editor_new ());
+       e_html_editor_register_content_editor (editor, MARKDOWN_EDITOR_NAME, E_CONTENT_EDITOR 
(priv->markdown_editor));
+
+       /* Construct the main editing area. */
+       widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "visible", TRUE,
+               NULL);
+       gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
+       priv->content_editors_box = widget;
+
+       e_html_editor_actions_init (editor);
        priv->editor_layout_row = 2;
 
        /* Tweak the main-toolbar style. */
@@ -862,31 +913,10 @@ html_editor_constructed (GObject *object)
        priv->alert_bar = g_object_ref (widget);
        /* EAlertBar controls its own visibility. */
 
-       /* Construct the main editing area. */
+       /* Have the default editor added (it's done inside the function) */
        widget = GTK_WIDGET (e_html_editor_get_content_editor (editor));
-
-       /* Pack editors which implement GtkScrollable in a scrolled window */
-       if (GTK_IS_SCROLLABLE (widget)) {
-               GtkWidget *scrolled_window;
-
-               scrolled_window = gtk_scrolled_window_new (NULL, NULL);
-               gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
-                       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-               gtk_widget_show (scrolled_window);
-
-               gtk_grid_attach (GTK_GRID (editor), scrolled_window, 0, 4, 1, 1);
-
-               gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
-       } else {
-               gtk_grid_attach (GTK_GRID (editor), widget, 0, 4, 1, 1);
-       }
-
        gtk_widget_show (widget);
 
-       g_signal_connect (
-               widget, "context-menu-requested",
-               G_CALLBACK (html_editor_context_menu_requested_cb), editor);
-
        /* Add some combo boxes to the "edit" toolbar. */
 
        toolbar = GTK_TOOLBAR (priv->edit_toolbar);
@@ -913,6 +943,7 @@ html_editor_constructed (GObject *object)
        gtk_widget_set_tooltip_text (widget, _("Editing Mode"));
        gtk_toolbar_insert (toolbar, tool_item, 0);
        priv->mode_combo_box = g_object_ref (widget);
+       priv->mode_tool_item = g_object_ref (tool_item);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
 
        /* Add some combo boxes to the "html" toolbar. */
@@ -957,6 +988,41 @@ html_editor_constructed (GObject *object)
        priv->font_name_combo_box = g_object_ref (widget);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
 
+       e_binding_bind_property_full (
+               editor, "mode",
+               E_HTML_EDITOR_ACTION (editor, "paragraph-style-menu"), "visible",
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_mode_to_bool_hide_in_markdown_cb,
+               NULL, NULL, NULL);
+
+       e_binding_bind_property_full (
+               editor, "mode",
+               E_HTML_EDITOR_ACTION (editor, "justify-menu"), "visible",
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_mode_to_bool_hide_in_markdown_cb,
+               NULL, NULL, NULL);
+
+       e_binding_bind_property_full (
+               editor, "mode",
+               E_HTML_EDITOR_ACTION_WRAP_LINES (editor), "visible",
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_mode_to_bool_hide_in_markdown_cb,
+               NULL, NULL, NULL);
+
+       e_binding_bind_property_full (
+               editor, "mode",
+               E_HTML_EDITOR_ACTION_INDENT (editor), "visible",
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_mode_to_bool_hide_in_markdown_cb,
+               NULL, NULL, NULL);
+
+       e_binding_bind_property_full (
+               editor, "mode",
+               E_HTML_EDITOR_ACTION_UNINDENT (editor), "visible",
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_mode_to_bool_hide_in_markdown_cb,
+               NULL, NULL, NULL);
+
        g_signal_connect_after (object, "realize", G_CALLBACK (html_editor_realize), NULL);
 
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
@@ -976,6 +1042,9 @@ html_editor_dispose (GObject *object)
 
        priv = E_HTML_EDITOR_GET_PRIVATE (object);
 
+       if (priv->mode_change_content_cancellable)
+               g_cancellable_cancel (priv->mode_change_content_cancellable);
+
        g_clear_object (&priv->manager);
        g_clear_object (&priv->core_actions);
        g_clear_object (&priv->core_editor_actions);
@@ -993,14 +1062,24 @@ html_editor_dispose (GObject *object)
        g_clear_object (&priv->activity_bar);
        g_clear_object (&priv->alert_bar);
        g_clear_object (&priv->edit_area);
+       g_clear_object (&priv->markdown_editor);
 
        g_clear_object (&priv->fg_color_combo_box);
        g_clear_object (&priv->bg_color_combo_box);
        g_clear_object (&priv->mode_combo_box);
+       g_clear_object (&priv->mode_tool_item);
        g_clear_object (&priv->size_combo_box);
        g_clear_object (&priv->font_name_combo_box);
        g_clear_object (&priv->style_combo_box);
 
+       g_clear_object (&priv->mode_change_content_cancellable);
+
+       /* Do not unbind/disconnect signal handlers here, just free/unset them */
+       g_slist_free_full (priv->content_editor_bindings, g_object_unref);
+       priv->content_editor_bindings = NULL;
+       priv->subscript_notify_id = 0;
+       priv->superscript_notify_id = 0;
+
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_html_editor_parent_class)->dispose (object);
 }
@@ -1012,6 +1091,7 @@ html_editor_finalize (GObject *object)
 
        g_hash_table_destroy (editor->priv->cid_parts);
        g_hash_table_destroy (editor->priv->content_editors);
+       g_hash_table_destroy (editor->priv->content_editors_for_mode);
 
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_html_editor_parent_class)->finalize (object);
@@ -1049,6 +1129,19 @@ e_html_editor_class_init (EHTMLEditorClass *class)
        class->update_actions = html_editor_update_actions;
        class->spell_languages_changed = html_editor_spell_languages_changed;
 
+       g_object_class_install_property (
+               object_class,
+               PROP_MODE,
+               g_param_spec_enum (
+                       "mode",
+                       NULL,
+                       NULL,
+                       E_TYPE_CONTENT_EDITOR_MODE,
+                       E_CONTENT_EDITOR_MODE_HTML,
+                       G_PARAM_READWRITE |
+                       G_PARAM_EXPLICIT_NOTIFY |
+                       G_PARAM_STATIC_STRINGS));
+
        g_object_class_install_property (
                object_class,
                PROP_FILENAME,
@@ -1109,6 +1202,7 @@ e_html_editor_init (EHTMLEditor *editor)
 
        priv = editor->priv;
 
+       priv->mode = E_CONTENT_EDITOR_MODE_HTML;
        priv->manager = gtk_ui_manager_new ();
        priv->core_actions = gtk_action_group_new ("core");
        priv->core_editor_actions = gtk_action_group_new ("core-editor");
@@ -1120,6 +1214,7 @@ e_html_editor_init (EHTMLEditor *editor)
        priv->suggestion_actions = gtk_action_group_new ("suggestion");
        priv->cid_parts = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, 
g_object_unref);
        priv->content_editors = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
+       priv->content_editors_for_mode = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
 
        filename = html_editor_find_ui_file ("e-html-editor-manager.ui");
        if (!gtk_ui_manager_add_ui_from_file (priv->manager, filename, &error)) {
@@ -1142,23 +1237,12 @@ e_html_editor_content_editor_initialized (EContentEditor *content_editor,
        g_return_if_fail (E_IS_HTML_EDITOR (html_editor));
        g_return_if_fail (content_editor == e_html_editor_get_content_editor (html_editor));
 
-       e_binding_bind_property (
-               html_editor->priv->fg_color_combo_box, "current-color",
-               content_editor, "font-color",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
-               content_editor, "editable",
-               html_editor->priv->fg_color_combo_box, "sensitive",
-               G_BINDING_SYNC_CREATE);
-       e_binding_bind_property (
-               html_editor->priv->bg_color_combo_box, "current-color",
-               content_editor, "background-color",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
-               content_editor, "editable",
-               html_editor->priv->bg_color_combo_box, "sensitive",
-               G_BINDING_SYNC_CREATE);
-       editor_actions_bind (html_editor);
+       /* Synchronize widget mode with the buttons */
+       e_html_editor_set_mode (html_editor, E_CONTENT_EDITOR_MODE_HTML);
+
+       /* Make sure the actions did bind, even when the content editor did not change */
+       e_html_editor_actions_unbind (html_editor);
+       e_html_editor_actions_bind (html_editor);
 
        g_object_set (G_OBJECT (content_editor),
                "halign", GTK_ALIGN_FILL,
@@ -1227,53 +1311,268 @@ e_html_editor_new_finish (GAsyncResult *result,
 }
 
 /**
- * e_html_editor_get_content_editor:
+ * e_html_editor_connect_focus_tracker:
  * @editor: an #EHTMLEditor
+ * @focus_tracker: an #EFocusTracker
  *
- * Returns instance of #EContentEditor used in the @editor.
- */
-EContentEditor *
-e_html_editor_get_content_editor (EHTMLEditor *editor)
+ * Connects @editor actions and widgets to the @focus_tracker.
+ *
+ * Since: 3.44
+ **/
+void
+e_html_editor_connect_focus_tracker (EHTMLEditor *editor,
+                                    EFocusTracker *focus_tracker)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+       e_focus_tracker_set_cut_clipboard_action (focus_tracker,
+               e_html_editor_get_action (editor, "cut"));
+
+       e_focus_tracker_set_copy_clipboard_action (focus_tracker,
+               e_html_editor_get_action (editor, "copy"));
+
+       e_focus_tracker_set_paste_clipboard_action (focus_tracker,
+               e_html_editor_get_action (editor, "paste"));
+
+       e_focus_tracker_set_select_all_action (focus_tracker,
+               e_html_editor_get_action (editor, "select-all"));
+
+       e_focus_tracker_set_undo_action (focus_tracker,
+               e_html_editor_get_action (editor, "undo"));
+
+       e_focus_tracker_set_redo_action (focus_tracker,
+               e_html_editor_get_action (editor, "redo"));
+
+       e_markdown_editor_connect_focus_tracker (E_MARKDOWN_EDITOR (editor->priv->markdown_editor), 
focus_tracker);
+}
+
+/**
+ * e_html_editor_get_content_box:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns: (transfer none): the content box, the content editors are
+ *    packed into.
+ *
+ * Since: 3.44
+ **/
+GtkWidget *
+e_html_editor_get_content_box (EHTMLEditor *editor)
 {
        g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
 
-       if (!editor->priv->use_content_editor) {
-               GSettings *settings;
-               gchar *name;
+       return editor->priv->content_editors_box;
+}
+
+static void
+e_html_editor_content_editor_notify_mode_cb (GObject *object,
+                                            GParamSpec *param,
+                                            gpointer user_data)
+{
+       EHTMLEditor *editor = user_data;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (E_IS_CONTENT_EDITOR (object));
+
+       if (E_CONTENT_EDITOR (object) == e_html_editor_get_content_editor (editor)) {
+               EContentEditorMode mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+
+               g_object_get (object, "mode", &mode, NULL);
+
+               e_html_editor_set_mode (editor, mode);
+       }
+}
+
+static EContentEditor *
+e_html_editor_get_content_editor_for_mode (EHTMLEditor *editor,
+                                          EContentEditorMode mode)
+{
+       EContentEditor *cnt_editor;
+       GSettings *settings;
+       const gchar *mode_name = NULL;
+       gchar *name;
+
+       if (!g_hash_table_size (editor->priv->content_editors))
+               return NULL;
+
+       cnt_editor = g_hash_table_lookup (editor->priv->content_editors_for_mode, GINT_TO_POINTER (mode));
+
+       if (cnt_editor)
+               return cnt_editor;
+
+       switch (mode) {
+       case E_CONTENT_EDITOR_MODE_UNKNOWN:
+               g_warn_if_reached ();
+               break;
+       case E_CONTENT_EDITOR_MODE_PLAIN_TEXT:
+               mode_name = "plain";
+               break;
+       case E_CONTENT_EDITOR_MODE_HTML:
+               mode_name = "html";
+               break;
+       case E_CONTENT_EDITOR_MODE_MARKDOWN:
+               mode_name = "markdown";
+               break;
+       case E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT:
+               mode_name = "markdown-plain";
+               break;
+       case E_CONTENT_EDITOR_MODE_MARKDOWN_HTML:
+               mode_name = "markdown-html";
+               break;
+       }
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       name = g_settings_get_string (settings, "composer-editor");
+       g_clear_object (&settings);
+
+       if (name && *name && mode_name) {
+               gchar **split_names;
+               gint ii, mode_name_len = strlen (mode_name);
 
-               if (!g_hash_table_size (editor->priv->content_editors))
-                       return NULL;
+               split_names = g_strsplit (name, ",", -1);
 
-               settings = e_util_ref_settings ("org.gnome.evolution.mail");
-               name = g_settings_get_string (settings, "composer-editor");
-               g_clear_object (&settings);
+               /* first round with the mode-specific overrides */
+               for (ii = 0; split_names && split_names[ii] && !cnt_editor; ii++) {
+                       const gchar *check_name = split_names[ii];
 
-               if (name)
-                       editor->priv->use_content_editor = g_hash_table_lookup 
(editor->priv->content_editors, name);
+                       if (g_ascii_strncasecmp (check_name, mode_name, mode_name_len) == 0 &&
+                           check_name[mode_name_len] == ':') {
+                               cnt_editor = g_hash_table_lookup (editor->priv->content_editors, check_name + 
mode_name_len + 1);
 
-               g_free (name);
+                               if (cnt_editor && !e_content_editor_supports_mode (cnt_editor, mode))
+                                       cnt_editor = NULL;
+                       }
+               }
+
+               /* second round without the mode-specific overrides */
+               for (ii = 0; split_names && split_names[ii] && !cnt_editor; ii++) {
+                       const gchar *check_name = split_names[ii];
+
+                       cnt_editor = g_hash_table_lookup (editor->priv->content_editors, check_name);
+
+                       if (cnt_editor && !e_content_editor_supports_mode (cnt_editor, mode))
+                               cnt_editor = NULL;
+               }
+
+               g_strfreev (split_names);
+       }
 
-               if (!editor->priv->use_content_editor)
-                       editor->priv->use_content_editor = g_hash_table_lookup 
(editor->priv->content_editors, DEFAULT_CONTENT_EDITOR_NAME);
+       g_free (name);
 
-               if (!editor->priv->use_content_editor) {
-                       GHashTableIter iter;
-                       gpointer key, value;
+       if (!cnt_editor) {
+               if (mode == E_CONTENT_EDITOR_MODE_MARKDOWN ||
+                   mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT ||
+                   mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML)
+                       cnt_editor = g_hash_table_lookup (editor->priv->content_editors, 
MARKDOWN_EDITOR_NAME);
+               else
+                       cnt_editor = g_hash_table_lookup (editor->priv->content_editors, 
DEFAULT_CONTENT_EDITOR_NAME);
 
-                       g_hash_table_iter_init (&iter, editor->priv->content_editors);
-                       if (g_hash_table_iter_next (&iter, &key, &value)) {
-                               editor->priv->use_content_editor = value;
+               if (cnt_editor && !e_content_editor_supports_mode (cnt_editor, mode))
+                       cnt_editor = NULL;
+       }
+
+       if (!cnt_editor) {
+               GHashTableIter iter;
+               gpointer value;
+
+               g_hash_table_iter_init (&iter, editor->priv->content_editors);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       if (e_content_editor_supports_mode (value, mode)) {
+                               cnt_editor = value;
+                               break;
                        }
                }
+       }
 
-               if (editor->priv->use_content_editor) {
-                       e_content_editor_setup_editor (editor->priv->use_content_editor, editor);
+       if (cnt_editor) {
+               GHashTableIter iter;
+               gpointer value;
 
-                       g_signal_connect_swapped (editor->priv->use_content_editor, "ref-mime-part",
-                               G_CALLBACK (e_html_editor_ref_cid_part), editor);
+               g_hash_table_iter_init (&iter, editor->priv->content_editors_for_mode);
+               while (g_hash_table_iter_next (&iter, NULL, &value)) {
+                       /* The editor can be used for multiple modes and it is already packed in the content 
box. */
+                       if (value == cnt_editor) {
+                               g_hash_table_insert (editor->priv->content_editors_for_mode, GINT_TO_POINTER 
(mode), cnt_editor);
+                               return cnt_editor;
+                       }
                }
        }
 
+       if (cnt_editor) {
+               e_content_editor_setup_editor (cnt_editor, editor);
+
+               g_signal_connect_swapped (cnt_editor, "ref-mime-part",
+                       G_CALLBACK (e_html_editor_ref_cid_part), editor);
+
+               e_signal_connect_notify (cnt_editor, "notify::mode",
+                       G_CALLBACK (e_html_editor_content_editor_notify_mode_cb), editor);
+
+               /* Pack editors which implement GtkScrollable in a scrolled window */
+               if (GTK_IS_SCROLLABLE (cnt_editor)) {
+                       GtkWidget *scrolled_window;
+
+                       scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+                       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+                       gtk_box_pack_start (GTK_BOX (editor->priv->content_editors_box), scrolled_window, 
TRUE, TRUE, 0);
+
+                       gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (cnt_editor));
+
+                       e_binding_bind_property (cnt_editor, "visible",
+                               scrolled_window, "visible",
+                               G_BINDING_SYNC_CREATE);
+               } else {
+                       gtk_box_pack_start (GTK_BOX (editor->priv->content_editors_box), GTK_WIDGET 
(cnt_editor), TRUE, TRUE, 0);
+               }
+
+               g_signal_connect (
+                       cnt_editor, "context-menu-requested",
+                       G_CALLBACK (html_editor_context_menu_requested_cb), editor);
+
+               g_hash_table_insert (editor->priv->content_editors_for_mode, GINT_TO_POINTER (mode), 
cnt_editor);
+       }
+
+       return cnt_editor;
+}
+
+gboolean
+e_html_editor_has_editor_for_mode (EHTMLEditor *editor,
+                                  EContentEditorMode mode)
+{
+       GHashTableIter iter;
+       gpointer value;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), FALSE);
+
+       g_hash_table_iter_init (&iter, editor->priv->content_editors);
+
+       while (g_hash_table_iter_next (&iter, NULL, &value)) {
+               EContentEditor *cnt_editor = value;
+
+               if (e_content_editor_supports_mode (cnt_editor, mode))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+/**
+ * e_html_editor_get_content_editor:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns instance of #EContentEditor used in the @editor.
+ */
+EContentEditor *
+e_html_editor_get_content_editor (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+
+       if (!editor->priv->use_content_editor) {
+               editor->priv->use_content_editor =
+                       e_html_editor_get_content_editor_for_mode (editor, editor->priv->mode);
+       }
+
        return editor->priv->use_content_editor;
 }
 
@@ -1300,6 +1599,25 @@ e_html_editor_get_content_editor_name (EHTMLEditor *editor)
        return NULL;
 }
 
+static void
+e_html_editor_content_changed_cb (EContentEditor *cnt_editor,
+                                 gpointer user_data)
+{
+       EHTMLEditor *editor = user_data;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       if (editor->priv->mode_change_content_cancellable) {
+               if (cnt_editor == editor->priv->use_content_editor) {
+                       g_cancellable_cancel (editor->priv->mode_change_content_cancellable);
+                       g_clear_object (&editor->priv->mode_change_content_cancellable);
+               }
+       }
+
+       g_signal_handlers_disconnect_by_func (cnt_editor,
+               G_CALLBACK (e_html_editor_content_changed_cb), editor);
+}
+
 void
 e_html_editor_register_content_editor (EHTMLEditor *editor,
                                       const gchar *name,
@@ -1321,6 +1639,189 @@ e_html_editor_register_content_editor (EHTMLEditor *editor,
        }
 }
 
+static void
+e_html_editor_update_content_on_mode_change_cb (GObject *source_object,
+                                               GAsyncResult *result,
+                                               gpointer user_data)
+{
+       GWeakRef *weak_ref = user_data;
+       EContentEditorContentHash *content_hash;
+       EContentEditor *cnt_editor;
+       EHTMLEditor *editor;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+       g_return_if_fail (weak_ref != NULL);
+
+       editor = g_weak_ref_get (weak_ref);
+
+       e_weak_ref_free (weak_ref);
+
+       if (!editor)
+               return;
+
+       g_clear_object (&editor->priv->mode_change_content_cancellable);
+
+       cnt_editor = E_CONTENT_EDITOR (source_object);
+       content_hash = e_content_editor_get_content_finish (cnt_editor, result, NULL);
+
+       if (content_hash) {
+               gpointer text;
+
+               text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_HTML);
+
+               if (editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML && text) {
+                       e_content_editor_insert_content (editor->priv->use_content_editor, text,
+                               E_CONTENT_EDITOR_INSERT_CONVERT |
+                               E_CONTENT_EDITOR_INSERT_TEXT_HTML |
+                               E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
+               } else {
+                       text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
+
+                       if (text) {
+                               e_content_editor_insert_content (editor->priv->use_content_editor, text,
+                                       E_CONTENT_EDITOR_INSERT_CONVERT |
+                                       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
+                                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
+                       }
+               }
+
+               e_content_editor_clear_undo_redo_history (editor->priv->use_content_editor);
+
+               e_content_editor_util_free_content_hash (content_hash);
+       }
+
+       g_object_unref (editor);
+}
+
+/**
+ * e_html_editor_get_mode:
+ * @editor: an #EHTMLEditor
+ *
+ * Returns: Current editor mode, as an #EContentEditorMode
+ *
+ * Since: 3.44
+ **/
+EContentEditorMode
+e_html_editor_get_mode (EHTMLEditor *editor)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), E_CONTENT_EDITOR_MODE_PLAIN_TEXT);
+
+       return editor->priv->mode;
+}
+
+/**
+ * e_html_editor_set_mode:
+ * @editor: an #EHTMLEditor
+ * @mode: an #EContentEditorMode
+ *
+ * Sets the editor mode.
+ *
+ * Since: 3.44
+ **/
+void
+e_html_editor_set_mode (EHTMLEditor *editor,
+                       EContentEditorMode mode)
+{
+       EContentEditor *cnt_editor;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       if (mode == E_CONTENT_EDITOR_MODE_UNKNOWN)
+               mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+
+       /* In case the same mode is set, but no editor is assigned, then assign it */
+       if (editor->priv->mode == mode && editor->priv->use_content_editor != NULL)
+               return;
+
+       if (editor->priv->mode_change_content_cancellable) {
+               g_cancellable_cancel (editor->priv->mode_change_content_cancellable);
+               g_clear_object (&editor->priv->mode_change_content_cancellable);
+       }
+
+       cnt_editor = e_html_editor_get_content_editor_for_mode (editor, mode);
+
+       if (cnt_editor) {
+               gboolean editor_changed = cnt_editor != editor->priv->use_content_editor;
+
+               if (editor_changed) {
+                       EContentEditorInterface *iface;
+                       gboolean is_focused = FALSE;
+
+                       if (editor->priv->use_content_editor) {
+                               e_html_editor_actions_unbind (editor);
+
+                               is_focused = e_content_editor_is_focus (editor->priv->use_content_editor);
+
+                               editor->priv->mode_change_content_cancellable = g_cancellable_new ();
+
+                               g_signal_connect_object (cnt_editor, "content-changed",
+                                       G_CALLBACK (e_html_editor_content_changed_cb), editor, 0);
+
+                               /* Transfer also the content between editors */
+                               e_content_editor_get_content (editor->priv->use_content_editor,
+                                       E_CONTENT_EDITOR_GET_TO_SEND_HTML | 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN,
+                                       "localhost", editor->priv->mode_change_content_cancellable,
+                                       e_html_editor_update_content_on_mode_change_cb,
+                                       e_weak_ref_new (editor));
+
+                               gtk_widget_hide (GTK_WIDGET (editor->priv->use_content_editor));
+
+                               if (E_IS_MARKDOWN_EDITOR (editor->priv->use_content_editor)) {
+                                       EMarkdownEditor *markdown_editor;
+                                       GtkToolbar *toolbar;
+
+                                       markdown_editor = E_MARKDOWN_EDITOR 
(editor->priv->use_content_editor);
+
+                                       e_markdown_editor_set_preview_mode (markdown_editor, FALSE);
+
+                                       toolbar = e_markdown_editor_get_action_toolbar (markdown_editor);
+                                       gtk_container_remove (GTK_CONTAINER (toolbar), GTK_WIDGET 
(editor->priv->mode_tool_item));
+
+                                       toolbar = GTK_TOOLBAR (editor->priv->edit_toolbar);
+                                       gtk_toolbar_insert (toolbar, editor->priv->mode_tool_item, 0);
+
+                                       gtk_widget_show (GTK_WIDGET (editor->priv->edit_toolbar));
+                               }
+                       }
+
+                       gtk_widget_show (GTK_WIDGET (cnt_editor));
+
+                       if (E_IS_MARKDOWN_EDITOR (cnt_editor)) {
+                               GtkToolbar *toolbar;
+
+                               toolbar = GTK_TOOLBAR (editor->priv->edit_toolbar);
+                               gtk_container_remove (GTK_CONTAINER (toolbar), GTK_WIDGET 
(editor->priv->mode_tool_item));
+
+                               toolbar = e_markdown_editor_get_action_toolbar (E_MARKDOWN_EDITOR 
(cnt_editor));
+                               gtk_toolbar_insert (toolbar, editor->priv->mode_tool_item, 0);
+
+                               gtk_widget_hide (GTK_WIDGET (editor->priv->edit_toolbar));
+                       }
+
+                       if (is_focused)
+                               e_content_editor_grab_focus (cnt_editor);
+
+                       /* Disable spell-check dialog when the content editor doesn't
+                          support moving between misspelled words. */
+                       iface = E_CONTENT_EDITOR_GET_IFACE (cnt_editor);
+
+                       gtk_action_set_visible (e_html_editor_get_action (editor, "spell-check"),
+                               iface && iface->spell_check_next_word && iface->spell_check_prev_word);
+
+                       e_content_editor_clear_undo_redo_history (cnt_editor);
+               }
+
+               editor->priv->mode = mode;
+               editor->priv->use_content_editor = cnt_editor;
+
+               if (editor_changed)
+                       e_html_editor_actions_bind (editor);
+
+               g_object_set (G_OBJECT (cnt_editor), "mode", mode, NULL);
+               g_object_notify (G_OBJECT (editor), "mode");
+       }
+}
+
 /**
  * e_html_editor_get_ui_manager:
  * @editor: an #EHTMLEditor
diff --git a/src/e-util/e-html-editor.h b/src/e-util/e-html-editor.h
index b96ab542f9..b30250bdf3 100644
--- a/src/e-util/e-html-editor.h
+++ b/src/e-util/e-html-editor.h
@@ -26,9 +26,11 @@
 #define E_HTML_EDITOR_H
 
 #include <gtk/gtk.h>
+#include <e-util/e-action-combo-box.h>
 #include <e-util/e-activity.h>
 #include <e-util/e-activity-bar.h>
 #include <e-util/e-content-editor.h>
+#include <e-util/e-focus-tracker.h>
 
 /* Standard GObject macros */
 #define E_TYPE_HTML_EDITOR \
@@ -78,6 +80,10 @@ void         e_html_editor_new               (GAsyncReadyCallback callback,
                                                 gpointer user_data);
 GtkWidget *    e_html_editor_new_finish        (GAsyncResult *result,
                                                 GError **error);
+void           e_html_editor_connect_focus_tracker
+                                               (EHTMLEditor *editor,
+                                                EFocusTracker *focus_tracker);
+GtkWidget *    e_html_editor_get_content_box   (EHTMLEditor *editor);
 EContentEditor *
                e_html_editor_get_content_editor
                                                (EHTMLEditor *editor);
@@ -85,6 +91,10 @@ void         e_html_editor_register_content_editor
                                                (EHTMLEditor *editor,
                                                 const gchar *name,
                                                 EContentEditor *cnt_editor);
+EContentEditorMode
+               e_html_editor_get_mode          (EHTMLEditor *editor);
+void           e_html_editor_set_mode          (EHTMLEditor *editor,
+                                                EContentEditorMode mode);
 GtkBuilder *   e_html_editor_get_builder       (EHTMLEditor *editor);
 GtkUIManager * e_html_editor_get_ui_manager    (EHTMLEditor *editor);
 GtkAction *    e_html_editor_get_action        (EHTMLEditor *editor,
@@ -129,6 +139,9 @@ void                e_html_editor_remove_all_cid_parts
 CamelMimePart * e_html_editor_ref_cid_part     (EHTMLEditor *editor,
                                                 const gchar *cid_uri);
 
+EActionComboBox *
+               e_html_editor_util_new_mode_combobox
+                                               (void);
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_H */
diff --git a/src/e-util/e-mail-signature-combo-box.c b/src/e-util/e-mail-signature-combo-box.c
index 7dac48bc1e..34c313590a 100644
--- a/src/e-util/e-mail-signature-combo-box.c
+++ b/src/e-util/e-mail-signature-combo-box.c
@@ -618,7 +618,7 @@ struct _LoadContext {
        GCancellable *cancellable;
        gchar *contents;
        gsize length;
-       gboolean is_html;
+       EContentEditorMode editor_mode;
 };
 
 static void
@@ -708,7 +708,7 @@ mail_signature_combo_box_autogenerate (EMailSignatureComboBox *combo_box,
 
        context->length = buffer->len;
        context->contents = g_string_free (buffer, FALSE);
-       context->is_html = TRUE;
+       context->editor_mode = E_CONTENT_EDITOR_MODE_HTML;
 
        g_object_unref (source);
 }
@@ -740,7 +740,17 @@ mail_signature_combo_box_load_cb (ESource *source,
        extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
        extension = e_source_get_extension (source, extension_name);
        mime_type = e_source_mail_signature_get_mime_type (extension);
-       context->is_html = (g_strcmp0 (mime_type, "text/html") == 0);
+
+       if (g_strcmp0 (mime_type, "text/html") == 0)
+               context->editor_mode = E_CONTENT_EDITOR_MODE_HTML;
+       else if (g_strcmp0 (mime_type, "text/markdown") == 0)
+               context->editor_mode = E_CONTENT_EDITOR_MODE_MARKDOWN;
+       else if (g_strcmp0 (mime_type, "text/markdown-plain") == 0)
+               context->editor_mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+       else if (g_strcmp0 (mime_type, "text/markdown-html") == 0)
+               context->editor_mode = E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+       else
+               context->editor_mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
 
        g_simple_async_result_complete (simple);
 
@@ -810,7 +820,7 @@ e_mail_signature_combo_box_load_selected_finish (EMailSignatureComboBox *combo_b
                                                  GAsyncResult *result,
                                                  gchar **contents,
                                                  gsize *length,
-                                                 gboolean *is_html,
+                                                 EContentEditorMode *out_editor_mode,
                                                  GError **error)
 {
        GSimpleAsyncResult *simple;
@@ -838,8 +848,8 @@ e_mail_signature_combo_box_load_selected_finish (EMailSignatureComboBox *combo_b
        if (length != NULL)
                *length = context->length;
 
-       if (is_html != NULL)
-               *is_html = context->is_html;
+       if (out_editor_mode)
+               *out_editor_mode = context->editor_mode;
 
        return TRUE;
 }
diff --git a/src/e-util/e-mail-signature-combo-box.h b/src/e-util/e-mail-signature-combo-box.h
index 62c440aad7..ba88019a42 100644
--- a/src/e-util/e-mail-signature-combo-box.h
+++ b/src/e-util/e-mail-signature-combo-box.h
@@ -24,6 +24,7 @@
 
 #include <gtk/gtk.h>
 #include <libedataserver/libedataserver.h>
+#include <e-util/e-util-enums.h>
 
 /* Standard GObject macros */
 #define E_TYPE_MAIL_SIGNATURE_COMBO_BOX \
@@ -101,7 +102,7 @@ gboolean    e_mail_signature_combo_box_load_selected_finish
                                         GAsyncResult *result,
                                         gchar **contents,
                                         gsize *length,
-                                        gboolean *is_html,
+                                        EContentEditorMode *out_editor_mode,
                                         GError **error);
 
 G_END_DECLS
diff --git a/src/e-util/e-mail-signature-editor.c b/src/e-util/e-mail-signature-editor.c
index 38d8d2a729..7d1d88342d 100644
--- a/src/e-util/e-mail-signature-editor.c
+++ b/src/e-util/e-mail-signature-editor.c
@@ -50,6 +50,7 @@ struct _AsyncContext {
        ESource *source;
        GCancellable *cancellable;
        EContentEditorGetContentFlags contents_flag;
+       EContentEditorMode editor_mode;
        gchar *contents;
        gsize length;
        GDestroyNotify destroy_contents;
@@ -108,13 +109,13 @@ mail_signature_editor_loaded_cb (GObject *object,
 {
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
+       EContentEditorMode mode;
        ESource *source;
        EMailSignatureEditor *window;
        ESourceMailSignature *extension;
        const gchar *extension_name;
        const gchar *mime_type;
        gchar *contents = NULL;
-       gboolean is_html;
        GError *error = NULL;
 
        source = E_SOURCE (object);
@@ -147,15 +148,24 @@ mail_signature_editor_loaded_cb (GObject *object,
        extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
        extension = e_source_get_extension (source, extension_name);
        mime_type = e_source_mail_signature_get_mime_type (extension);
-       is_html = (g_strcmp0 (mime_type, "text/html") == 0);
+       if (g_strcmp0 (mime_type, "text/html") == 0)
+               mode = E_CONTENT_EDITOR_MODE_HTML;
+       else if (g_strcmp0 (mime_type, "text/markdown") == 0)
+               mode = E_CONTENT_EDITOR_MODE_MARKDOWN;
+       else if (g_strcmp0 (mime_type, "text/markdown-plain") == 0)
+               mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+       else if (g_strcmp0 (mime_type, "text/markdown-html") == 0)
+               mode = E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+       else
+               mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
 
        editor = e_mail_signature_editor_get_editor (window);
+       e_html_editor_set_mode (editor, mode);
        cnt_editor = e_html_editor_get_content_editor (editor);
-       e_content_editor_set_html_mode (cnt_editor, is_html);
 
-       if (is_html) {
+       if (mode == E_CONTENT_EDITOR_MODE_HTML) {
                if (strstr (contents, "data-evo-signature-plain-text-mode"))
-                       e_content_editor_set_html_mode (cnt_editor, FALSE);
+                       e_html_editor_set_mode (editor, E_CONTENT_EDITOR_MODE_PLAIN_TEXT);
 
                e_content_editor_insert_content (
                        cnt_editor,
@@ -597,17 +607,7 @@ mail_signature_editor_constructed (GObject *object)
        /* Configure an EFocusTracker to manage selection actions. */
        focus_tracker = e_focus_tracker_new (GTK_WINDOW (window));
 
-       action = e_html_editor_get_action (editor, "cut");
-       e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "copy");
-       e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "paste");
-       e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
-
-       action = e_html_editor_get_action (editor, "select-all");
-       e_focus_tracker_set_select_all_action (focus_tracker, action);
+       e_html_editor_connect_focus_tracker (editor, focus_tracker);
 
        window->priv->focus_tracker = focus_tracker;
 
@@ -917,6 +917,7 @@ mail_signature_editor_content_hash_ready_cb (GObject *source_object,
        EContentEditorContentHash *content_hash;
        ESourceMailSignature *extension;
        AsyncContext *async_context;
+       const gchar *mime_type = "text/plain";
        GError *error = NULL;
 
        g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
@@ -946,9 +947,29 @@ mail_signature_editor_content_hash_ready_cb (GObject *source_object,
 
        async_context->length = strlen (async_context->contents);
 
+       switch (async_context->editor_mode) {
+       case E_CONTENT_EDITOR_MODE_UNKNOWN:
+               g_warn_if_reached ();
+               break;
+       case E_CONTENT_EDITOR_MODE_PLAIN_TEXT:
+               mime_type = "text/plain";
+               break;
+       case E_CONTENT_EDITOR_MODE_HTML:
+               mime_type = "text/html";
+               break;
+       case E_CONTENT_EDITOR_MODE_MARKDOWN:
+               mime_type = "text/markdown";
+               break;
+       case E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT:
+               mime_type = "text/markdown-plain";
+               break;
+       case E_CONTENT_EDITOR_MODE_MARKDOWN_HTML:
+               mime_type = "text/markdown-html";
+               break;
+       }
+
        extension = e_source_get_extension (async_context->source, E_SOURCE_EXTENSION_MAIL_SIGNATURE);
-       e_source_mail_signature_set_mime_type (extension,
-               async_context->contents_flag == E_CONTENT_EDITOR_GET_RAW_BODY_HTML ? "text/html" : 
"text/plain");
+       e_source_mail_signature_set_mime_type (extension, mime_type);
 
        e_source_registry_commit_source (
                async_context->registry, async_context->source,
@@ -981,7 +1002,9 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window,
        async_context = g_slice_new0 (AsyncContext);
        async_context->registry = g_object_ref (registry);
        async_context->source = g_object_ref (source);
-       async_context->contents_flag = e_content_editor_get_html_mode (cnt_editor) ? 
E_CONTENT_EDITOR_GET_RAW_BODY_HTML : E_CONTENT_EDITOR_GET_TO_SEND_PLAIN;
+       async_context->editor_mode = e_html_editor_get_mode (editor);
+       async_context->contents_flag = async_context->editor_mode == E_CONTENT_EDITOR_MODE_HTML ?
+               E_CONTENT_EDITOR_GET_RAW_BODY_HTML : E_CONTENT_EDITOR_GET_TO_SEND_PLAIN;
 
        if (G_IS_CANCELLABLE (cancellable))
                async_context->cancellable = g_object_ref (cancellable);
diff --git a/src/e-util/e-mail-signature-manager.c b/src/e-util/e-mail-signature-manager.c
index b414828d1f..e1d7cca68d 100644
--- a/src/e-util/e-mail-signature-manager.c
+++ b/src/e-util/e-mail-signature-manager.c
@@ -46,12 +46,12 @@ struct _EMailSignatureManagerPrivate {
        GtkWidget *preview;             /* not referenced */
        GtkWidget *preview_frame;       /* not referenced */
 
-       gboolean prefer_html;
+       EContentEditorMode prefer_mode;
 };
 
 enum {
        PROP_0,
-       PROP_PREFER_HTML,
+       PROP_PREFER_MODE,
        PROP_REGISTRY
 };
 
@@ -188,10 +188,10 @@ mail_signature_manager_set_property (GObject *object,
                                 GParamSpec *pspec)
 {
        switch (property_id) {
-               case PROP_PREFER_HTML:
-                       e_mail_signature_manager_set_prefer_html (
+               case PROP_PREFER_MODE:
+                       e_mail_signature_manager_set_prefer_mode (
                                E_MAIL_SIGNATURE_MANAGER (object),
-                               g_value_get_boolean (value));
+                               g_value_get_enum (value));
                        return;
 
                case PROP_REGISTRY:
@@ -211,10 +211,10 @@ mail_signature_manager_get_property (GObject *object,
                                 GParamSpec *pspec)
 {
        switch (property_id) {
-               case PROP_PREFER_HTML:
-                       g_value_set_boolean (
+               case PROP_PREFER_MODE:
+                       g_value_set_enum (
                                value,
-                               e_mail_signature_manager_get_prefer_html (
+                               e_mail_signature_manager_get_prefer_mode (
                                E_MAIL_SIGNATURE_MANAGER (object)));
                        return;
 
@@ -396,7 +396,6 @@ mail_signature_manager_editor_created_add_signature_cb (GObject *source_object,
 {
        EMailSignatureManager *manager = user_data;
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
        GtkWidget *widget;
        GError *error = NULL;
 
@@ -411,8 +410,7 @@ mail_signature_manager_editor_created_add_signature_cb (GObject *source_object,
        }
 
        editor = e_mail_signature_editor_get_editor (E_MAIL_SIGNATURE_EDITOR (widget));
-       cnt_editor = e_html_editor_get_content_editor (editor);
-       e_content_editor_set_html_mode (cnt_editor, manager->priv->prefer_html);
+       e_html_editor_set_mode (editor, manager->priv->prefer_mode);
 
        mail_signature_manager_emit_editor_created (manager, widget);
 
@@ -601,12 +599,13 @@ e_mail_signature_manager_class_init (EMailSignatureManagerClass *class)
 
        g_object_class_install_property (
                object_class,
-               PROP_PREFER_HTML,
-               g_param_spec_boolean (
-                       "prefer-html",
-                       "Prefer HTML",
+               PROP_PREFER_MODE,
+               g_param_spec_enum (
+                       "prefer-mode",
+                       "Prefer editor mode",
                        NULL,
-                       TRUE,
+                       E_TYPE_CONTENT_EDITOR_MODE,
+                       E_CONTENT_EDITOR_MODE_HTML,
                        G_PARAM_READWRITE |
                        G_PARAM_CONSTRUCT |
                        G_PARAM_STATIC_STRINGS));
@@ -719,26 +718,29 @@ e_mail_signature_manager_remove_signature (EMailSignatureManager *manager)
        g_signal_emit (manager, signals[REMOVE_SIGNATURE], 0);
 }
 
-gboolean
-e_mail_signature_manager_get_prefer_html (EMailSignatureManager *manager)
+EContentEditorMode
+e_mail_signature_manager_get_prefer_mode (EMailSignatureManager *manager)
 {
        g_return_val_if_fail (E_IS_MAIL_SIGNATURE_MANAGER (manager), FALSE);
 
-       return manager->priv->prefer_html;
+       return manager->priv->prefer_mode;
 }
 
 void
-e_mail_signature_manager_set_prefer_html (EMailSignatureManager *manager,
-                                          gboolean prefer_html)
+e_mail_signature_manager_set_prefer_mode (EMailSignatureManager *manager,
+                                          EContentEditorMode prefer_mode)
 {
        g_return_if_fail (E_IS_MAIL_SIGNATURE_MANAGER (manager));
 
-       if (manager->priv->prefer_html == prefer_html)
+       if (prefer_mode == E_CONTENT_EDITOR_MODE_UNKNOWN)
+               prefer_mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+
+       if (manager->priv->prefer_mode == prefer_mode)
                return;
 
-       manager->priv->prefer_html = prefer_html;
+       manager->priv->prefer_mode = prefer_mode;
 
-       g_object_notify (G_OBJECT (manager), "prefer-html");
+       g_object_notify (G_OBJECT (manager), "prefer-mode");
 }
 
 ESourceRegistry *
diff --git a/src/e-util/e-mail-signature-manager.h b/src/e-util/e-mail-signature-manager.h
index b67b3df9d7..c6d986d0ba 100644
--- a/src/e-util/e-mail-signature-manager.h
+++ b/src/e-util/e-mail-signature-manager.h
@@ -24,6 +24,7 @@
 
 #include <gtk/gtk.h>
 
+#include <e-util/e-util-enumtypes.h>
 #include <e-util/e-mail-signature-editor.h>
 #include <e-util/e-mail-signature-tree-view.h>
 
@@ -80,11 +81,12 @@ void                e_mail_signature_manager_edit_signature
                                        (EMailSignatureManager *manager);
 void           e_mail_signature_manager_remove_signature
                                        (EMailSignatureManager *manager);
-gboolean       e_mail_signature_manager_get_prefer_html
+EContentEditorMode
+               e_mail_signature_manager_get_prefer_mode
                                        (EMailSignatureManager *manager);
-void           e_mail_signature_manager_set_prefer_html
+void           e_mail_signature_manager_set_prefer_mode
                                        (EMailSignatureManager *manager,
-                                        gboolean prefer_html);
+                                        EContentEditorMode prefer_mode);
 ESourceRegistry *
                e_mail_signature_manager_get_registry
                                        (EMailSignatureManager *manager);
diff --git a/src/e-util/e-markdown-editor.c b/src/e-util/e-markdown-editor.c
index 08d4db5bee..1c9950b839 100644
--- a/src/e-util/e-markdown-editor.c
+++ b/src/e-util/e-markdown-editor.c
@@ -6,36 +6,1130 @@
 
 #include "evolution-config.h"
 
-#ifdef HAVE_MARKDOWN
-#include <cmark.h>
-#endif
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserverui/libedataserverui.h>
+
+#include "e-content-editor.h"
+#include "e-markdown-utils.h"
+#include "e-misc-utils.h"
+#include "e-spell-text-view.h"
+#include "e-web-view.h"
+#include "e-widget-undo.h"
+
+#include "e-markdown-editor.h"
+
+/* Where an existing signature is */
+#define EVO_SIGNATURE_START_MARK "x-evo-signature-start"
+#define EVO_SIGNATURE_END_MARK "x-evo-signature-end"
+
+struct _EMarkdownEditorPrivate {
+       GtkNotebook *notebook;
+       GtkTextView *text_view;
+       EWebView *web_view;
+       GtkToolbar *action_toolbar;
+       gboolean is_dark_theme;
+
+       /* EContentEditor properties */
+       gboolean can_copy;
+       gboolean can_cut;
+       gboolean can_paste;
+       gboolean can_redo;
+       gboolean can_undo;
+       gboolean changed;
+       EContentEditorMode mode;
+       ESpellChecker *spell_checker; /* this is not used internally */
+       EThreeState start_bottom;
+       EThreeState top_signature;
+       gchar *signature_uid;
+       gboolean selection_saved;
+       GtkTextIter selection_start; /* valid only if selection_saved is TRUE */
+       GtkTextIter selection_end; /* valid only if selection_saved is TRUE */
+};
+
+static void e_markdown_editor_content_editor_init (EContentEditorInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EMarkdownEditor, e_markdown_editor, GTK_TYPE_BOX,
+       G_ADD_PRIVATE (EMarkdownEditor)
+       G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)
+       G_IMPLEMENT_INTERFACE (E_TYPE_CONTENT_EDITOR, e_markdown_editor_content_editor_init))
+
+enum {
+       PROP_0,
+       PROP_IS_MALFUNCTION,
+       PROP_CAN_COPY,
+       PROP_CAN_CUT,
+       PROP_CAN_PASTE,
+       PROP_CAN_REDO,
+       PROP_CAN_UNDO,
+       PROP_CHANGED,
+       PROP_EDITABLE,
+       PROP_MODE,
+       PROP_SPELL_CHECK_ENABLED,
+       PROP_SPELL_CHECKER,
+       PROP_START_BOTTOM,
+       PROP_TOP_SIGNATURE,
+       PROP_VISUALLY_WRAP_LONG_LINES,
+       PROP_LAST_ERROR,
+
+       PROP_ALIGNMENT,
+       PROP_BACKGROUND_COLOR,
+       PROP_BLOCK_FORMAT,
+       PROP_BOLD,
+       PROP_FONT_COLOR,
+       PROP_FONT_NAME,
+       PROP_FONT_SIZE,
+       PROP_INDENT_LEVEL,
+       PROP_ITALIC,
+       PROP_STRIKETHROUGH,
+       PROP_SUBSCRIPT,
+       PROP_SUPERSCRIPT,
+       PROP_UNDERLINE
+};
+
+enum {
+       CHANGED,
+       FORMAT_BOLD,
+       FORMAT_ITALIC,
+       FORMAT_QUOTE,
+       FORMAT_CODE,
+       FORMAT_BULLET_LIST,
+       FORMAT_NUMBERED_LIST,
+       FORMAT_HEADER,
+       INSERT_LINK,
+       INSERT_EMOJI,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+typedef void (* AsyncCallback) (EMarkdownEditor *self,
+                               gpointer user_data);
+
+typedef struct _AsyncData {
+       EMarkdownEditor *self;
+       AsyncCallback callback;
+       gpointer user_data;
+} AsyncData;
+
+static AsyncData *
+async_data_new (EMarkdownEditor *self,
+               AsyncCallback callback,
+               gpointer user_data)
+{
+       AsyncData *data;
+
+       data = g_slice_new (AsyncData);
+       data->self = g_object_ref (self);
+       data->callback = callback;
+       data->user_data = user_data;
+
+       return data;
+}
+
+static void
+async_data_free (gpointer ptr)
+{
+       AsyncData *data = ptr;
+
+       if (data) {
+               g_clear_object (&data->self);
+               g_slice_free (AsyncData, data);
+       }
+}
+
+static gboolean
+e_markdown_editor_call_async_cb (gpointer user_data)
+{
+       AsyncData *data = user_data;
+
+       g_return_val_if_fail (data != NULL, FALSE);
+       g_return_val_if_fail (data->callback != NULL, FALSE);
+
+       data->callback (data->self, data->user_data);
+
+       return FALSE;
+}
+
+static void
+e_markdown_editor_call_async (EMarkdownEditor *self,
+                             AsyncCallback callback,
+                             gpointer user_data)
+{
+       g_timeout_add_full (G_PRIORITY_HIGH, 1,
+               e_markdown_editor_call_async_cb,
+               async_data_new (self, callback, user_data),
+               async_data_free);
+}
+
+static gboolean
+e_markdown_editor_supports_mode (EContentEditor *cnt_editor,
+                                EContentEditorMode mode)
+{
+       return mode == E_CONTENT_EDITOR_MODE_MARKDOWN ||
+       #ifdef HAVE_MARKDOWN
+               mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML ||
+       #endif
+               mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+}
+
+static void
+e_markdown_editor_grab_focus (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       gtk_widget_grab_focus (GTK_WIDGET (self->priv->text_view));
+}
+
+static gboolean
+e_markdown_editor_is_focus (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       return gtk_widget_is_focus (GTK_WIDGET (self->priv->text_view));
+}
+
+typedef struct _InitAsyncData {
+       EContentEditorInitializedCallback callback;
+       gpointer user_data;
+} InitAsyncData;
+
+static void
+e_markdown_editor_initialize_done (EMarkdownEditor *self,
+                                  gpointer user_data)
+{
+       InitAsyncData *data = user_data;
+
+       g_return_if_fail (data != NULL);
+       g_return_if_fail (data->callback != NULL);
+
+       data->callback (E_CONTENT_EDITOR (self), data->user_data);
+
+       g_slice_free (InitAsyncData, data);
+}
+
+static void
+e_markdown_editor_initialize (EContentEditor *cnt_editor,
+                             EContentEditorInitializedCallback callback,
+                             gpointer user_data)
+{
+       InitAsyncData *data;
+
+       data = g_slice_new (InitAsyncData);
+       data->callback = callback;
+       data->user_data = user_data;
+
+       e_markdown_editor_call_async (E_MARKDOWN_EDITOR (cnt_editor), e_markdown_editor_initialize_done, 
data);
+}
+
+static gboolean
+e_markdown_editor_is_ready (EContentEditor *cnt_editor)
+{
+       return TRUE;
+}
+
+static void
+e_markdown_editor_update_styles (EContentEditor *cnt_editor)
+{
+}
+
+static void
+e_markdown_editor_insert_content (EContentEditor *cnt_editor,
+                                 const gchar *content,
+                                 EContentEditorInsertContentFlags flags)
+{
+       EMarkdownEditor *self;
+       gchar *text = NULL;
+
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (cnt_editor));
+       g_return_if_fail (content != NULL);
+
+       self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       if ((flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0) {
+               text = e_markdown_utils_html_to_text (content, -1, 
E_MARKDOWN_HTML_TO_TEXT_FLAG_COMPOSER_QUIRKS);
+               content = text;
+       }
+
+       if ((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) != 0) {
+               e_markdown_editor_set_text (self, content);
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT) != 0) {
+               GtkTextBuffer *text_buffer;
+               GString *quoted;
+               gint ii;
+
+               quoted = g_string_sized_new (strlen (content) + 4);
+               g_string_append (quoted, "> ");
+               g_string_append (quoted, content);
+
+               for (ii = 0; ii < quoted->len; ii++) {
+                       if (quoted->str[ii] == '\n' && ii + 1 < quoted->len)
+                               g_string_insert (quoted, ii + 1, "> ");
+               }
+
+               text_buffer = gtk_text_view_get_buffer (self->priv->text_view);
+               gtk_text_buffer_insert_at_cursor (text_buffer, quoted->str, -1);
+
+               g_string_free (quoted, TRUE);
+       } else {
+               GtkTextBuffer *text_buffer;
+
+               text_buffer = gtk_text_view_get_buffer (self->priv->text_view);
+               gtk_text_buffer_insert_at_cursor (text_buffer, content, -1);
+       }
+
+       g_free (text);
+}
+
+static void
+e_markdown_editor_get_content (EContentEditor *cnt_editor,
+                              guint32 flags, /* bit-or of EContentEditorGetContentFlags */
+                              const gchar *inline_images_from_domain,
+                              GCancellable *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
+{
+       GTask *task;
+       EContentEditorContentHash *content_hash;
+
+       content_hash = e_content_editor_util_new_content_hash ();
+       if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_HTML) != 0 ||
+           (flags & E_CONTENT_EDITOR_GET_TO_SEND_HTML) != 0) {
+               gchar *html;
+
+               html = e_markdown_editor_dup_html (E_MARKDOWN_EDITOR (cnt_editor));
+
+               if (html) {
+                       if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_HTML) != 0 &&
+                           (flags & E_CONTENT_EDITOR_GET_TO_SEND_HTML) != 0) {
+                               e_content_editor_util_put_content_data (content_hash,
+                                       E_CONTENT_EDITOR_GET_RAW_BODY_HTML, html);
+                               e_content_editor_util_take_content_data (content_hash,
+                                       E_CONTENT_EDITOR_GET_TO_SEND_HTML, html, g_free);
+                       } else if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_HTML) != 0) {
+                               e_content_editor_util_take_content_data (content_hash,
+                                       E_CONTENT_EDITOR_GET_RAW_BODY_HTML, html, g_free);
+                       } else {
+                               e_content_editor_util_take_content_data (content_hash,
+                                       E_CONTENT_EDITOR_GET_TO_SEND_HTML, html, g_free);
+                       }
+               }
+       }
+
+       if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN) != 0 ||
+           (flags & E_CONTENT_EDITOR_GET_RAW_DRAFT) != 0 ||
+           (flags & E_CONTENT_EDITOR_GET_TO_SEND_PLAIN) != 0) {
+               gchar *text;
+
+               text = e_markdown_editor_dup_text (E_MARKDOWN_EDITOR (cnt_editor));
+
+               if (text) {
+                       gint n_formats = ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN) != 0 ? 1 : 0) +
+                                        ((flags & E_CONTENT_EDITOR_GET_RAW_DRAFT) != 0 ? 1 : 0) +
+                                        ((flags & E_CONTENT_EDITOR_GET_TO_SEND_PLAIN) != 0 ? 1 : 0);
+
+                       if (n_formats == 1) {
+                               EContentEditorGetContentFlags format;
+                               if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN) != 0)
+                                       format = E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN;
+                               else if ((flags & E_CONTENT_EDITOR_GET_RAW_DRAFT) != 0)
+                                       format = E_CONTENT_EDITOR_GET_RAW_DRAFT;
+                               else
+                                       format = E_CONTENT_EDITOR_GET_TO_SEND_PLAIN;
+
+                               e_content_editor_util_take_content_data (content_hash,
+                                       format, text, g_free);
+                       } else {
+                               if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN) != 0)
+                                       e_content_editor_util_put_content_data (content_hash,
+                                               E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN, text);
+
+                               if ((flags & E_CONTENT_EDITOR_GET_RAW_DRAFT) != 0)
+                                       e_content_editor_util_put_content_data (content_hash,
+                                               E_CONTENT_EDITOR_GET_RAW_DRAFT, text);
+
+                               if ((flags & E_CONTENT_EDITOR_GET_TO_SEND_PLAIN) != 0)
+                                       e_content_editor_util_put_content_data (content_hash,
+                                               E_CONTENT_EDITOR_GET_TO_SEND_PLAIN, text);
+
+                               g_free (text);
+                       }
+               }
+       }
+
+       if ((flags & E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED) != 0) {
+               gchar *text;
+
+               text = e_markdown_editor_dup_text (E_MARKDOWN_EDITOR (cnt_editor));
+
+               if (text) {
+                       gchar *separator;
+
+                       separator = strstr (text, "-- \n");
+
+                       if (separator)
+                               *separator = '\0';
+               }
+
+               if (text) {
+                       e_content_editor_util_take_content_data (content_hash,
+                                       E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED, text, g_free);
+               } else {
+                       e_content_editor_util_put_content_data (content_hash,
+                                       E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED, "");
+               }
+       }
+
+       task = g_task_new (cnt_editor, cancellable, callback, user_data);
+       g_task_set_source_tag (task, e_markdown_editor_get_content);
+       g_task_return_pointer (task, content_hash, (GDestroyNotify) e_content_editor_util_free_content_hash);
+       g_object_unref (task);
+}
+
+static EContentEditorContentHash *
+e_markdown_editor_get_content_finish (EContentEditor *cnt_editor,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+       g_return_val_if_fail (g_task_is_valid (result, cnt_editor), NULL);
+
+       return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+e_markdown_editor_move_caret_on_coordinates (EContentEditor *cnt_editor,
+                                            gint x,
+                                            gint y,
+                                            gboolean cancel_if_not_collapsed)
+{
+}
+
+static void
+e_markdown_editor_cut (EContentEditor *cnt_editor)
+{
+       /* Handled by GtktextView itself */
+}
+
+static void
+e_markdown_editor_copy (EContentEditor *cnt_editor)
+{
+       /* Handled by GtktextView itself */
+}
+
+static void
+e_markdown_editor_paste (EContentEditor *cnt_editor)
+{
+       /* Handled by GtktextView itself */
+}
+
+static void
+e_markdown_editor_paste_primary (EContentEditor *cnt_editor)
+{
+       /* Handled by GtktextView itself */
+}
+
+static void
+e_markdown_editor_undo (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       e_widget_undo_do_undo (GTK_WIDGET (self->priv->text_view));
+}
+
+static void
+e_markdown_editor_redo (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       e_widget_undo_do_redo (GTK_WIDGET (self->priv->text_view));
+}
+
+static void
+e_markdown_editor_clear_undo_redo_history (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       e_widget_undo_reset (GTK_WIDGET (self->priv->text_view));
+
+       g_object_notify (G_OBJECT (self), "can-undo");
+       g_object_notify (G_OBJECT (self), "can-redo");
+}
+
+static void
+e_markdown_editor_set_spell_checking_languages (EContentEditor *cnt_editor,
+                                               const gchar **languages)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       e_spell_text_view_set_languages (self->priv->text_view, languages);
+}
+
+static gchar *
+e_markdown_editor_get_caret_word (EContentEditor *cnt_editor)
+{
+       return NULL;
+}
+
+static void
+e_markdown_editor_replace_caret_word (EContentEditor *cnt_editor,
+                                     const gchar *replacement)
+{
+}
+
+static void
+e_markdown_editor_select_all (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       g_signal_emit_by_name (self->priv->text_view, "select-all", 0, TRUE, NULL);
+}
+
+static gunichar *
+e_markdown_editor_prepare_search_text (const gchar *text,
+                                      guint32 *flags)
+{
+       gunichar *search_text;
+       guint ii;
+
+       if (!text || !*text)
+               return NULL;
+
+       /* Fine-tune the direction flags:
+          forward & next = forward
+          forward & previous = backward
+          backward & next = backward
+          backward & previous = forward
+       */
+       if ((*flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) == 0 &&
+           (*flags & E_CONTENT_EDITOR_FIND_PREVIOUS) != 0) {
+               *flags = ((*flags) & ~(E_CONTENT_EDITOR_FIND_MODE_BACKWARDS | E_CONTENT_EDITOR_FIND_PREVIOUS 
| E_CONTENT_EDITOR_FIND_NEXT)) |
+                       E_CONTENT_EDITOR_FIND_MODE_BACKWARDS;
+       } else if ((*flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0 &&
+                  (*flags & E_CONTENT_EDITOR_FIND_PREVIOUS) != 0) {
+               *flags = ((*flags) & ~(E_CONTENT_EDITOR_FIND_MODE_BACKWARDS | E_CONTENT_EDITOR_FIND_PREVIOUS 
| E_CONTENT_EDITOR_FIND_NEXT));
+       }
+
+       search_text = g_utf8_to_ucs4 (text, -1, NULL, NULL, NULL);
+
+       if (search_text && (*flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0) {
+               guint len;
+
+               for (len = 0; search_text[len]; len++) {
+                       /* Just count them */
+               }
+
+               if (len) {
+                       len--;
+
+                       /* Swap the letters backwards */
+                       for (ii = 0; ii < len; ii++) {
+                               gunichar chr = search_text[ii];
+                               search_text[ii] = search_text[len];
+                               search_text[len] = chr;
+                               len--;
+                       }
+               }
+       }
+
+       if (search_text && (*flags & E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE) != 0) {
+               for (ii = 0; search_text[ii]; ii++) {
+                       search_text[ii] = g_unichar_tolower (search_text[ii]);
+               }
+       }
+
+       return search_text;
+}
+
+static gboolean
+e_markdown_editor_do_search_text (GtkTextBuffer *buffer,
+                                 const gunichar *search_text,
+                                 guint32 flags,
+                                 gboolean *did_wrap_around,
+                                 const GtkTextIter *from_iter,
+                                 const GtkTextIter *limit, /* used only after wrap around */
+                                 GtkTextIter *out_occur_start,
+                                 GtkTextIter *out_occur_end)
+{
+       GtkTextIter iter, from_cursor;
+       gboolean case_insensitive = (flags & E_CONTENT_EDITOR_FIND_CASE_INSENSITIVE) != 0;
+       gboolean wrap_around = (!*did_wrap_around) && (flags & E_CONTENT_EDITOR_FIND_WRAP_AROUND) != 0;
+       gboolean backwards = (flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0;
+       gboolean found = FALSE;
+
+       if (from_iter) {
+               iter = *from_iter;
+       } else {
+               gtk_text_buffer_get_selection_bounds (buffer, &from_cursor, &iter);
+
+               if (!backwards)
+                       from_cursor = iter;
+
+               if (!limit)
+                       limit = &from_cursor;
+
+               iter = from_cursor;
+       }
+
+       if (backwards && !gtk_text_iter_backward_char (&iter)) {
+               if (wrap_around) {
+                       wrap_around = FALSE;
+                       gtk_text_buffer_get_end_iter (buffer, &iter);
+                       if (!gtk_text_iter_backward_char (&iter))
+                               return FALSE;
+               } else {
+                       return FALSE;
+               }
+       }
+
+       while (!found) {
+               gunichar chr;
+
+               chr = gtk_text_iter_get_char (&iter);
+
+               if (chr) {
+                       if ((case_insensitive && g_unichar_tolower (chr) == search_text[0]) ||
+                           (!case_insensitive && chr == search_text[0])) {
+                               GtkTextIter next = iter;
+                               guint ii;
+
+                               for (ii = 1; !found; ii++) {
+                                       if (!search_text[ii]) {
+                                               /* To have selected also the last character */
+                                               if (backwards)
+                                                       gtk_text_iter_forward_char (&iter);
+                                               else
+                                                       gtk_text_iter_forward_char (&next);
+
+                                               found = TRUE;
+                                               if (backwards) {
+                                                       *out_occur_start = next;
+                                                       *out_occur_end = iter;
+                                               } else {
+                                                       *out_occur_start = iter;
+                                                       *out_occur_end = next;
+                                               }
+                                               break;
+                                       }
+
+                                       if ((backwards && !gtk_text_iter_backward_char (&next)) ||
+                                           (!backwards && !gtk_text_iter_forward_char (&next)))
+                                               break;
+
+                                       if (*did_wrap_around && !gtk_text_iter_compare (&iter, limit))
+                                               break;
+
+                                       chr = gtk_text_iter_get_char (&next);
+
+                                       if (!chr)
+                                               break;
+
+                                       if ((case_insensitive && g_unichar_tolower (chr) == search_text[ii]) 
||
+                                           (!case_insensitive && chr == search_text[ii])) {
+                                               /* matched the next letter */
+                                       } else {
+                                               break;
+                                       }
+                               }
+
+                               if (found)
+                                       break;
+                       }
+
+                       if (!found && *did_wrap_around && !gtk_text_iter_compare (&iter, limit))
+                               break;
+               }
+
+               if ((backwards && !gtk_text_iter_backward_char (&iter)) ||
+                   (!backwards && !gtk_text_iter_forward_char (&iter))) {
+                       if (!wrap_around)
+                               break;
+
+                       *did_wrap_around = TRUE;
+                       wrap_around = FALSE;
+
+                       if (backwards)
+                               gtk_text_buffer_get_end_iter (buffer, &iter);
+                       else
+                               gtk_text_buffer_get_start_iter (buffer, &iter);
+
+                       if (!gtk_text_iter_compare (&iter, limit))
+                               break;
+               }
+       }
+
+       return found;
+}
+
+static void
+e_markdown_editor_find (EContentEditor *cnt_editor,
+                       guint32 flags,
+                       const gchar *text)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+       GtkTextBuffer *buffer;
+       GtkTextIter occur_start, occur_end;
+       gboolean did_wrap_around = FALSE;
+       gunichar *search_text;
+
+       search_text = e_markdown_editor_prepare_search_text (text, &flags);
+
+       if (!search_text) {
+               e_content_editor_emit_find_done (cnt_editor, 0);
+               return;
+       }
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+
+       if (e_markdown_editor_do_search_text (buffer, search_text, flags, &did_wrap_around, NULL, NULL, 
&occur_start, &occur_end)) {
+               gtk_text_buffer_select_range (buffer, &occur_start, &occur_end);
+               e_content_editor_emit_find_done (cnt_editor, 1);
+       } else {
+               e_content_editor_emit_find_done (cnt_editor, 0);
+       }
+
+       g_free (search_text);
+}
+
+static void
+e_markdown_editor_replace (EContentEditor *cnt_editor,
+                          const gchar *replacement)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+       gtk_text_buffer_delete (buffer, &start, &end);
+       gtk_text_buffer_insert_at_cursor (buffer, replacement, -1);
+}
+
+static void
+e_markdown_editor_replace_all (EContentEditor *cnt_editor,
+                              guint32 flags,
+                              const gchar *find_text,
+                              const gchar *replace_with)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+       GtkTextBuffer *buffer;
+       GtkTextIter occur_start, occur_end, limit, last_replace;
+       gboolean did_wrap_around = FALSE;
+       gunichar *search_text;
+       guint count = 0, replace_len = 0;
+
+       search_text = e_markdown_editor_prepare_search_text (find_text, &flags);
+
+       if (!search_text) {
+               e_content_editor_emit_replace_all_done (cnt_editor, 0);
+               return;
+       }
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       gtk_text_buffer_get_selection_bounds (buffer, &occur_start, &occur_end);
+
+       /* Different bound than in search, to replace also current match */
+       if ((flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0)
+               limit = occur_end;
+       else
+               limit = occur_start;
+
+       if (replace_with)
+               replace_len = g_utf8_strlen (replace_with, -1);
+
+       last_replace = limit;
+
+       while (e_markdown_editor_do_search_text (buffer, search_text, flags, &did_wrap_around, &last_replace, 
&limit, &occur_start, &occur_end)) {
+               GtkTextMark *mark;
+               gboolean last_match;
+
+               last_match = did_wrap_around && !gtk_text_iter_compare (&occur_start, &limit);
+
+               if (last_match && !(flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS))
+                       break;
+
+               /* Remember where the limit was... */
+               mark = gtk_text_buffer_create_mark (buffer, NULL, &limit, !(flags & 
E_CONTENT_EDITOR_FIND_MODE_BACKWARDS));
+
+               gtk_text_buffer_delete (buffer, &occur_start, &occur_end);
+
+               last_replace = occur_start;
+
+               if (replace_with && *replace_with) {
+                       gtk_text_buffer_insert (buffer, &occur_start, replace_with, -1);
+
+                       /* Get on the first letter of the replaced word */
+                       if ((flags & E_CONTENT_EDITOR_FIND_MODE_BACKWARDS) != 0 &&
+                           !gtk_text_iter_backward_chars (&occur_start, replace_len)) {
+                               break;
+                       }
+
+                       last_replace = occur_start;
+               }
+
+               /* ... then restore the limit, after the buffer changed (which invalidated the iterator) */
+               gtk_text_buffer_get_iter_at_mark (buffer, &limit, mark);
+               gtk_text_buffer_delete_mark (buffer, mark);
+
+               count++;
+
+               if (last_match)
+                       break;
+       }
+
+       g_free (search_text);
+
+       if (count)
+               gtk_text_buffer_select_range (buffer, &last_replace, &last_replace);
+
+       e_content_editor_emit_replace_all_done (cnt_editor, count);
+}
+
+static void
+e_markdown_editor_selection_save (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+       GtkTextBuffer *buffer;
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       gtk_text_buffer_get_selection_bounds (buffer, &self->priv->selection_start, 
&self->priv->selection_end);
+
+       self->priv->selection_saved = TRUE;
+}
+
+static void
+e_markdown_editor_selection_restore (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       if (self->priv->selection_saved) {
+               GtkTextBuffer *buffer;
+
+               self->priv->selection_saved = FALSE;
+
+               buffer = gtk_text_view_get_buffer (self->priv->text_view);
+               gtk_text_buffer_select_range (buffer, &self->priv->selection_start, 
&self->priv->selection_end);
+       }
+}
+
+static gchar *
+e_markdown_editor_get_current_signature_uid (EContentEditor *cnt_editor)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+
+       return self->priv->signature_uid;
+}
+
+static gchar *
+e_markdown_editor_insert_signature (EContentEditor *cnt_editor,
+                                   const gchar *content,
+                                   EContentEditorMode editor_mode,
+                                   gboolean can_reposition_caret,
+                                   const gchar *signature_id,
+                                   gboolean *set_signature_from_message,
+                                   gboolean *check_if_signature_is_changed,
+                                   gboolean *ignore_next_signature_change)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+       GtkTextMark *sig_start_mark, *sig_end_mark;
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+       gchar *plain_text = NULL;
+
+       g_clear_pointer (&self->priv->signature_uid, g_free);
+       self->priv->signature_uid = g_strdup (signature_id);
+
+       if (content && *content && editor_mode == E_CONTENT_EDITOR_MODE_HTML) {
+               plain_text = e_markdown_utils_html_to_text (content, -1, 
E_MARKDOWN_HTML_TO_TEXT_FLAG_PLAIN_TEXT);
+               content = plain_text;
+               editor_mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+       }
+
+       if (content && *content && editor_mode == E_CONTENT_EDITOR_MODE_PLAIN_TEXT) {
+               gchar *tmp;
+
+               tmp = g_strconcat ("```\n", content,
+                       g_str_has_suffix (content, "\n") ? "" : "\n",
+                       "```\n", NULL);
+
+               g_free (plain_text);
+               plain_text = tmp;
+               content = plain_text;
+       }
+
+       if (!e_content_editor_util_three_state_to_bool (E_THREE_STATE_INCONSISTENT, 
"composer-no-signature-delim") &&
+           content && *content && !g_str_has_prefix (content, "-- \n") && !strstr (content, "\n-- \n")) {
+               gchar *tmp;
+
+               tmp = g_strconcat ("-- \n",
+                       /* Add an empty line between the delimiter and the markdown signature */
+                       editor_mode == E_CONTENT_EDITOR_MODE_PLAIN_TEXT ? "" : "\n",
+                       content, NULL);
+
+               g_free (plain_text);
+               plain_text = tmp;
+               content = plain_text;
+       }
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+       sig_start_mark = gtk_text_buffer_get_mark (buffer, EVO_SIGNATURE_START_MARK);
+       sig_end_mark = gtk_text_buffer_get_mark (buffer, EVO_SIGNATURE_END_MARK);
+
+       if (content && *content) {
+               gtk_text_buffer_begin_user_action (buffer);
+
+               if (sig_start_mark && sig_end_mark) {
+                       GtkTextIter sig_start, sig_end;
+
+                       gtk_text_buffer_get_iter_at_mark (buffer, &sig_start, sig_start_mark);
+                       gtk_text_buffer_get_iter_at_mark (buffer, &sig_end, sig_end_mark);
+
+                       gtk_text_buffer_delete (buffer, &sig_start, &sig_end);
+
+                       gtk_text_buffer_insert (buffer, &sig_start, content, -1);
+
+                       gtk_text_buffer_get_bounds (buffer, &start, &end);
+               } else if (e_content_editor_util_three_state_to_bool (e_content_editor_get_top_signature 
(cnt_editor), "composer-top-signature")) {
+                       if (!g_str_has_suffix (content, "\n\n")) {
+                               if (g_str_has_suffix (content, "\n"))
+                                       gtk_text_buffer_insert (buffer, &start, "\n", 1);
+                               else
+                                       gtk_text_buffer_insert (buffer, &start, "\n\n", 2);
+
+                               gtk_text_buffer_get_start_iter (buffer, &start);
+                       }
+
+                       if (sig_start_mark)
+                               gtk_text_buffer_delete_mark_by_name (buffer, EVO_SIGNATURE_START_MARK);
+                       if (sig_end_mark)
+                               gtk_text_buffer_delete_mark_by_name (buffer, EVO_SIGNATURE_END_MARK);
+
+                       gtk_text_buffer_create_mark (buffer, EVO_SIGNATURE_END_MARK, &start, FALSE);
+
+                       gtk_text_buffer_insert (buffer, &start, content, -1);
+                       gtk_text_buffer_get_start_iter (buffer, &start);
+
+                       gtk_text_buffer_create_mark (buffer, EVO_SIGNATURE_START_MARK, &start, TRUE);
+
+                       gtk_text_buffer_insert (buffer, &start, "\n", 1);
+                       gtk_text_buffer_get_start_iter (buffer, &start);
+               } else {
+                       GtkTextIter iter = end;
+
+                       if (gtk_text_iter_backward_char (&iter) &&
+                           gtk_text_iter_get_char (&iter) != '\n') {
+                               gtk_text_buffer_insert (buffer, &end, "\n", 1);
+                               gtk_text_buffer_get_end_iter (buffer, &end);
+                       }
+
+                       if (sig_start_mark)
+                               gtk_text_buffer_delete_mark_by_name (buffer, EVO_SIGNATURE_START_MARK);
+                       if (sig_end_mark)
+                               gtk_text_buffer_delete_mark_by_name (buffer, EVO_SIGNATURE_END_MARK);
+
+                       gtk_text_buffer_create_mark (buffer, EVO_SIGNATURE_START_MARK, &end, TRUE);
+
+                       gtk_text_buffer_insert (buffer, &end, content, -1);
+                       gtk_text_buffer_get_end_iter (buffer, &end);
+
+                       gtk_text_buffer_create_mark (buffer, EVO_SIGNATURE_END_MARK, &end, FALSE);
+               }
+
+               gtk_text_buffer_end_user_action (buffer);
+       } else if (sig_start_mark && sig_end_mark) {
+               GtkTextIter sig_start, sig_end;
+
+               gtk_text_buffer_begin_user_action (buffer);
+               gtk_text_buffer_get_iter_at_mark (buffer, &sig_start, sig_start_mark);
+               gtk_text_buffer_get_iter_at_mark (buffer, &sig_end, sig_end_mark);
+               gtk_text_buffer_delete (buffer, &sig_start, &sig_end);
+               gtk_text_buffer_get_bounds (buffer, &start, &end);
+               gtk_text_buffer_end_user_action (buffer);
+       } else {
+               if (sig_start_mark)
+                       gtk_text_buffer_delete_mark_by_name (buffer, EVO_SIGNATURE_START_MARK);
+               if (sig_end_mark)
+                       gtk_text_buffer_delete_mark_by_name (buffer, EVO_SIGNATURE_END_MARK);
+       }
+
+       if (can_reposition_caret) {
+               if (e_content_editor_util_three_state_to_bool (e_content_editor_get_start_bottom 
(cnt_editor), "composer-reply-start-bottom")) {
+                       gtk_text_buffer_select_range (buffer, &end, &end);
+               } else {
+                       gtk_text_buffer_get_start_iter (buffer, &start);
+                       gtk_text_buffer_select_range (buffer, &start, &start);
+               }
+       }
+
+       g_free (plain_text);
+
+       return g_strdup (self->priv->signature_uid);
+}
+
+static void
+e_markdown_editor_on_dialog_open (EContentEditor *cnt_editor,
+                                 const gchar *name)
+{
+       if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_REPLACE) == 0) {
+               EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+               GtkTextBuffer *buffer;
+
+               buffer = gtk_text_view_get_buffer (self->priv->text_view);
+               gtk_text_buffer_begin_user_action (buffer);
+       }
+}
+
+static void
+e_markdown_editor_on_dialog_close (EContentEditor *cnt_editor,
+                                  const gchar *name)
+{
+       if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_REPLACE) == 0) {
+               EMarkdownEditor *self = E_MARKDOWN_EDITOR (cnt_editor);
+               GtkTextBuffer *buffer;
+
+               buffer = gtk_text_view_get_buffer (self->priv->text_view);
+               gtk_text_buffer_end_user_action (buffer);
+       }
+}
+
+static gboolean
+e_markdown_editor_can_copy (EMarkdownEditor *self)
+{
+       return self->priv->can_copy;
+}
+
+static gboolean
+e_markdown_editor_can_cut (EMarkdownEditor *self)
+{
+       return self->priv->can_cut;
+}
+
+static gboolean
+e_markdown_editor_can_paste (EMarkdownEditor *self)
+{
+       return self->priv->can_paste;
+}
+
+static gboolean
+e_markdown_editor_can_redo (EMarkdownEditor *self)
+{
+       return e_widget_undo_has_redo (GTK_WIDGET (self->priv->text_view));
+}
+
+static gboolean
+e_markdown_editor_can_undo (EMarkdownEditor *self)
+{
+       return e_widget_undo_has_undo (GTK_WIDGET (self->priv->text_view));
+}
+
+static gboolean
+e_markdown_editor_get_changed (EMarkdownEditor *self)
+{
+       return self->priv->changed;
+}
 
-#include <glib/gi18n-lib.h>
-#include <gtk/gtk.h>
+static void
+e_markdown_editor_set_changed (EMarkdownEditor *self,
+                              gboolean value)
+{
+       if ((self->priv->changed ? 1 : 0) != (value ? 1 : 0)) {
+               self->priv->changed = value;
+               g_object_notify (G_OBJECT (self), "changed");
+       }
+}
 
-#include <libedataserverui/libedataserverui.h>
+static gboolean
+e_markdown_editor_is_editable (EMarkdownEditor *self)
+{
+       return gtk_text_view_get_editable (self->priv->text_view);
+}
 
-#include "e-misc-utils.h"
-#include "e-spell-text-view.h"
-#include "e-web-view.h"
+static void
+e_markdown_editor_set_editable (EMarkdownEditor *self,
+                               gboolean value)
+{
+       if ((gtk_text_view_get_editable (self->priv->text_view) ? 1 : 0) != (value ? 1 : 0)) {
+               gtk_text_view_set_editable (self->priv->text_view, value);
+               g_object_notify (G_OBJECT (self), "editable");
+       }
+}
 
-#include "e-markdown-editor.h"
+static EContentEditorMode
+e_markdown_editor_get_mode (EMarkdownEditor *self)
+{
+       return self->priv->mode;
+}
 
-struct _EMarkdownEditorPrivate {
-       GtkTextView *text_view;
-       EWebView *web_view;
-       GtkToolbar *action_toolbar;
-       gboolean is_dark_theme;
-};
+static void
+e_markdown_editor_set_mode (EMarkdownEditor *self,
+                           EContentEditorMode mode)
+{
+       g_return_if_fail (mode == E_CONTENT_EDITOR_MODE_MARKDOWN ||
+               mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT ||
+               mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML);
 
-G_DEFINE_TYPE_WITH_PRIVATE (EMarkdownEditor, e_markdown_editor, GTK_TYPE_BOX)
+       if (self->priv->mode != mode) {
+               self->priv->mode = mode;
+               g_object_notify (G_OBJECT (self), "mode");
+       }
+}
 
-enum {
-       CHANGED,
-       LAST_SIGNAL
-};
+static gboolean
+e_markdown_editor_get_spell_check_enabled (EMarkdownEditor *self)
+{
+       return e_spell_text_view_get_enabled (self->priv->text_view);
+}
 
-static guint signals[LAST_SIGNAL];
+static void
+e_markdown_editor_set_spell_check_enabled (EMarkdownEditor *self,
+                                          gboolean value)
+{
+       gboolean spell_check_enabled = e_markdown_editor_get_spell_check_enabled (self);
+       if ((spell_check_enabled ? 1 : 0) != (value ? 1 : 0)) {
+               e_spell_text_view_set_enabled (self->priv->text_view, value);
+               g_object_notify (G_OBJECT (self), "spell-check-enabled");
+       }
+}
+
+static ESpellChecker *
+e_markdown_editor_get_spell_checker (EMarkdownEditor *self)
+{
+       return self->priv->spell_checker;
+}
+
+static EThreeState
+e_markdown_editor_get_start_bottom (EMarkdownEditor *self)
+{
+       return self->priv->start_bottom;
+}
+
+static void
+e_markdown_editor_set_start_bottom (EMarkdownEditor *self,
+                                   EThreeState value)
+{
+       if (self->priv->start_bottom != value) {
+               self->priv->start_bottom = value;
+               g_object_notify (G_OBJECT (self), "start-bottom");
+       }
+}
+
+static EThreeState
+e_markdown_editor_get_top_signature (EMarkdownEditor *self)
+{
+       return self->priv->top_signature;
+}
+
+static void
+e_markdown_editor_set_top_signature (EMarkdownEditor *self,
+                                    EThreeState value)
+{
+       if (self->priv->top_signature != value) {
+               self->priv->top_signature = value;
+               g_object_notify (G_OBJECT (self), "top-signature");
+       }
+}
 
 static void
 e_markdown_editor_get_selection (EMarkdownEditor *self,
@@ -116,43 +1210,32 @@ e_markdown_editor_surround_selection (EMarkdownEditor *self,
 }
 
 static void
-e_markdown_editor_add_bold_text_cb (GtkToolButton *button,
-                                   gpointer user_data)
+e_markdown_editor_format_bold_text_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
-
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
        e_markdown_editor_surround_selection (self, FALSE, "**", "**");
 }
 
 static void
-e_markdown_editor_add_italic_text_cb (GtkToolButton *button,
-                                     gpointer user_data)
+e_markdown_editor_format_italic_text_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
-
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
-       e_markdown_editor_surround_selection (self, FALSE, "_", "_");
+       e_markdown_editor_surround_selection (self, FALSE, "*", "*");
 }
 
 static void
-e_markdown_editor_insert_quote_cb (GtkToolButton *button,
-                                  gpointer user_data)
+e_markdown_editor_format_quote_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
-
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
        e_markdown_editor_surround_selection (self, TRUE, "> ", NULL);
 }
 
 static void
-e_markdown_editor_insert_code_cb (GtkToolButton *button,
-                                 gpointer user_data)
+e_markdown_editor_format_code_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
        GtkTextIter start, end;
        gchar *selection = NULL;
 
@@ -199,10 +1282,8 @@ e_markdown_editor_insert_code_cb (GtkToolButton *button,
 }
 
 static void
-e_markdown_editor_add_link_cb (GtkToolButton *button,
-                              gpointer user_data)
+e_markdown_editor_insert_link_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
        GtkTextBuffer *buffer;
        GtkTextIter start, end;
        gchar *selection = NULL;
@@ -254,43 +1335,40 @@ e_markdown_editor_add_link_cb (GtkToolButton *button,
 }
 
 static void
-e_markdown_editor_add_bullet_list_cb (GtkToolButton *button,
-                                     gpointer user_data)
+e_markdown_editor_format_bullet_list_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
-
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
        e_markdown_editor_surround_selection (self, TRUE, "- ", NULL);
 }
 
 static void
-e_markdown_editor_add_numbered_list_cb (GtkToolButton *button,
-                                       gpointer user_data)
+e_markdown_editor_format_numbered_list_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
-
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
        e_markdown_editor_surround_selection (self, TRUE, "1. ", NULL);
 }
 
 static void
-e_markdown_editor_add_header_cb (GtkToolButton *button,
-                                gpointer user_data)
+e_markdown_editor_format_header_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
-
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
        e_markdown_editor_surround_selection (self, TRUE, "# ", NULL);
 }
 
 static void
-e_markdown_editor_markdown_syntax_cb (GtkToolButton *button,
-                                     gpointer user_data)
+e_markdown_editor_insert_emoji_cb (EMarkdownEditor *self)
+{
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+
+       g_signal_emit_by_name (self->priv->text_view, "insert-emoji", 0, NULL);
+}
+
+static void
+e_markdown_editor_markdown_syntax_cb (EMarkdownEditor *self)
 {
-       EMarkdownEditor *self = user_data;
        GtkWidget *toplevel;
 
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
@@ -375,14 +1453,15 @@ struct _toolbar_items {
 
 static struct _toolbar_items toolbar_items[] = {
        #define ITEM(lbl, icn, cbk) { lbl, icn, icn "-dark", G_CALLBACK (cbk) }
-       ITEM (N_("Add bold text"), "markdown-bold", e_markdown_editor_add_bold_text_cb),
-       ITEM (N_("Add italic text"), "markdown-italic", e_markdown_editor_add_italic_text_cb),
-       ITEM (N_("Insert a quote"), "markdown-quote", e_markdown_editor_insert_quote_cb),
-       ITEM (N_("Insert code"), "markdown-code", e_markdown_editor_insert_code_cb),
-       ITEM (N_("Add a link"), "markdown-link", e_markdown_editor_add_link_cb),
-       ITEM (N_("Add a bullet list"), "markdown-bullets", e_markdown_editor_add_bullet_list_cb),
-       ITEM (N_("Add a numbered list"), "markdown-numbers", e_markdown_editor_add_numbered_list_cb),
-       ITEM (N_("Add a header"), "markdown-header", e_markdown_editor_add_header_cb),
+       ITEM (N_("Add bold text"), "markdown-bold", e_markdown_editor_format_bold_text_cb),
+       ITEM (N_("Add italic text"), "markdown-italic", e_markdown_editor_format_italic_text_cb),
+       ITEM (N_("Insert a quote"), "markdown-quote", e_markdown_editor_format_quote_cb),
+       ITEM (N_("Insert code"), "markdown-code", e_markdown_editor_format_code_cb),
+       ITEM (N_("Add a link"), "markdown-link", e_markdown_editor_insert_link_cb),
+       ITEM (N_("Add a bullet list"), "markdown-bullets", e_markdown_editor_format_bullet_list_cb),
+       ITEM (N_("Add a numbered list"), "markdown-numbers", e_markdown_editor_format_numbered_list_cb),
+       ITEM (N_("Add a header"), "markdown-header", e_markdown_editor_format_header_cb),
+       ITEM (N_("Insert Emoji"), "markdown-emoji", e_markdown_editor_insert_emoji_cb),
        ITEM (NULL, "", NULL),
        ITEM (N_("Open online common mark documentation"), "markdown-help", G_CALLBACK 
(e_markdown_editor_markdown_syntax_cb))
        #undef ITEM
@@ -478,7 +1557,210 @@ e_markdown_editor_text_view_changed_cb (GtkTextView *text_view,
 
        g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
+       e_markdown_editor_set_changed (self, TRUE);
+
        g_signal_emit (self, signals[CHANGED], 0, NULL);
+       e_content_editor_emit_content_changed (E_CONTENT_EDITOR (self));
+}
+
+static void
+e_markdown_editor_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CHANGED:
+                       e_markdown_editor_set_changed (
+                               E_MARKDOWN_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_EDITABLE:
+                       e_markdown_editor_set_editable (
+                               E_MARKDOWN_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_MODE:
+                       e_markdown_editor_set_mode (
+                               E_MARKDOWN_EDITOR (object),
+                               g_value_get_enum (value));
+                       return;
+
+               case PROP_SPELL_CHECK_ENABLED:
+                       e_markdown_editor_set_spell_check_enabled (
+                               E_MARKDOWN_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_START_BOTTOM:
+                       e_markdown_editor_set_start_bottom (
+                               E_MARKDOWN_EDITOR (object),
+                               g_value_get_enum (value));
+                       return;
+
+               case PROP_TOP_SIGNATURE:
+                       e_markdown_editor_set_top_signature (
+                               E_MARKDOWN_EDITOR (object),
+                               g_value_get_enum (value));
+                       return;
+
+               case PROP_VISUALLY_WRAP_LONG_LINES:
+               case PROP_LAST_ERROR:
+               case PROP_ALIGNMENT:
+               case PROP_BACKGROUND_COLOR:
+               case PROP_BLOCK_FORMAT:
+               case PROP_BOLD:
+               case PROP_FONT_COLOR:
+               case PROP_FONT_NAME:
+               case PROP_FONT_SIZE:
+               case PROP_INDENT_LEVEL:
+               case PROP_ITALIC:
+               case PROP_STRIKETHROUGH:
+               case PROP_SUBSCRIPT:
+               case PROP_SUPERSCRIPT:
+               case PROP_UNDERLINE:
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_markdown_editor_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_IS_MALFUNCTION:
+                       g_value_set_boolean (value, FALSE);
+                       return;
+
+               case PROP_CAN_COPY:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_can_copy (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_CUT:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_can_cut (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_PASTE:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_can_paste (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_REDO:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_can_redo (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_CAN_UNDO:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_can_undo (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_CHANGED:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_get_changed (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_EDITABLE:
+                       g_value_set_boolean (
+                               value, e_markdown_editor_is_editable (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_MODE:
+                       g_value_set_enum (
+                               value, e_markdown_editor_get_mode (
+                               E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_SPELL_CHECK_ENABLED:
+                       g_value_set_boolean (
+                               value,
+                               e_markdown_editor_get_spell_check_enabled (
+                                       E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_SPELL_CHECKER:
+                       g_value_set_object (
+                               value,
+                               e_markdown_editor_get_spell_checker (
+                                       E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_START_BOTTOM:
+                       g_value_set_enum (
+                               value,
+                               e_markdown_editor_get_start_bottom (
+                                       E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_TOP_SIGNATURE:
+                       g_value_set_enum (
+                               value,
+                               e_markdown_editor_get_top_signature (
+                                       E_MARKDOWN_EDITOR (object)));
+                       return;
+
+               case PROP_VISUALLY_WRAP_LONG_LINES:
+                       g_value_set_boolean (value, FALSE);
+                       return;
+
+               case PROP_LAST_ERROR:
+                       g_value_set_boxed (value, NULL);
+                       return;
+
+               case PROP_ALIGNMENT:
+                       g_value_set_enum (value, E_CONTENT_EDITOR_ALIGNMENT_LEFT);
+                       return;
+
+               case PROP_BACKGROUND_COLOR:
+                       g_value_set_boxed (value, NULL);
+                       return;
+
+               case PROP_BLOCK_FORMAT:
+                       g_value_set_enum (value, E_CONTENT_EDITOR_BLOCK_FORMAT_PRE);
+                       return;
+
+               case PROP_FONT_COLOR:
+                       g_value_set_boxed (value, NULL);
+                       return;
+
+               case PROP_FONT_NAME:
+                       g_value_set_string (value, NULL);
+                       return;
+
+               case PROP_FONT_SIZE:
+                       g_value_set_int (value, E_CONTENT_EDITOR_FONT_SIZE_NORMAL);
+                       return;
+
+               case PROP_INDENT_LEVEL:
+                       g_value_set_int (value, 0);
+                       return;
+
+               case PROP_BOLD:
+               case PROP_ITALIC:
+               case PROP_STRIKETHROUGH:
+               case PROP_SUBSCRIPT:
+               case PROP_SUPERSCRIPT:
+               case PROP_UNDERLINE:
+                       g_value_set_boolean (value, FALSE);
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
@@ -486,7 +1768,6 @@ e_markdown_editor_constructed (GObject *object)
 {
        EMarkdownEditor *self = E_MARKDOWN_EDITOR (object);
        GtkWidget *widget;
-       GtkNotebook *notebook;
        GtkScrolledWindow *scrolled_window;
        guint ii;
 
@@ -505,7 +1786,7 @@ e_markdown_editor_constructed (GObject *object)
                NULL);
        gtk_box_pack_start (GTK_BOX (self), widget, TRUE, TRUE, 0);
 
-       notebook = GTK_NOTEBOOK (widget);
+       self->priv->notebook = GTK_NOTEBOOK (widget);
 
        widget = gtk_scrolled_window_new (NULL, NULL);
        g_object_set (G_OBJECT (widget),
@@ -518,7 +1799,7 @@ e_markdown_editor_constructed (GObject *object)
                "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
                NULL);
 
-       gtk_notebook_append_page (notebook, widget, gtk_label_new_with_mnemonic (_("_Write")));
+       gtk_notebook_append_page (self->priv->notebook, widget, gtk_label_new_with_mnemonic (_("_Write")));
 
        scrolled_window = GTK_SCROLLED_WINDOW (widget);
 
@@ -553,7 +1834,7 @@ e_markdown_editor_constructed (GObject *object)
                "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
                NULL);
 
-       gtk_notebook_append_page (notebook, widget, gtk_label_new_with_mnemonic (_("_Preview")));
+       gtk_notebook_append_page (self->priv->notebook, widget, gtk_label_new_with_mnemonic (_("_Preview")));
 
        scrolled_window = GTK_SCROLLED_WINDOW (widget);
 
@@ -575,7 +1856,7 @@ e_markdown_editor_constructed (GObject *object)
        widget = gtk_toolbar_new ();
        gtk_toolbar_set_icon_size (GTK_TOOLBAR (widget), GTK_ICON_SIZE_SMALL_TOOLBAR);
        gtk_widget_show (widget);
-       gtk_notebook_set_action_widget (notebook, widget, GTK_PACK_END);
+       gtk_notebook_set_action_widget (self->priv->notebook, widget, GTK_PACK_END);
 
        self->priv->action_toolbar = GTK_TOOLBAR (widget);
        self->priv->is_dark_theme = e_markdown_editor_is_dark_theme (self);
@@ -593,7 +1874,7 @@ e_markdown_editor_constructed (GObject *object)
                        item = gtk_tool_button_new (icon, _(toolbar_items[ii].label));
                        gtk_widget_set_name (GTK_WIDGET (item), toolbar_items[ii].icon_name);
                        gtk_tool_item_set_tooltip_text (item, _(toolbar_items[ii].label));
-                       g_signal_connect_object (item, "clicked", toolbar_items[ii].callback, self, 0);
+                       g_signal_connect_object (item, "clicked", toolbar_items[ii].callback, self, 
G_CONNECT_SWAPPED);
                } else {
                        item = gtk_separator_tool_item_new ();
                }
@@ -603,7 +1884,7 @@ e_markdown_editor_constructed (GObject *object)
        }
 
        #ifdef HAVE_MARKDOWN
-       g_signal_connect_object (notebook, "switch-page", G_CALLBACK (e_markdown_editor_switch_page_cb), 
self, 0);
+       g_signal_connect_object (self->priv->notebook, "switch-page", G_CALLBACK 
(e_markdown_editor_switch_page_cb), self, 0);
        #endif
 
        g_signal_connect (self, "style-updated", G_CALLBACK (e_markdown_editor_style_updated_cb), NULL);
@@ -611,13 +1892,68 @@ e_markdown_editor_constructed (GObject *object)
        e_signal_connect_notify_object (self->priv->text_view, "notify::editable", G_CALLBACK 
(e_markdown_editor_notify_editable_cb), self, 0);
 }
 
+static void
+e_markdown_editor_finalize (GObject *object)
+{
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (object);
+
+       g_clear_object (&self->priv->spell_checker);
+       g_clear_pointer (&self->priv->signature_uid, g_free);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_markdown_editor_parent_class)->finalize (object);
+}
+
 static void
 e_markdown_editor_class_init (EMarkdownEditorClass *klass)
 {
        GObjectClass *object_class;
+       GtkBindingSet *binding_set;
+
+       klass->format_bold = e_markdown_editor_format_bold_text_cb;
+       klass->format_italic = e_markdown_editor_format_italic_text_cb;
+       klass->format_quote = e_markdown_editor_format_quote_cb;
+       klass->format_code = e_markdown_editor_format_code_cb;
+       klass->format_bullet_list = e_markdown_editor_format_bullet_list_cb;
+       klass->format_numbered_list = e_markdown_editor_format_numbered_list_cb;
+       klass->format_header = e_markdown_editor_format_header_cb;
+       klass->insert_link = e_markdown_editor_insert_link_cb;
+       klass->insert_emoji = e_markdown_editor_insert_emoji_cb;
 
        object_class = G_OBJECT_CLASS (klass);
+       object_class->get_property = e_markdown_editor_get_property;
+       object_class->set_property = e_markdown_editor_set_property;
        object_class->constructed = e_markdown_editor_constructed;
+       object_class->finalize = e_markdown_editor_finalize;
+
+       g_object_class_override_property (object_class, PROP_IS_MALFUNCTION, "is-malfunction");
+       g_object_class_override_property (object_class, PROP_CAN_COPY, "can-copy");
+       g_object_class_override_property (object_class, PROP_CAN_CUT, "can-cut");
+       g_object_class_override_property (object_class, PROP_CAN_PASTE, "can-paste");
+       g_object_class_override_property (object_class, PROP_CAN_REDO, "can-redo");
+       g_object_class_override_property (object_class, PROP_CAN_UNDO, "can-undo");
+       g_object_class_override_property (object_class, PROP_CHANGED, "changed");
+       g_object_class_override_property (object_class, PROP_MODE, "mode");
+       g_object_class_override_property (object_class, PROP_EDITABLE, "editable");
+       g_object_class_override_property (object_class, PROP_ALIGNMENT, "alignment");
+       g_object_class_override_property (object_class, PROP_BACKGROUND_COLOR, "background-color");
+       g_object_class_override_property (object_class, PROP_BLOCK_FORMAT, "block-format");
+       g_object_class_override_property (object_class, PROP_BOLD, "bold");
+       g_object_class_override_property (object_class, PROP_FONT_COLOR, "font-color");
+       g_object_class_override_property (object_class, PROP_FONT_NAME, "font-name");
+       g_object_class_override_property (object_class, PROP_FONT_SIZE, "font-size");
+       g_object_class_override_property (object_class, PROP_INDENT_LEVEL, "indent-level");
+       g_object_class_override_property (object_class, PROP_ITALIC, "italic");
+       g_object_class_override_property (object_class, PROP_STRIKETHROUGH, "strikethrough");
+       g_object_class_override_property (object_class, PROP_SUBSCRIPT, "subscript");
+       g_object_class_override_property (object_class, PROP_SUPERSCRIPT, "superscript");
+       g_object_class_override_property (object_class, PROP_UNDERLINE, "underline");
+       g_object_class_override_property (object_class, PROP_START_BOTTOM, "start-bottom");
+       g_object_class_override_property (object_class, PROP_TOP_SIGNATURE, "top-signature");
+       g_object_class_override_property (object_class, PROP_SPELL_CHECK_ENABLED, "spell-check-enabled");
+       g_object_class_override_property (object_class, PROP_VISUALLY_WRAP_LONG_LINES, 
"visually-wrap-long-lines");
+       g_object_class_override_property (object_class, PROP_LAST_ERROR, "last-error");
+       g_object_class_override_property (object_class, PROP_SPELL_CHECKER, "spell-checker");
 
        /**
         * EMarkdownEditor::changed:
@@ -631,16 +1967,215 @@ e_markdown_editor_class_init (EMarkdownEditorClass *klass)
                "changed",
                G_TYPE_FROM_CLASS (klass),
                G_SIGNAL_RUN_FIRST,
-               0,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, changed),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-bold:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to bold.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_BOLD] = g_signal_new (
+               "format-bold",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_bold),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-italic:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to italic.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_ITALIC] = g_signal_new (
+               "format-italic",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_italic),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-quote:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to quote.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_QUOTE] = g_signal_new (
+               "format-quote",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_quote),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-code:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to code.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_CODE] = g_signal_new (
+               "format-code",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_code),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-bullet-list:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to bullet list.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_BULLET_LIST] = g_signal_new (
+               "format-bullet-list",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_bullet_list),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-numbered-list:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to numbered list.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_NUMBERED_LIST] = g_signal_new (
+               "format-numbered-list",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_numbered_list),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::format-header:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to set text format to header.
+        *
+        * Since: 3.44
+        **/
+       signals[FORMAT_HEADER] = g_signal_new (
+               "format-header",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, format_header),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::insert-link:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to insert a link.
+        *
+        * Since: 3.44
+        **/
+       signals[INSERT_LINK] = g_signal_new (
+               "insert-link",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, insert_link),
                NULL, NULL, NULL,
                G_TYPE_NONE, 0,
                G_TYPE_NONE);
+
+       /**
+        * EMarkdownEditor::insert-emoji:
+        * @self: an #EMarkdownEditor, which receives the signal
+        *
+        * A signal to open a dialog to insert Emoji.
+        *
+        * Since: 3.44
+        **/
+       signals[INSERT_EMOJI] = g_signal_new (
+               "insert-emoji",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EMarkdownEditorClass, insert_emoji),
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+
+       binding_set = gtk_binding_set_by_class (klass);
+
+       gtk_binding_entry_add_signal (binding_set, GDK_KEY_b, GDK_CONTROL_MASK, "format-bold", 0);
+       gtk_binding_entry_add_signal (binding_set, GDK_KEY_i, GDK_CONTROL_MASK, "format-italic", 0);
+       gtk_binding_entry_add_signal (binding_set, GDK_KEY_k, GDK_CONTROL_MASK, "insert-link", 0);
+}
+
+static void
+e_markdown_editor_content_editor_init (EContentEditorInterface *iface)
+{
+       iface->supports_mode = e_markdown_editor_supports_mode;
+       iface->grab_focus = e_markdown_editor_grab_focus;
+       iface->is_focus = e_markdown_editor_is_focus;
+       iface->initialize = e_markdown_editor_initialize;
+       iface->is_ready = e_markdown_editor_is_ready;
+       iface->update_styles = e_markdown_editor_update_styles;
+       iface->insert_content = e_markdown_editor_insert_content;
+       iface->get_content = e_markdown_editor_get_content;
+       iface->get_content_finish = e_markdown_editor_get_content_finish;
+       iface->move_caret_on_coordinates = e_markdown_editor_move_caret_on_coordinates;
+       iface->cut = e_markdown_editor_cut;
+       iface->copy = e_markdown_editor_copy;
+       iface->paste = e_markdown_editor_paste;
+       iface->paste_primary = e_markdown_editor_paste_primary;
+       iface->undo = e_markdown_editor_undo;
+       iface->redo = e_markdown_editor_redo;
+       iface->clear_undo_redo_history = e_markdown_editor_clear_undo_redo_history;
+       iface->set_spell_checking_languages = e_markdown_editor_set_spell_checking_languages;
+       iface->get_caret_word = e_markdown_editor_get_caret_word;
+       iface->replace_caret_word = e_markdown_editor_replace_caret_word;
+       iface->select_all = e_markdown_editor_select_all;
+       iface->find = e_markdown_editor_find;
+       iface->replace = e_markdown_editor_replace;
+       iface->replace_all = e_markdown_editor_replace_all;
+       iface->selection_save = e_markdown_editor_selection_save;
+       iface->selection_restore = e_markdown_editor_selection_restore;
+       iface->get_current_signature_uid = e_markdown_editor_get_current_signature_uid;
+       iface->insert_signature = e_markdown_editor_insert_signature;
+       iface->on_dialog_open = e_markdown_editor_on_dialog_open;
+       iface->on_dialog_close = e_markdown_editor_on_dialog_close;
 }
 
 static void
 e_markdown_editor_init (EMarkdownEditor *self)
 {
        self->priv = e_markdown_editor_get_instance_private (self);
+
+       self->priv->spell_checker = e_spell_checker_new ();
+       self->priv->mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+       self->priv->start_bottom = E_THREE_STATE_INCONSISTENT;
+       self->priv->top_signature = E_THREE_STATE_INCONSISTENT;
 }
 
 /**
@@ -658,6 +2193,25 @@ e_markdown_editor_new (void)
        return g_object_new (E_TYPE_MARKDOWN_EDITOR, NULL);
 }
 
+/**
+ * e_markdown_editor_connect_focus_tracker:
+ * @self: an #EMarkdownEditor
+ * @focus_tracker: an #EFocusTracker
+ *
+ * Connects @self widgets to the @focus_tracker.
+ *
+ * Since: 3.44
+ **/
+void
+e_markdown_editor_connect_focus_tracker (EMarkdownEditor *self,
+                                        EFocusTracker *focus_tracker)
+{
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+       g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+       e_widget_undo_attach (GTK_WIDGET (self->priv->text_view), focus_tracker);
+}
+
 /**
  * e_markdown_editor_get_text_view:
  * @self: an #EMarkdownEditor
@@ -691,6 +2245,28 @@ e_markdown_editor_get_action_toolbar (EMarkdownEditor *self)
        return self->priv->action_toolbar;
 }
 
+/**
+ * e_markdown_editor_set_text:
+ * @self an #EMarkdownEditor
+ * @text: text to set
+ *
+ * Sets the @text as the editor content.
+ *
+ * Since: 3.44
+ **/
+void
+e_markdown_editor_set_text (EMarkdownEditor *self,
+                           const gchar *text)
+{
+       GtkTextBuffer *buffer;
+
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+       g_return_if_fail (text != NULL);
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       gtk_text_buffer_set_text (buffer, text, -1);
+}
+
 /**
  * e_markdown_editor_dup_text:
  * @self: an #EMarkdownEditor
@@ -744,7 +2320,7 @@ e_markdown_editor_dup_html (EMarkdownEditor *self)
 
        #ifdef HAVE_MARKDOWN
        text = e_markdown_editor_dup_text (self);
-       html = e_markdown_util_text_to_html (text, -1);
+       html = e_markdown_utils_text_to_html (text, -1);
 
        g_free (text);
 
@@ -755,41 +2331,44 @@ e_markdown_editor_dup_html (EMarkdownEditor *self)
 }
 
 /**
- * e_markdown_util_text_to_html:
- * @plain_text: plain text with markdown to convert to HTML
- * @length: length of the @plain_text, or -1 when it's nul-terminated
- *
- * Convert @plain_text, possibly with markdown, into the HTML.
- *
- * Note: The function can return %NULL when was not built
- *    with the markdown support.
+ * e_markdown_editor_get_preview_mode:
+ * @self: an #EMarkdownEditor
  *
- * Returns: (transfer full) (nullable): text converted into HTML,
- *    or %NULL, when was not built with the markdown support.
- *    Free the string with g_free(), when no longer needed.
+ * Returns: whether the @self is in the preview mode; %FALSE means
+ *    it is in the editing mode
  *
  * Since: 3.44
  **/
-gchar *
-e_markdown_util_text_to_html (const gchar *plain_text,
-                             gssize length)
+gboolean
+e_markdown_editor_get_preview_mode (EMarkdownEditor *self)
 {
-       #ifdef HAVE_MARKDOWN
-       GString *html;
-       gchar *converted;
-
-       if (length == -1)
-               length = plain_text ? strlen (plain_text) : 0;
+       g_return_val_if_fail (E_IS_MARKDOWN_EDITOR (self), FALSE);
 
-       converted = cmark_markdown_to_html (plain_text ? plain_text : "", length,
-               CMARK_OPT_VALIDATE_UTF8 | CMARK_OPT_UNSAFE);
-
-       html = e_str_replace_string (converted, "<blockquote>", "<blockquote type=\"cite\">");
+       return gtk_notebook_get_current_page (self->priv->notebook) == 1;
+}
 
-       g_free (converted);
+/**
+ * e_markdown_editor_set_preview_mode:
+ * @self: an #EMarkdownEditor
+ * @preview_mode: %TRUE to set the preview mode, %FALSE otherwise
+ *
+ * Sets the @self into the preview mode, when @preview_mode is %TRUE, or
+ * into editing mode, when @preview_mode is %FALSE.
+ *
+ * Note: The request to move to the preview mode can be silently ignored
+ *    when the Evolution was not built with the markdown support.
+ *
+ * Since: 3.44
+ **/
+void
+e_markdown_editor_set_preview_mode (EMarkdownEditor *self,
+                                   gboolean preview_mode)
+{
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
 
-       return g_string_free (html, FALSE);
+       #ifdef HAVE_MARKDOWN
+       gtk_notebook_set_current_page (self->priv->notebook, preview_mode ? 1 : 0);
        #else
-       return NULL;
+       gtk_notebook_set_current_page (self->priv->notebook, 0);
        #endif
 }
diff --git a/src/e-util/e-markdown-editor.h b/src/e-util/e-markdown-editor.h
index 0cacea1493..db62d706f5 100644
--- a/src/e-util/e-markdown-editor.h
+++ b/src/e-util/e-markdown-editor.h
@@ -13,6 +13,8 @@
 
 #include <gtk/gtk.h>
 
+#include <e-util/e-focus-tracker.h>
+
 /* Standard GObject macros */
 #define E_TYPE_MARKDOWN_EDITOR \
        (e_markdown_editor_get_type ())
@@ -45,17 +47,35 @@ struct _EMarkdownEditor {
 
 struct _EMarkdownEditorClass {
        GtkBoxClass parent_class;
+
+       void    (* changed)             (EMarkdownEditor *self);
+       void    (* format_bold)         (EMarkdownEditor *self);
+       void    (* format_italic)       (EMarkdownEditor *self);
+       void    (* format_quote)        (EMarkdownEditor *self);
+       void    (* format_code)         (EMarkdownEditor *self);
+       void    (* format_bullet_list)  (EMarkdownEditor *self);
+       void    (* format_numbered_list)(EMarkdownEditor *self);
+       void    (* format_header)       (EMarkdownEditor *self);
+       void    (* insert_link)         (EMarkdownEditor *self);
+       void    (* insert_emoji)        (EMarkdownEditor *self);
+
+       /* Padding for future expansion */
+       gpointer padding[12];
 };
 
 GType          e_markdown_editor_get_type              (void) G_GNUC_CONST;
 GtkWidget *    e_markdown_editor_new                   (void);
+void           e_markdown_editor_connect_focus_tracker (EMarkdownEditor *self,
+                                                        EFocusTracker *focus_tracker);
 GtkTextView *  e_markdown_editor_get_text_view         (EMarkdownEditor *self);
 GtkToolbar *   e_markdown_editor_get_action_toolbar    (EMarkdownEditor *self);
+void           e_markdown_editor_set_text              (EMarkdownEditor *self,
+                                                        const gchar *text);
 gchar *                e_markdown_editor_dup_text              (EMarkdownEditor *self);
 gchar *                e_markdown_editor_dup_html              (EMarkdownEditor *self);
-
-gchar *                e_markdown_util_text_to_html            (const gchar *plain_text,
-                                                        gssize length);
+gboolean       e_markdown_editor_get_preview_mode      (EMarkdownEditor *self);
+void           e_markdown_editor_set_preview_mode      (EMarkdownEditor *self,
+                                                        gboolean preview_mode);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-markdown-utils.c b/src/e-util/e-markdown-utils.c
new file mode 100644
index 0000000000..ccfc6acb2a
--- /dev/null
+++ b/src/e-util/e-markdown-utils.c
@@ -0,0 +1,608 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2022 Red Hat (www.redhat.com)
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "evolution-config.h"
+
+#ifdef HAVE_MARKDOWN
+#include <cmark.h>
+#endif
+
+#include <libxml/HTMLparser.h>
+#include <libxml/HTMLtree.h>
+
+#include "e-misc-utils.h"
+
+#include "e-markdown-utils.h"
+
+#define dd(x)
+
+/**
+ * e_markdown_utils_text_to_html:
+ * @plain_text: plain text with markdown to convert to HTML
+ * @length: length of the @plain_text, or -1 when it's nul-terminated
+ *
+ * Convert @plain_text, possibly with markdown, into the HTML.
+ *
+ * Note: The function can return %NULL when was not built
+ *    with the markdown support.
+ *
+ * Returns: (transfer full) (nullable): text converted into HTML,
+ *    or %NULL, when was not built with the markdown support.
+ *    Free the string with g_free(), when no longer needed.
+ *
+ * Since: 3.44
+ **/
+gchar *
+e_markdown_utils_text_to_html (const gchar *plain_text,
+                              gssize length)
+{
+       #ifdef HAVE_MARKDOWN
+       GString *html;
+       gchar *converted;
+
+       if (length == -1)
+               length = plain_text ? strlen (plain_text) : 0;
+
+       converted = cmark_markdown_to_html (plain_text ? plain_text : "", length,
+               CMARK_OPT_VALIDATE_UTF8 | CMARK_OPT_UNSAFE);
+
+       html = e_str_replace_string (converted, "<blockquote>", "<blockquote type=\"cite\">");
+
+       g_free (converted);
+
+       return g_string_free (html, FALSE);
+       #else
+       return NULL;
+       #endif
+}
+
+static const gchar *
+markdown_utils_get_attribute_value (const xmlChar **xcattrs,
+                                   const gchar *name)
+{
+       gint ii;
+
+       if (!xcattrs)
+               return NULL;
+
+       for (ii = 0; xcattrs[ii] && xcattrs[ii + 1]; ii += 2) {
+               if (g_ascii_strcasecmp (name, (const gchar *) xcattrs[ii]) == 0)
+                       return (const gchar *) xcattrs[ii + 1];
+       }
+
+       return NULL;
+}
+
+struct _ComposerQuirks {
+       gboolean enabled;
+       gboolean reading_html_end;
+       gchar *to_body_credits;
+       gboolean cite_body;
+};
+
+static void
+markdown_utils_apply_composer_quirks (GString *buffer,
+                                     struct _ComposerQuirks *quirks)
+{
+       if (!quirks || !quirks->enabled)
+               return;
+
+       if (quirks->cite_body) {
+               gint ii;
+
+               g_string_insert (buffer, 0, "> ");
+
+               for (ii = 0; ii < buffer->len; ii++) {
+                       if (buffer->str[ii] == '\n' && ii + 1 < buffer->len) {
+                               g_string_insert (buffer, ii + 1, "> ");
+                               ii += 2;
+                       }
+               }
+       }
+
+       if (quirks->to_body_credits) {
+               g_string_insert (buffer, 0, "\n");
+               g_string_insert (buffer, 0, quirks->to_body_credits);
+       }
+}
+
+typedef struct _HTMLToTextData {
+       GString *buffer;
+       gboolean in_body;
+       gint in_code;
+       gint in_pre;
+       gint in_paragraph;
+       gboolean in_paragraph_end;
+       gboolean in_li;
+       GString *quote_prefix;
+       gchar *href;
+       GString *link_text;
+       GSList *list_index; /* gint; -1 for unordered list */
+       gboolean plain_text;
+       struct _ComposerQuirks composer_quirks;
+} HTMLToTextData;
+
+static void
+markdown_utils_sax_start_element_cb (gpointer ctx,
+                                    const xmlChar *xcname,
+                                    const xmlChar **xcattrs)
+{
+       HTMLToTextData *data = ctx;
+       const gchar *name = (const gchar *) xcname;
+       #if dd(1)+0
+       {
+               gint ii;
+
+               printf ("%s: '%s'\n", G_STRFUNC, name);
+               for (ii = 0; xcattrs && xcattrs[ii]; ii++) {
+                       printf ("   attr[%d]: '%s'\n", ii, xcattrs[ii]);
+               }
+       }
+       #endif
+
+       if (data->composer_quirks.enabled && g_ascii_strcasecmp (name, "span") == 0) {
+               const gchar *value;
+
+               value = markdown_utils_get_attribute_value (xcattrs, "class");
+
+               if (value && g_ascii_strcasecmp (value, "-x-evo-cite-body") == 0) {
+                       data->composer_quirks.cite_body = TRUE;
+                       return;
+               } else if (value && g_ascii_strcasecmp (value, "-x-evo-to-body") == 0) {
+                       value = markdown_utils_get_attribute_value (xcattrs, "data-credits");
+
+                       if (value && *value) {
+                               g_free (data->composer_quirks.to_body_credits);
+                               data->composer_quirks.to_body_credits = g_strdup (value);
+                               return;
+                       }
+               }
+       }
+
+       if (data->composer_quirks.reading_html_end)
+               return;
+
+       if (g_ascii_strcasecmp (name, "body") == 0) {
+               data->in_body = TRUE;
+               return;
+       }
+
+       if (!data->in_body)
+               return;
+
+       if (g_ascii_strcasecmp (name, "a") == 0) {
+               if (!data->plain_text && !data->href) {
+                       const gchar *href;
+
+                       href = markdown_utils_get_attribute_value (xcattrs, "href");
+
+                       if (href && *href) {
+                               data->href = g_strdup (href);
+                               data->link_text = g_string_new (NULL);
+                       }
+               }
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "blockquote") == 0) {
+               if (data->in_paragraph_end) {
+                       if (data->quote_prefix->len)
+                               g_string_append (data->buffer, data->quote_prefix->str);
+
+                       g_string_append_c (data->buffer, '\n');
+
+                       data->in_paragraph_end = FALSE;
+               }
+
+               g_string_append (data->quote_prefix, "> ");
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "br") == 0) {
+               if (data->plain_text) {
+                       g_string_append (data->buffer, "\n");
+
+                       if (data->quote_prefix->len)
+                               g_string_append (data->buffer, data->quote_prefix->str);
+               } else if (!data->composer_quirks.enabled) {
+                       g_string_append (data->buffer, "<br>");
+               }
+
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "b") == 0 ||
+           g_ascii_strcasecmp (name, "strong") == 0) {
+               if (!data->plain_text)
+                       g_string_append (data->buffer, "**");
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "i") == 0 ||
+           g_ascii_strcasecmp (name, "em") == 0) {
+               if (!data->plain_text)
+                       g_string_append (data->buffer, "*");
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "pre") == 0) {
+               data->in_paragraph++;
+               data->in_pre++;
+               if (data->in_pre == 1) {
+                       if (!data->plain_text)
+                               g_string_append (data->buffer, "```\n");
+               }
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "code") == 0) {
+               data->in_code++;
+               if (data->in_code == 1 && !data->in_pre && !data->plain_text)
+                       g_string_append (data->buffer, "`");
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "h1") == 0 ||
+           g_ascii_strcasecmp (name, "h2") == 0 ||
+           g_ascii_strcasecmp (name, "h3") == 0 ||
+           g_ascii_strcasecmp (name, "h4") == 0 ||
+           g_ascii_strcasecmp (name, "h5") == 0 ||
+           g_ascii_strcasecmp (name, "h6") == 0) {
+               if (data->in_paragraph_end) {
+                       g_string_append_c (data->buffer, '\n');
+                       data->in_paragraph_end = FALSE;
+               }
+
+               data->in_paragraph++;
+               if (data->quote_prefix->len)
+                       g_string_append (data->buffer, data->quote_prefix->str);
+
+               if (!data->plain_text) {
+                       switch (name[1]) {
+                       case '1':
+                               g_string_append (data->buffer, "# ");
+                               break;
+                       case '2':
+                               g_string_append (data->buffer, "## ");
+                               break;
+                       case '3':
+                               g_string_append (data->buffer, "### ");
+                               break;
+                       case '4':
+                               g_string_append (data->buffer, "#### ");
+                               break;
+                       case '5':
+                               g_string_append (data->buffer, "##### ");
+                               break;
+                       case '6':
+                               g_string_append (data->buffer, "###### ");
+                               break;
+                       }
+               }
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "p") == 0 ||
+           g_ascii_strcasecmp (name, "div") == 0) {
+               if (data->in_paragraph_end) {
+                       data->in_paragraph_end = FALSE;
+
+                       if (data->quote_prefix->len)
+                               g_string_append (data->buffer, data->quote_prefix->str);
+
+                       g_string_append_c (data->buffer, '\n');
+               }
+
+               data->in_paragraph++;
+               if (data->quote_prefix->len)
+                       g_string_append (data->buffer, data->quote_prefix->str);
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "ul") == 0) {
+               if (data->in_paragraph_end) {
+                       g_string_append_c (data->buffer, '\n');
+                       data->in_paragraph_end = FALSE;
+               }
+               data->list_index = g_slist_prepend (data->list_index, GINT_TO_POINTER (-1));
+               data->in_li = FALSE;
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "ol") == 0) {
+               if (data->in_paragraph_end) {
+                       g_string_append_c (data->buffer, '\n');
+                       data->in_paragraph_end = FALSE;
+               }
+               data->list_index = g_slist_prepend (data->list_index, GINT_TO_POINTER (1));
+               data->in_li = FALSE;
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "li") == 0) {
+               data->in_paragraph_end = FALSE;
+               data->in_li = TRUE;
+
+               if (data->list_index) {
+                       gint index = GPOINTER_TO_INT (data->list_index->data);
+                       gint level = g_slist_length (data->list_index) - 1;
+
+                       if (data->quote_prefix->len)
+                               g_string_append (data->buffer, data->quote_prefix->str);
+
+                       if (level > 0)
+                               g_string_append_printf (data->buffer, "%*s", level * 3, "");
+
+                       if (index == -1) {
+                               g_string_append (data->buffer, "- ");
+                       } else {
+                               g_string_append_printf (data->buffer, "%d. ", index);
+                               data->list_index->data = GINT_TO_POINTER (index + 1);
+                       }
+               }
+               return;
+       }
+}
+
+static void
+markdown_utils_sax_end_element_cb (gpointer ctx,
+                                  const xmlChar *xcname)
+{
+       HTMLToTextData *data = ctx;
+       const gchar *name = (const gchar *) xcname;
+
+       dd (printf ("%s: '%s'\n", G_STRFUNC, name);)
+
+       if (g_ascii_strcasecmp (name, "body") == 0) {
+               data->in_body = FALSE;
+               return;
+       }
+
+       if (!data->in_body)
+               return;
+
+       if (g_ascii_strcasecmp (name, "a") == 0) {
+               if (!data->plain_text && data->href && data->link_text) {
+                       g_string_append_printf (data->buffer, "[%s](%s)", data->link_text->str, data->href);
+
+                       g_free (data->href);
+                       data->href = NULL;
+
+                       g_string_free (data->link_text, TRUE);
+                       data->link_text = NULL;
+               }
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "blockquote") == 0) {
+               if (data->quote_prefix->len > 1)
+                       g_string_truncate (data->quote_prefix, data->quote_prefix->len - 2);
+
+               data->in_paragraph_end = data->quote_prefix->len > 1;
+
+               if (!data->in_paragraph_end)
+                       g_string_append_c (data->buffer, '\n');
+
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "b") == 0 ||
+           g_ascii_strcasecmp (name, "strong") == 0) {
+               if (!data->plain_text)
+                       g_string_append (data->buffer, "**");
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "i") == 0 ||
+           g_ascii_strcasecmp (name, "em") == 0) {
+               if (!data->plain_text)
+                       g_string_append (data->buffer, "*");
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "pre") == 0) {
+               if (data->in_paragraph > 0)
+                       data->in_paragraph--;
+
+               if (data->in_pre > 0) {
+                       data->in_pre--;
+                       if (data->in_pre == 0 && !data->plain_text)
+                               g_string_append (data->buffer, "```");
+                       g_string_append_c (data->buffer, '\n');
+               }
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "code") == 0) {
+               if (data->in_code > 0) {
+                       data->in_code--;
+                       if (data->in_code == 0 && !data->in_pre && !data->plain_text)
+                               g_string_append (data->buffer, "`");
+               }
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "p") == 0 ||
+           g_ascii_strcasecmp (name, "div") == 0 ||
+           g_ascii_strcasecmp (name, "h1") == 0 ||
+           g_ascii_strcasecmp (name, "h2") == 0 ||
+           g_ascii_strcasecmp (name, "h3") == 0 ||
+           g_ascii_strcasecmp (name, "h4") == 0 ||
+           g_ascii_strcasecmp (name, "h5") == 0 ||
+           g_ascii_strcasecmp (name, "h6") == 0) {
+               /* To avoid double-line ends when parsing composer HTML */
+               if (data->composer_quirks.enabled && !(
+                   g_ascii_strcasecmp (name, "p") == 0 ||
+                   g_ascii_strcasecmp (name, "div") == 0))
+                       g_string_append_c (data->buffer, '\n');
+
+               data->in_paragraph_end = TRUE;
+
+               if (data->in_paragraph > 0)
+                       data->in_paragraph--;
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "ul") == 0 ||
+           g_ascii_strcasecmp (name, "ol") == 0) {
+               if (data->list_index)
+                       data->list_index = g_slist_remove (data->list_index, data->list_index->data);
+               data->in_paragraph_end = data->list_index == NULL;
+
+               if (!data->in_paragraph_end && data->buffer->len && data->buffer->str[data->buffer->len - 1] 
== '\n')
+                       g_string_truncate (data->buffer, data->buffer->len - 1);
+
+               return;
+       }
+
+       if (g_ascii_strcasecmp (name, "li") == 0) {
+               g_string_append_c (data->buffer, '\n');
+
+               data->in_paragraph_end = FALSE;
+               data->in_li = FALSE;
+
+               return;
+       }
+}
+
+static gboolean
+markdown_utils_only_whitespace (const gchar *text,
+                               gint len)
+{
+       gint ii;
+
+       for (ii = 0; ii < len && text[ii]; ii++) {
+               if (!g_ascii_isspace (text[ii]))
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void
+markdown_utils_sax_characters_cb (gpointer ctx,
+                                 const xmlChar *xctext,
+                                 gint len)
+{
+       HTMLToTextData *data = ctx;
+       const gchar *text = (const gchar *) xctext;
+
+       dd (printf ("%s: text:'%.*s' in_body:%d in_paragraph:%d in_li:%d\n", G_STRFUNC, len, text, 
data->in_body, data->in_paragraph, data->in_li);)
+
+       if (data->in_body && (data->in_paragraph || data->in_li || !markdown_utils_only_whitespace (text, 
len))) {
+               if (data->link_text) {
+                       g_string_append_len (data->link_text, text, len);
+               } else {
+                       gsize from_index = data->buffer->len;
+
+                       g_string_append_len (data->buffer, text, len);
+
+                       if (data->quote_prefix->len && !data->in_li && strchr (data->buffer->str + 
from_index, '\n')) {
+                               gint ii;
+
+                               for (ii = from_index; ii < data->buffer->len; ii++) {
+                                       if (data->buffer->str[ii] == '\n') {
+                                               g_string_insert (data->buffer, ii + 1, 
data->quote_prefix->str);
+                                               ii += data->quote_prefix->len + 1;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void
+markdown_utils_sax_warning_cb (gpointer ctx,
+                              const gchar *msg,
+                              ...)
+{
+       /* Ignore these */
+}
+
+static void
+markdown_utils_sax_error_cb (gpointer ctx,
+                            const gchar *msg,
+                            ...)
+{
+       /* Ignore these */
+}
+
+/**
+ * e_markdown_utils_html_to_text:
+ * @html: a text in HTML
+ * @length: length of the @html, or -1 when it's nul-terminated
+ * @flags: a bit-or of %EMarkdownHTMLToTextFlags
+ *
+ * Convert @html into the markdown text. The @flags influence
+ * what can be preserved from the @html.
+ *
+ * Returns: (transfer full) (nullable): HTML converted into markdown text.
+ *    Free the string with g_free(), when no longer needed.
+ *
+ * Since: 3.44
+ **/
+gchar *
+e_markdown_utils_html_to_text (const gchar *html,
+                              gssize length,
+                              EMarkdownHTMLToTextFlags flags)
+{
+       htmlParserCtxtPtr ctxt;
+       htmlSAXHandler sax;
+       HTMLToTextData data;
+
+       if (length < 0)
+               length = html ? strlen (html) : 0;
+
+       memset (&data, 0, sizeof (HTMLToTextData));
+
+       data.buffer = g_string_new (NULL);
+       data.quote_prefix = g_string_new (NULL);
+       data.plain_text = (flags & E_MARKDOWN_HTML_TO_TEXT_FLAG_PLAIN_TEXT) != 0;
+       data.composer_quirks.enabled = (flags & E_MARKDOWN_HTML_TO_TEXT_FLAG_COMPOSER_QUIRKS) != 0;
+
+       memset (&sax, 0, sizeof (htmlSAXHandler));
+
+       sax.startElement = markdown_utils_sax_start_element_cb;
+       sax.endElement = markdown_utils_sax_end_element_cb;
+       sax.characters = markdown_utils_sax_characters_cb;
+       sax.warning = markdown_utils_sax_warning_cb;
+       sax.error = markdown_utils_sax_error_cb;
+
+       ctxt = htmlCreatePushParserCtxt (&sax, &data, html ? html : "", length, "", XML_CHAR_ENCODING_UTF8);
+
+       htmlParseChunk (ctxt, "", 0, 1);
+
+       /* The libxml doesn't read elements after </html>, but the quirks can be stored after them,
+          thus retry after that element end, if it exists. */
+       if (data.composer_quirks.enabled && html && ctxt->input && ctxt->input->cur) {
+               guint html_end_length = ctxt->input->end - ctxt->input->cur;
+
+               if (html_end_length > 1) {
+                       htmlParserCtxtPtr ctxt2;
+
+                       data.composer_quirks.reading_html_end = TRUE;
+
+                       ctxt2 = htmlCreatePushParserCtxt (&sax, &data, (const gchar *) ctxt->input->cur, 
html_end_length, "", XML_CHAR_ENCODING_UTF8);
+                       htmlParseChunk (ctxt2, "", 0, 1);
+                       htmlFreeParserCtxt (ctxt2);
+               }
+       }
+
+       htmlFreeParserCtxt (ctxt);
+
+       markdown_utils_apply_composer_quirks (data.buffer, &data.composer_quirks);
+
+       g_free (data.href);
+
+       if (data.link_text)
+               g_string_free (data.link_text, TRUE);
+
+       g_string_free (data.quote_prefix, TRUE);
+       g_slist_free (data.list_index);
+       g_free (data.composer_quirks.to_body_credits);
+
+       return g_string_free (data.buffer, FALSE);
+}
diff --git a/src/e-util/e-markdown-utils.h b/src/e-util/e-markdown-utils.h
new file mode 100644
index 0000000000..928dbbbb70
--- /dev/null
+++ b/src/e-util/e-markdown-utils.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * SPDX-FileCopyrightText: (C) 2022 Red Hat (www.redhat.com)
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_MARKDOWN_UTILS_H
+#define E_MARKDOWN_UTILS_H
+
+#include <glib.h>
+
+#include <e-util/e-util-enums.h>
+
+G_BEGIN_DECLS
+
+gchar *                e_markdown_utils_text_to_html           (const gchar *plain_text,
+                                                        gssize length);
+gchar *                e_markdown_utils_html_to_text           (const gchar *html,
+                                                        gssize length,
+                                                        EMarkdownHTMLToTextFlags flags);
+
+G_END_DECLS
+
+#endif /* E_MARKDOWN_UTILS_H */
diff --git a/src/e-util/e-spell-text-view.c b/src/e-util/e-spell-text-view.c
index d63a594ef0..0a2a80de21 100644
--- a/src/e-util/e-spell-text-view.c
+++ b/src/e-util/e-spell-text-view.c
@@ -103,3 +103,85 @@ e_spell_text_view_attach (GtkTextView *text_view)
        gspell_text_view_set_enable_language_menu (spell_view, TRUE);
 #endif /* HAVE_GSPELL */
 }
+
+/**
+ * e_spell_text_view_get_enabled:
+ * @text_view: a #GtkTextView
+ *
+ * Returns: whether the inline spell checking is enabled for the @text_view.
+ * This can be used only after calling e_spell_text_view_attach().
+ *
+ * Since: 3.44
+ **/
+gboolean
+e_spell_text_view_get_enabled (GtkTextView *text_view)
+{
+#ifdef HAVE_GSPELL
+       GspellTextView *spell_view;
+
+       spell_view = gspell_text_view_get_from_gtk_text_view (text_view);
+
+       return gspell_text_view_get_inline_spell_checking (spell_view);
+#else /* HAVE_GSPELL */
+       return FALSE;
+#endif /* HAVE_GSPELL */
+}
+
+/**
+ * e_spell_text_view_set_enabled:
+ * @text_view: a #GtkTextView
+ * @enabled: value to set
+ *
+ * Sets whether the inline spell checking is enabled for the @text_view.
+ * This can be used only after calling e_spell_text_view_attach().
+ *
+ * Since: 3.44
+ **/
+void
+e_spell_text_view_set_enabled (GtkTextView *text_view,
+                              gboolean enabled)
+{
+#ifdef HAVE_GSPELL
+       GspellTextView *spell_view;
+
+       spell_view = gspell_text_view_get_from_gtk_text_view (text_view);
+
+       gspell_text_view_set_inline_spell_checking (spell_view, enabled);
+#endif /* HAVE_GSPELL */
+}
+
+/**
+ * e_spell_text_view_set_languages:
+ * @text_view: a #GtkTextView
+ * @languages: (nullable): languages to set, or %NULL to unset any previous
+ *
+ * Sets @languages for inline spell checking for the @text_view.
+ * This can be used only after calling e_spell_text_view_attach().
+ *
+ * Since: 3.44
+ **/
+void
+e_spell_text_view_set_languages (GtkTextView *text_view,
+                                const gchar **languages)
+{
+#ifdef HAVE_GSPELL
+       GspellTextBuffer *spell_buffer;
+       GspellChecker *checker = NULL;
+       GtkTextBuffer *text_buffer;
+       guint ii;
+
+       for (ii = 0; !checker && languages && languages[ii]; ii++) {
+               const GspellLanguage *language;
+
+               language = gspell_language_lookup (languages[ii]);
+
+               if (language)
+                       checker = gspell_checker_new (language);
+       }
+
+       text_buffer = gtk_text_view_get_buffer (text_view);
+       spell_buffer = gspell_text_buffer_get_from_gtk_text_buffer (text_buffer);
+       gspell_text_buffer_set_spell_checker (spell_buffer, checker);
+       g_clear_object (&checker);
+#endif /* HAVE_GSPELL */
+}
diff --git a/src/e-util/e-spell-text-view.h b/src/e-util/e-spell-text-view.h
index 0d8788eca0..7026fd0e4c 100644
--- a/src/e-util/e-spell-text-view.h
+++ b/src/e-util/e-spell-text-view.h
@@ -28,6 +28,11 @@ G_BEGIN_DECLS
 
 gboolean       e_spell_text_view_is_supported  (void);
 void           e_spell_text_view_attach        (GtkTextView *text_view);
+gboolean       e_spell_text_view_get_enabled   (GtkTextView *text_view);
+void           e_spell_text_view_set_enabled   (GtkTextView *text_view,
+                                                gboolean enabled);
+void           e_spell_text_view_set_languages (GtkTextView *text_view,
+                                                const gchar **languages);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-util-enums.h b/src/e-util/e-util-enums.h
index 2a32dfa8bb..18cd855ca0 100644
--- a/src/e-util/e-util-enums.h
+++ b/src/e-util/e-util-enums.h
@@ -525,6 +525,28 @@ typedef enum {
        E_CONTENT_EDITOR_FIND_WRAP_AROUND       = 1 << 4
 } EContentEditorFindFlags;
 
+/**
+ * EContentEditorMode:
+ * @E_CONTENT_EDITOR_MODE_UNKNOWN: unknown mode
+ * @E_CONTENT_EDITOR_MODE_PLAIN_TEXT: plain text, expects export as text/plain
+ * @E_CONTENT_EDITOR_MODE_HTML: HTML, expects export as text/html
+ * @E_CONTENT_EDITOR_MODE_MARKDOWN: markdown, expects export as text/markdown
+ * @E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT: markdown, expects export as text/plain
+ * @E_CONTENT_EDITOR_MODE_MARKDOWN_HTML: markdown, expects export as text/html
+ *
+ * Editing mode of a content editor.
+ *
+ * Since: 3.44
+ **/
+typedef enum {
+       E_CONTENT_EDITOR_MODE_UNKNOWN = -1,
+       E_CONTENT_EDITOR_MODE_PLAIN_TEXT,
+       E_CONTENT_EDITOR_MODE_HTML,
+       E_CONTENT_EDITOR_MODE_MARKDOWN,
+       E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT,
+       E_CONTENT_EDITOR_MODE_MARKDOWN_HTML
+} EContentEditorMode;
+
 /**
  * EUndoRedoState:
  * @E_UNDO_REDO_STATE_NONE: Cannot undo, neither redo.
@@ -620,6 +642,22 @@ typedef enum {
  **/
 #define E_CONFIG_LOOKUP_RESULT_LAST_KIND E_CONFIG_LOOKUP_RESULT_TASK_LIST
 
+/**
+ * EMarkdownHTMLToTextFlags:
+ * @E_MARKDOWN_HTML_TO_TEXT_FLAG_NONE: no flag set
+ * @E_MARKDOWN_HTML_TO_TEXT_FLAG_PLAIN_TEXT: disallow any HTML, save in pure plain text
+ * @E_MARKDOWN_HTML_TO_TEXT_FLAG_COMPOSER_QUIRKS: enable composer quirks to post-process the text
+ *
+ * Flags used in e_markdown_util_html_to_text().
+ *
+ * Since: 3.44
+ **/
+typedef enum { /*< flags >*/
+       E_MARKDOWN_HTML_TO_TEXT_FLAG_NONE               = 0,
+       E_MARKDOWN_HTML_TO_TEXT_FLAG_PLAIN_TEXT         = 1 << 0,
+       E_MARKDOWN_HTML_TO_TEXT_FLAG_COMPOSER_QUIRKS    = 1 << 1
+} EMarkdownHTMLToTextFlags;
+
 G_END_DECLS
 
 #endif /* E_UTIL_ENUMS_H */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index 666363e9e8..6801baf41c 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -153,6 +153,7 @@
 #include <e-util/e-mail-signature-tree-view.h>
 #include <e-util/e-map.h>
 #include <e-util/e-markdown-editor.h>
+#include <e-util/e-markdown-utils.h>
 #include <e-util/e-menu-tool-action.h>
 #include <e-util/e-menu-tool-button.h>
 #include <e-util/e-misc-utils.h>
diff --git a/src/e-util/test-html-editor-units-utils.c b/src/e-util/test-html-editor-units-utils.c
index abbeef8130..8912e64c38 100644
--- a/src/e-util/test-html-editor-units-utils.c
+++ b/src/e-util/test-html-editor-units-utils.c
@@ -292,23 +292,7 @@ test_utils_html_editor_created_cb (GObject *source_object,
 
        fixture->focus_tracker = e_focus_tracker_new (GTK_WINDOW (fixture->window));
 
-       e_focus_tracker_set_cut_clipboard_action (fixture->focus_tracker,
-               e_html_editor_get_action (fixture->editor, "cut"));
-
-       e_focus_tracker_set_copy_clipboard_action (fixture->focus_tracker,
-               e_html_editor_get_action (fixture->editor, "copy"));
-
-       e_focus_tracker_set_paste_clipboard_action (fixture->focus_tracker,
-               e_html_editor_get_action (fixture->editor, "paste"));
-
-       e_focus_tracker_set_select_all_action (fixture->focus_tracker,
-               e_html_editor_get_action (fixture->editor, "select-all"));
-
-       e_focus_tracker_set_undo_action (fixture->focus_tracker,
-               e_html_editor_get_action (fixture->editor, "undo"));
-
-       e_focus_tracker_set_redo_action (fixture->focus_tracker,
-               e_html_editor_get_action (fixture->editor, "redo"));
+       e_html_editor_connect_focus_tracker (fixture->editor, fixture->focus_tracker);
 
        /* Make sure this is off */
        test_utils_fixture_change_setting_boolean (fixture,
diff --git a/src/e-util/test-html-editor.c b/src/e-util/test-html-editor.c
index 7dd5765be4..f8a9bb8cf6 100644
--- a/src/e-util/test-html-editor.c
+++ b/src/e-util/test-html-editor.c
@@ -344,7 +344,7 @@ action_save_cb (GtkAction *action,
                        return;
 
        filename = e_html_editor_get_filename (editor);
-       as_html = (e_content_editor_get_html_mode (e_html_editor_get_content_editor (editor)));
+       as_html = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
 
        e_html_editor_save (editor, filename, as_html, NULL, html_editor_save_done_cb, NULL);
 }
@@ -360,7 +360,7 @@ action_save_as_cb (GtkAction *action,
                return;
 
        filename = e_html_editor_get_filename (editor);
-       as_html = (e_content_editor_get_html_mode (e_html_editor_get_content_editor (editor)));
+       as_html = e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_HTML;
 
        e_html_editor_save (editor, filename, as_html, NULL, html_editor_save_done_cb, NULL);
 }
@@ -592,23 +592,7 @@ create_new_editor_cb (GObject *source_object,
        focus_tracker = e_focus_tracker_new (GTK_WINDOW (widget));
        g_object_set_data_full (G_OBJECT (widget), "e-focus-tracker", focus_tracker, g_object_unref);
 
-       e_focus_tracker_set_cut_clipboard_action (focus_tracker,
-               e_html_editor_get_action (editor, "cut"));
-
-       e_focus_tracker_set_copy_clipboard_action (focus_tracker,
-               e_html_editor_get_action (editor, "copy"));
-
-       e_focus_tracker_set_paste_clipboard_action (focus_tracker,
-               e_html_editor_get_action (editor, "paste"));
-
-       e_focus_tracker_set_select_all_action (focus_tracker,
-               e_html_editor_get_action (editor, "select-all"));
-
-       e_focus_tracker_set_undo_action (focus_tracker,
-               e_html_editor_get_action (editor, "undo"));
-
-       e_focus_tracker_set_redo_action (focus_tracker,
-               e_html_editor_get_action (editor, "redo"));
+       e_html_editor_connect_focus_tracker (editor, focus_tracker);
 
        g_signal_connect_swapped (
                widget, "destroy",
diff --git a/src/e-util/test-mail-signatures.c b/src/e-util/test-mail-signatures.c
index 95ad835668..b0f5dd5da4 100644
--- a/src/e-util/test-mail-signatures.c
+++ b/src/e-util/test-mail-signatures.c
@@ -29,11 +29,11 @@ signature_loaded_cb (EMailSignatureComboBox *combo_box,
                      EWebView *web_view)
 {
        gchar *contents = NULL;
-       gboolean is_html;
+       EContentEditorMode editor_mode = E_CONTENT_EDITOR_MODE_UNKNOWN;
        GError *error = NULL;
 
        e_mail_signature_combo_box_load_selected_finish (
-               combo_box, result, &contents, NULL, &is_html, &error);
+               combo_box, result, &contents, NULL, &editor_mode, &error);
 
        /* Ignore cancellations. */
        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
@@ -55,7 +55,7 @@ signature_loaded_cb (EMailSignatureComboBox *combo_box,
 
        if (contents == NULL)
                e_web_view_clear (web_view);
-       else if (is_html)
+       else if (editor_mode == E_CONTENT_EDITOR_MODE_HTML)
                e_web_view_load_string (web_view, contents);
        else {
                gchar *string;
diff --git a/src/e-util/test-markdown-editor.c b/src/e-util/test-markdown-editor.c
index 88d439dd7e..9fc5dc32a3 100644
--- a/src/e-util/test-markdown-editor.c
+++ b/src/e-util/test-markdown-editor.c
@@ -19,10 +19,153 @@ window_delete_event_cb (GtkWidget *widget,
        return FALSE;
 }
 
+static void
+editor_to_plain_text_cb (GObject *button,
+                        EMarkdownEditor *editor)
+{
+       GtkTextView *text_view;
+       GtkTextBuffer *buffer;
+       gchar *text;
+
+       text_view = g_object_get_data (button, "text_view");
+       buffer = gtk_text_view_get_buffer (text_view);
+       text = e_markdown_editor_dup_text (editor);
+
+       gtk_text_buffer_set_text (buffer, text, -1);
+
+       g_free (text);
+}
+
+static void
+editor_to_html_cb (GObject *button,
+                  EMarkdownEditor *editor)
+{
+       GtkTextView *text_view;
+       GtkTextBuffer *buffer;
+       gchar *text;
+
+       text_view = g_object_get_data (button, "text_view");
+       buffer = gtk_text_view_get_buffer (text_view);
+       text = e_markdown_editor_dup_html (editor);
+
+       gtk_text_buffer_set_text (buffer, text ? text : "NULL", -1);
+
+       g_free (text);
+}
+
+/* The cmark can add an empty line at the end of the HTML, thus compare without it too */
+static gboolean
+texts_are_same (gchar *text1,
+               gchar *text2)
+{
+       gint len1 = 0, len2 = 0;
+       gboolean text1_modified = FALSE, text2_modified = FALSE;
+       gboolean same;
+
+       if (text1 && text2) {
+               len1 = strlen (text1);
+               len2 = strlen (text2);
+
+               if (len1 + 1 == len2 && text2[len2 - 1] == '\n') {
+                       text2[len2 - 1] = '\0';
+                       text2_modified = TRUE;
+               } else if (len1 == len2 + 1 && text1[len1 - 1] == '\n') {
+                       text1[len1 - 1] = '\0';
+                       text1_modified = TRUE;
+               }
+       }
+
+       same = g_strcmp0 (text1, text2) == 0;
+
+       if (text1_modified)
+               text1[len1 - 1] = '\n';
+       else if (text2_modified)
+               text2[len2 - 1] = '\n';
+
+       return same || ((text1_modified || text2_modified) && g_strcmp0 (text1, text2) == 0);
+}
+
+static void
+plain_text_to_html_cb (GObject *button,
+                      GtkLabel *label)
+{
+       GtkTextView *plain_text_view, *html_text_view;
+       GtkTextBuffer *plain_buffer, *html_buffer;
+       GtkTextIter start_iter, end_iter;
+       gchar *old_text, *new_text, *tmp;
+
+       plain_text_view = g_object_get_data (button, "plain_text_view");
+       plain_buffer = gtk_text_view_get_buffer (plain_text_view);
+
+       html_text_view = g_object_get_data (button, "html_text_view");
+       html_buffer = gtk_text_view_get_buffer (html_text_view);
+
+       gtk_text_buffer_get_bounds (html_buffer, &start_iter, &end_iter);
+       old_text = gtk_text_buffer_get_text (html_buffer, &start_iter, &end_iter, FALSE);
+
+       gtk_text_buffer_get_bounds (plain_buffer, &start_iter, &end_iter);
+       tmp = gtk_text_buffer_get_text (plain_buffer, &start_iter, &end_iter, FALSE);
+
+       new_text = e_markdown_utils_text_to_html (tmp, -1);
+
+       gtk_text_buffer_set_text (html_buffer, new_text ? new_text : "NULL", -1);
+
+       if (texts_are_same (new_text, old_text))
+               gtk_label_set_text (label, "HTML text matches");
+       else
+               gtk_label_set_text (label, "Old and new HTML texts differ");
+
+       g_free (old_text);
+       g_free (new_text);
+       g_free (tmp);
+}
+
+static void
+html_to_plain_text_cb (GObject *button,
+                      GtkLabel *label)
+{
+       GtkTextView *plain_text_view, *html_text_view;
+       GtkTextBuffer *plain_buffer, *html_buffer;
+       GtkTextIter start_iter, end_iter;
+       GtkToggleButton *disallow_html_check;
+       gchar *old_text, *new_text, *tmp;
+
+       disallow_html_check = g_object_get_data (button, "disallow_html_check");
+
+       plain_text_view = g_object_get_data (button, "plain_text_view");
+       plain_buffer = gtk_text_view_get_buffer (plain_text_view);
+
+       html_text_view = g_object_get_data (button, "html_text_view");
+       html_buffer = gtk_text_view_get_buffer (html_text_view);
+
+       gtk_text_buffer_get_bounds (plain_buffer, &start_iter, &end_iter);
+       old_text = gtk_text_buffer_get_text (plain_buffer, &start_iter, &end_iter, FALSE);
+
+       gtk_text_buffer_get_bounds (html_buffer, &start_iter, &end_iter);
+       tmp = gtk_text_buffer_get_text (html_buffer, &start_iter, &end_iter, FALSE);
+
+       new_text = e_markdown_utils_html_to_text (tmp, -1, E_MARKDOWN_HTML_TO_TEXT_FLAG_NONE |
+               (gtk_toggle_button_get_active (disallow_html_check) ? E_MARKDOWN_HTML_TO_TEXT_FLAG_PLAIN_TEXT 
: 0));
+
+       gtk_text_buffer_set_text (plain_buffer, new_text ? new_text : "NULL", -1);
+
+       if (texts_are_same (new_text, old_text))
+               gtk_label_set_text (label, "Plain text matches");
+       else
+               gtk_label_set_text (label, "Old and new plain texts differ");
+
+       g_free (old_text);
+       g_free (new_text);
+       g_free (tmp);
+}
+
 static gint
 on_idle_create_widget (gpointer user_data)
 {
-       GtkWidget *window, *editor;
+       GtkWidget *window, *editor, *widget, *button1, *button2;
+       GtkGrid *grid;
+       GtkTextView *plain_text_view;
+       GtkTextView *html_text_view;
 
        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
@@ -31,6 +174,20 @@ on_idle_create_widget (gpointer user_data)
                window, "delete-event",
                G_CALLBACK (window_delete_event_cb), NULL);
 
+       widget = gtk_grid_new ();
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (window), widget);
+
+       grid = GTK_GRID (widget);
+
        editor = e_markdown_editor_new ();
 
        g_object_set (G_OBJECT (editor),
@@ -41,7 +198,157 @@ on_idle_create_widget (gpointer user_data)
                "visible", TRUE,
                NULL);
 
-       gtk_container_add (GTK_CONTAINER (window), editor);
+       gtk_grid_attach (grid, editor, 0, 0, 3, 1);
+
+       widget = gtk_button_new_with_label ("vvv   As Plain Text   vvv");
+       button1 = widget;
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+       widget = gtk_button_new_with_label ("vvv   As HTML   vvv");
+       button2 = widget;
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_grid_attach (grid, widget, 2, 1, 1, 1);
+
+       widget = gtk_text_view_new ();
+       plain_text_view = GTK_TEXT_VIEW (widget);
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "editable", TRUE,
+               "wrap-mode", GTK_WRAP_WORD_CHAR,
+               NULL);
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (plain_text_view));
+
+       gtk_grid_attach (grid, widget, 0, 2, 1, 2);
+
+       widget = gtk_text_view_new ();
+       html_text_view = GTK_TEXT_VIEW (widget);
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "editable", TRUE,
+               "wrap-mode", GTK_WRAP_WORD_CHAR,
+               NULL);
+
+       widget = gtk_scrolled_window_new (NULL, NULL);
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (html_text_view));
+
+       gtk_grid_attach (grid, widget, 2, 2, 1, 2);
+
+       g_object_set_data (G_OBJECT (button1), "text_view", plain_text_view);
+       g_signal_connect (button1, "clicked", G_CALLBACK (editor_to_plain_text_cb), editor);
+
+       g_object_set_data (G_OBJECT (button2), "text_view", html_text_view);
+       g_signal_connect (button2, "clicked", G_CALLBACK (editor_to_html_cb), editor);
+
+       widget = gtk_button_new_with_label (">\n>\n>");
+       button1 = widget;
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+
+       widget = gtk_button_new_with_label ("<\n<\n<");
+       button2 = widget;
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_grid_attach (grid, widget, 1, 3, 1, 1);
+
+       widget = gtk_label_new ("");
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_grid_attach (grid, widget, 0, 4, 3, 1);
+
+       g_object_set_data (G_OBJECT (button1), "plain_text_view", plain_text_view);
+       g_object_set_data (G_OBJECT (button1), "html_text_view", html_text_view);
+       g_signal_connect (button1, "clicked", G_CALLBACK (plain_text_to_html_cb), widget);
+
+       g_object_set_data (G_OBJECT (button2), "plain_text_view", plain_text_view);
+       g_object_set_data (G_OBJECT (button2), "html_text_view", html_text_view);
+       g_signal_connect (button2, "clicked", G_CALLBACK (html_to_plain_text_cb), widget);
+
+       widget = gtk_check_button_new_with_label ("HTML2Text: Disallow HTML");
+
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "hexpand", FALSE,
+               "valign", GTK_ALIGN_START,
+               "vexpand", FALSE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_grid_attach (grid, widget, 0, 5, 3, 1);
+
+       g_object_set_data (G_OBJECT (button2), "disallow_html_check", widget);
 
        gtk_widget_show (window);
 
diff --git a/src/em-format/e-mail-formatter-text-markdown.c b/src/em-format/e-mail-formatter-text-markdown.c
index 1d0fb906dc..1e2e634dcc 100644
--- a/src/em-format/e-mail-formatter-text-markdown.c
+++ b/src/em-format/e-mail-formatter-text-markdown.c
@@ -65,7 +65,7 @@ emfe_text_markdown_format (EMailFormatterExtension *extension,
                e_mail_formatter_format_text (formatter, part, output_stream, cancellable);
                g_output_stream_flush (output_stream, cancellable, NULL);
 
-               html = e_markdown_util_text_to_html ((const gchar *) g_memory_output_stream_get_data 
(G_MEMORY_OUTPUT_STREAM (output_stream)),
+               html = e_markdown_utils_text_to_html ((const gchar *) g_memory_output_stream_get_data 
(G_MEMORY_OUTPUT_STREAM (output_stream)),
                        g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (output_stream)));
 
                g_object_unref (output_stream);
diff --git a/src/mail/e-mail-enums.h b/src/mail/e-mail-enums.h
index 9ae17d76bf..4702bc1997 100644
--- a/src/mail/e-mail-enums.h
+++ b/src/mail/e-mail-enums.h
@@ -50,11 +50,25 @@ typedef enum {
  * @E_MAIL_REPLY_FLAG_FORCE_STYLE: Force use of the passed-in reply style; if not set,
  *    then also checks reply style setting for the used mail account.
  * @E_MAIL_REPLY_FLAG_FORMAT_PLAIN: Force compose in Plain Text format; cannot be used together
- *    with @E_MAIL_REPLY_FLAG_FORMAT_HTML. If none of these is set, then uses
- *    global setting.
+ *    with @E_MAIL_REPLY_FLAG_FORMAT_HTML, @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN,
+ *    @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN nor @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML.
+ *    If none of these is set, then uses global setting.
  * @E_MAIL_REPLY_FLAG_FORMAT_HTML: Force compose in HTML format; cannot be used together
- *    with @E_MAIL_REPLY_FLAG_FORMAT_PLAIN. If none of these is set, then uses
- *    global setting.
+ *    with @E_MAIL_REPLY_FLAG_FORMAT_PLAIN, @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN,
+ *    @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN nor @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML.
+ *    If none of these is set, then uses global setting.
+ * @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN: Force compose in Markdown format; cannot be used together
+ *    with @E_MAIL_REPLY_FLAG_FORMAT_PLAIN, @E_MAIL_REPLY_FLAG_FORMAT_HTML,
+ *    @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN nor @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML.
+ *    If none of these is set, then uses global setting. (Since: 3.44)
+ * @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN: Force compose in Markdown as Plain Text format; cannot be used 
together
+ *    with @E_MAIL_REPLY_FLAG_FORMAT_PLAIN, @E_MAIL_REPLY_FLAG_FORMAT_HTML,
+ *    @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN nor @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML.
+ *    If none of these is set, then uses global setting. (Since: 3.44)
+ * @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML: Force compose in Markdown as HTML format; cannot be used together
+ *    with @E_MAIL_REPLY_FLAG_FORMAT_PLAIN, @E_MAIL_REPLY_FLAG_FORMAT_HTML,
+ *    @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN nor @E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN.
+ *    If none of these is set, then uses global setting. (Since: 3.44)
  * @E_MAIL_REPLY_FLAG_TOP_POSTING: Force top posting; cannot be used together
  *    with @E_MAIL_REPLY_FLAG_BOTTOM_POSTING. If none is set, then uses global settings.
  * @E_MAIL_REPLY_FLAG_BOTTOM_POSTING: Force bottom posting; cannot be used together
@@ -79,7 +93,10 @@ typedef enum { /*< flags >*/
        E_MAIL_REPLY_FLAG_BOTTOM_POSTING        = 1 << 4,
        E_MAIL_REPLY_FLAG_TOP_SIGNATURE         = 1 << 5,
        E_MAIL_REPLY_FLAG_BOTTOM_SIGNATURE      = 1 << 6,
-       E_MAIL_REPLY_FLAG_FORCE_SENDER_REPLY    = 1 << 7
+       E_MAIL_REPLY_FLAG_FORCE_SENDER_REPLY    = 1 << 7,
+       E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN       = 1 << 8,
+       E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN = 1 << 9,
+       E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML  = 1 << 10
 } EMailReplyFlags;
 
 G_END_DECLS
diff --git a/src/mail/e-mail-notes.c b/src/mail/e-mail-notes.c
index a4fe577690..7a4a7589bf 100644
--- a/src/mail/e-mail-notes.c
+++ b/src/mail/e-mail-notes.c
@@ -30,6 +30,8 @@
 
 #include "e-mail-notes.h"
 
+#define E_MAIL_NOTES_FORMAT "X-Evolution-Format"
+
 #define E_TYPE_MAIL_NOTES_EDITOR \
        (e_mail_notes_editor_get_type ())
 #define E_MAIL_NOTES_EDITOR(obj) \
@@ -49,6 +51,7 @@ struct _EMailNotesEditor {
        EAttachmentPaned *attachment_paned; /* not referenced */
        EFocusTracker *focus_tracker;
        GtkActionGroup *action_group;
+       GBinding *attachment_paned_binding;
 
        gboolean had_message;
        CamelMimeMessage *message;
@@ -91,13 +94,54 @@ e_mail_notes_extract_text_content (CamelMimePart *part)
        return text;
 }
 
+static gboolean
+e_mail_notes_editor_extract_text_part (EHTMLEditor *editor,
+                                      CamelContentType *content_type,
+                                      CamelMimePart *part,
+                                      EContentEditorMode mode)
+{
+       guint32 insert_flag = E_CONTENT_EDITOR_INSERT_TEXT_PLAIN;
+       gchar *text;
+
+       if (camel_content_type_is (content_type, "text", "plain")) {
+               if (mode == E_CONTENT_EDITOR_MODE_UNKNOWN)
+                       mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+       } else if (camel_content_type_is (content_type, "text", "markdown")) {
+               mode = E_CONTENT_EDITOR_MODE_MARKDOWN;
+       } else if (camel_content_type_is (content_type, "text", "html")) {
+               mode = E_CONTENT_EDITOR_MODE_HTML;
+               insert_flag = E_CONTENT_EDITOR_INSERT_TEXT_HTML;
+       } else {
+               return FALSE;
+       }
+
+       text = e_mail_notes_extract_text_content (part);
+       if (text) {
+               e_html_editor_set_mode (editor, mode);
+
+               e_content_editor_insert_content (
+                       e_html_editor_get_content_editor (editor),
+                       text,
+                       insert_flag |
+                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
+
+               g_free (text);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
 static void
-e_mail_notes_extract_text_from_multipart_alternative (EContentEditor *cnt_editor,
-                                                     CamelMultipart *in_multipart)
+e_mail_notes_extract_text_from_multipart_alternative (EHTMLEditor *editor,
+                                                     CamelMultipart *in_multipart,
+                                                     EContentEditorMode mode)
 {
+       CamelMimePart *fallback_part = NULL;
        guint ii, nparts;
 
-       g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
        g_return_if_fail (CAMEL_IS_MULTIPART (in_multipart));
 
        nparts = camel_multipart_get_number (in_multipart);
@@ -115,40 +159,36 @@ e_mail_notes_extract_text_from_multipart_alternative (EContentEditor *cnt_editor
                if (!ct)
                        continue;
 
-               if (camel_content_type_is (ct, "text", "html")) {
-                       gchar *text;
-
-                       text = e_mail_notes_extract_text_content (part);
-                       if (text) {
-                               e_content_editor_set_html_mode (cnt_editor, TRUE);
-                               e_content_editor_insert_content (
-                                       cnt_editor,
-                                       text,
-                                       E_CONTENT_EDITOR_INSERT_TEXT_HTML |
-                                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
-                               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_content_editor_insert_content (
-                                       cnt_editor,
-                                       text,
-                                       E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
-                                       E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
-                               g_free (text);
+               if (mode == E_CONTENT_EDITOR_MODE_MARKDOWN ||
+                   mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT ||
+                   mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML) {
+                       /* Fallback to the text/html part when reading markdown text,
+                          to avoid conversion from HTML to markdown, because the text/plain
+                          part contains the raw markdown */
+                       if (camel_content_type_is (ct, "text", "html")) {
+                               fallback_part = part;
+                               continue;
                        }
+               }
+
+               if (e_mail_notes_editor_extract_text_part (editor, ct, part, mode)) {
+                       fallback_part = NULL;
                        break;
                }
        }
+
+       if (fallback_part) {
+               CamelContentType *ct;
+
+               ct = camel_mime_part_get_content_type (fallback_part);
+               e_mail_notes_editor_extract_text_part (editor, ct, fallback_part, mode);
+       }
 }
 
 static void
 e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes_editor,
-                                                        CamelMultipart *multipart)
+                                                        CamelMultipart *multipart,
+                                                        EContentEditorMode mode)
 {
        guint ii, nparts;
 
@@ -176,8 +216,8 @@ e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes
                        content = camel_medium_get_content (CAMEL_MEDIUM (part));
 
                        if (CAMEL_IS_MULTIPART (content)) {
-                               e_mail_notes_extract_text_from_multipart_alternative (
-                                       e_html_editor_get_content_editor (notes_editor->editor), 
CAMEL_MULTIPART (content));
+                               e_mail_notes_extract_text_from_multipart_alternative (notes_editor->editor,
+                                       CAMEL_MULTIPART (content), mode);
                        }
                }
        }
@@ -185,11 +225,11 @@ e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes
 
 static void
 e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
-                                           CamelMimePart *part)
+                                           CamelMimePart *part,
+                                           EContentEditorMode mode)
 {
        CamelContentType *ct;
        CamelDataWrapper *content;
-       EContentEditor *cnt_editor;
 
        g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
        g_return_if_fail (CAMEL_IS_MIME_PART (part));
@@ -200,28 +240,16 @@ e_mail_notes_editor_extract_text_from_part (EMailNotesEditor *notes_editor,
        g_return_if_fail (content != NULL);
        g_return_if_fail (ct != NULL);
 
-       cnt_editor = e_html_editor_get_content_editor (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));
+               e_mail_notes_editor_extract_text_from_multipart_related (notes_editor, CAMEL_MULTIPART 
(content), mode);
        } else if (camel_content_type_is (ct, "multipart", "alternative")) {
                if (CAMEL_IS_MULTIPART (content)) {
-                       e_mail_notes_extract_text_from_multipart_alternative (cnt_editor, 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_content_editor_insert_content (
-                               cnt_editor,
-                               text,
-                               E_CONTENT_EDITOR_INSERT_TEXT_PLAIN |
-                               E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
-                       g_free (text);
+                       e_mail_notes_extract_text_from_multipart_alternative (notes_editor->editor, 
CAMEL_MULTIPART (content), mode);
                }
+       } else {
+               e_mail_notes_editor_extract_text_part (notes_editor->editor, ct, part, mode);
        }
 }
 
@@ -231,7 +259,8 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
 {
        CamelContentType *ct;
        CamelDataWrapper *content;
-       EContentEditor *cnt_editor;
+       EContentEditorMode mode = E_CONTENT_EDITOR_MODE_UNKNOWN;
+       const gchar *format_header;
 
        g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
        g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
@@ -242,7 +271,14 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
        g_return_if_fail (content != NULL);
        g_return_if_fail (ct != NULL);
 
-       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
+       format_header = camel_medium_get_header (CAMEL_MEDIUM (message), E_MAIL_NOTES_FORMAT);
+
+       if (format_header) {
+               if (g_ascii_strcasecmp (format_header, "text/markdown-plain") == 0)
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+               else if (g_ascii_strcasecmp (format_header, "text/markdown-html") == 0)
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+       }
 
        if (camel_content_type_is (ct, "multipart", "mixed")) {
                EAttachmentStore *attachment_store;
@@ -268,7 +304,7 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
                                continue;
 
                        if (ii == 0) {
-                               e_mail_notes_editor_extract_text_from_part (notes_editor, part);
+                               e_mail_notes_editor_extract_text_from_part (notes_editor, part, mode);
                        } else {
                                EAttachment *attachment;
 
@@ -283,10 +319,10 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
                        }
                }
        } else {
-               e_mail_notes_editor_extract_text_from_part (notes_editor, CAMEL_MIME_PART (message));
+               e_mail_notes_editor_extract_text_from_part (notes_editor, CAMEL_MIME_PART (message), mode);
        }
 
-       e_content_editor_set_changed (cnt_editor, FALSE);
+       e_content_editor_set_changed (e_html_editor_get_content_editor (notes_editor->editor), FALSE);
 }
 
 static CamelMimeMessage *
@@ -294,6 +330,7 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor,
                                            EContentEditorContentHash *content_hash)
 {
        EContentEditor *cnt_editor;
+       EContentEditorMode mode;
        EAttachmentStore *attachment_store;
        CamelMimeMessage *message = NULL;
        gchar *message_uid;
@@ -328,7 +365,18 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor,
        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_content_editor_get_html_mode (cnt_editor)) {
+       mode = e_html_editor_get_mode (notes_editor->editor);
+
+       if (mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT ||
+           mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML) {
+               if (mode == E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT)
+                       camel_medium_add_header (CAMEL_MEDIUM (message), E_MAIL_NOTES_FORMAT, 
"text/markdown-plain");
+               else
+                       camel_medium_add_header (CAMEL_MEDIUM (message), E_MAIL_NOTES_FORMAT, 
"text/markdown-html");
+       }
+
+       if (mode == E_CONTENT_EDITOR_MODE_HTML ||
+           mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML) {
                CamelMultipart *multipart_alternative;
                CamelMultipart *multipart_body;
                CamelMimePart *part;
@@ -350,7 +398,8 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor,
                        }
 
                        part = camel_mime_part_new ();
-                       camel_mime_part_set_content (part, text, strlen (text), "text/plain");
+                       camel_mime_part_set_content (part, text, strlen (text), mode == 
E_CONTENT_EDITOR_MODE_MARKDOWN ?
+                               "text/markdown" : "text/plain");
                        camel_multipart_add_part (multipart_alternative, part);
 
                        g_object_unref (part);
@@ -463,7 +512,8 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor,
                                camel_multipart_set_boundary (multipart, NULL);
 
                                part = camel_mime_part_new ();
-                               camel_mime_part_set_content (part, text, strlen (text), "text/plain");
+                               camel_mime_part_set_content (part, text, strlen (text), mode == 
E_CONTENT_EDITOR_MODE_MARKDOWN ?
+                                       "text/markdown" : "text/plain");
                                camel_multipart_add_part (multipart, part);
                                g_object_unref (part);
 
@@ -473,7 +523,8 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor,
 
                                g_object_unref (multipart);
                        } else {
-                               camel_mime_part_set_content (CAMEL_MIME_PART (message), text, strlen (text), 
"text/plain");
+                               camel_mime_part_set_content (CAMEL_MIME_PART (message), text, strlen (text), 
mode == E_CONTENT_EDITOR_MODE_MARKDOWN ?
+                                       "text/markdown" : "text/plain");
                        }
 
                        has_text = TRUE;
@@ -587,17 +638,18 @@ mail_notes_editor_delete_event_cb (EMailNotesEditor *notes_editor,
 }
 
 static void
-notes_editor_activity_notify_cb (EActivityBar *activity_bar,
-                                GParamSpec *param,
-                                EMailNotesEditor *notes_editor)
+notes_editor_update_editable_on_notify_cb (GObject *object,
+                                          GParamSpec *param,
+                                          EMailNotesEditor *notes_editor)
 {
+       EActivityBar *activity_bar;
        EContentEditor *cnt_editor;
        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));
 
+       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
        cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
        can_edit = notes_editor->had_message && !e_activity_bar_get_activity (activity_bar);
 
@@ -919,6 +971,40 @@ action_save_and_close_cb (GtkAction *action,
                mail_notes_get_content_ready_cb, scd);
 }
 
+static void
+notes_editor_notify_mode_cb (GObject *object,
+                            GParamSpec *param,
+                            EMailNotesEditor *notes_editor)
+{
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+
+       if (notes_editor->attachment_paned_binding) {
+               g_binding_unbind (notes_editor->attachment_paned_binding);
+               g_clear_object (&notes_editor->attachment_paned_binding);
+       }
+
+       if (notes_editor->editor) {
+               EContentEditor *cnt_editor;
+
+               cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
+
+               if (cnt_editor) {
+                       EActivityBar *activity_bar;
+                       gboolean can_edit;
+
+                       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
+                       can_edit = notes_editor->had_message && !e_activity_bar_get_activity (activity_bar);
+
+                       g_object_set (cnt_editor, "editable", can_edit, NULL);
+
+                       notes_editor->attachment_paned_binding = g_object_ref (e_binding_bind_property (
+                               cnt_editor, "editable",
+                               notes_editor->attachment_paned, "sensitive",
+                               G_BINDING_SYNC_CREATE));
+               }
+       }
+}
+
 static void
 e_mail_notes_editor_dispose (GObject *object)
 {
@@ -929,13 +1015,14 @@ e_mail_notes_editor_dispose (GObject *object)
 
                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);
+                       G_CALLBACK (notes_editor_update_editable_on_notify_cb), notes_editor);
 
                notes_editor->editor = NULL;
        }
 
        g_clear_object (&notes_editor->focus_tracker);
        g_clear_object (&notes_editor->action_group);
+       g_clear_object (&notes_editor->attachment_paned_binding);
 
        /* Chain up to parent's method */
        G_OBJECT_CLASS (e_mail_notes_editor_parent_class)->dispose (object);
@@ -974,9 +1061,12 @@ static void
 set_preformatted_block_format_on_load_finished_cb (EContentEditor *cnt_editor,
                                                   gpointer user_data)
 {
+       EHTMLEditor *editor = user_data;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
        g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
 
-       if (!e_content_editor_get_html_mode (cnt_editor)) {
+       if (e_html_editor_get_mode (editor) == E_CONTENT_EDITOR_MODE_PLAIN_TEXT) {
                e_content_editor_set_block_format (cnt_editor, E_CONTENT_EDITOR_BLOCK_FORMAT_PRE);
                e_content_editor_set_changed (cnt_editor, FALSE);
                e_content_editor_clear_undo_redo_history (cnt_editor);
@@ -1113,35 +1203,25 @@ e_mail_notes_editor_new_with_editor (EHTMLEditor *html_editor,
        notes_editor->attachment_paned = E_ATTACHMENT_PANED (widget);
        gtk_widget_show (widget);
 
-       e_binding_bind_property (
+       notes_editor->attachment_paned_binding = g_object_ref (e_binding_bind_property (
                cnt_editor, "editable",
                widget, "sensitive",
-               G_BINDING_SYNC_CREATE);
+               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);
+       e_html_editor_connect_focus_tracker (notes_editor->editor, focus_tracker);
 
        notes_editor->focus_tracker = focus_tracker;
 
        gtk_widget_grab_focus (GTK_WIDGET (cnt_editor));
 
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
-       e_content_editor_set_html_mode (cnt_editor, g_settings_get_boolean (settings, "composer-send-html"));
+       e_html_editor_set_mode (html_editor, g_settings_get_enum (settings, "composer-mode"));
        if (g_settings_get_boolean (settings, "composer-plain-text-starts-preformatted")) {
-               g_signal_connect (cnt_editor, "load-finished",
-                       G_CALLBACK (set_preformatted_block_format_on_load_finished_cb), NULL);
+               g_signal_connect_object (cnt_editor, "load-finished",
+                       G_CALLBACK (set_preformatted_block_format_on_load_finished_cb), html_editor, 0);
        }
        g_object_unref (settings);
 
@@ -1152,7 +1232,9 @@ e_mail_notes_editor_new_with_editor (EHTMLEditor *html_editor,
        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);
+               G_CALLBACK (notes_editor_update_editable_on_notify_cb), notes_editor);
+       g_signal_connect_object (notes_editor->editor, "notify::mode",
+               G_CALLBACK (notes_editor_notify_mode_cb), notes_editor, 0);
 
        notes_editor->folder = g_object_ref (folder);
        notes_editor->uid = g_strdup (uid);
diff --git a/src/mail/em-composer-utils.c b/src/mail/em-composer-utils.c
index 33d29d99ef..ebd6df7a26 100644
--- a/src/mail/em-composer-utils.c
+++ b/src/mail/em-composer-utils.c
@@ -552,8 +552,8 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
 {
        EDestination **recipients;
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
        EComposerHeaderTable *table;
+       EContentEditorMode mode;
        GSettings *settings;
        gboolean check_passed = TRUE;
        gboolean html_mode;
@@ -564,13 +564,14 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
        settings = e_util_ref_settings ("org.gnome.evolution.mail");
 
        editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
-       html_mode = e_content_editor_get_html_mode (cnt_editor);
+       mode = e_html_editor_get_mode (editor);
+       html_mode = mode == E_CONTENT_EDITOR_MODE_HTML || mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
 
        table = e_msg_composer_get_header_table (composer);
        recipients = e_composer_header_table_get_destinations (table);
 
-       send_html = g_settings_get_boolean (settings, "composer-send-html");
+       mode = g_settings_get_enum (settings, "composer-mode");
+       send_html = mode == E_CONTENT_EDITOR_MODE_HTML || mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
        confirm_html = g_settings_get_boolean (settings, "prompt-on-unwanted-html");
 
        /* Only show this warning if our default is to send html.  If it
@@ -2582,7 +2583,7 @@ forward_non_attached (EMsgComposer *composer,
                E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG;
        if (style == E_MAIL_FORWARD_STYLE_QUOTED)
                flags |= E_MAIL_FORMATTER_QUOTE_FLAG_CITE;
-       if (!e_content_editor_get_html_mode (e_html_editor_get_content_editor (e_msg_composer_get_editor 
(composer))))
+       if (e_html_editor_get_mode (e_msg_composer_get_editor (composer)) != E_CONTENT_EDITOR_MODE_HTML)
                flags |= E_MAIL_FORMATTER_QUOTE_FLAG_NO_FORMATTING;
 
        /* Setup composer's From account before calling quoting_text() and
@@ -3855,9 +3856,9 @@ alt_reply_composer_created_cb (GObject *source_object,
        composer = e_msg_composer_new_finish (result, &error);
 
        if (composer) {
-               EContentEditor *cnt_editor;
+               EHTMLEditor *editor;
 
-               cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer));
+               editor = e_msg_composer_get_editor (composer);
 
                if (context->new_message) {
                        CamelInternetAddress *to = NULL, *cc = NULL;
@@ -3873,7 +3874,7 @@ alt_reply_composer_created_cb (GObject *source_object,
                        }
 
                        if ((context->flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | 
E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) {
-                               e_content_editor_set_html_mode (cnt_editor, (context->flags & 
E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0);
+                               e_html_editor_set_mode (editor, (context->flags & 
E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0 ? E_CONTENT_EDITOR_MODE_HTML : E_CONTENT_EDITOR_MODE_PLAIN_TEXT);
                        }
 
                        em_utils_edit_message (composer, context->folder, context->new_message, 
context->message_uid, TRUE, FALSE);
@@ -4110,7 +4111,8 @@ em_utils_reply_alternative (GtkWindow *parent,
        GtkRadioButton *recip_sender, *recip_list, *recip_all;
        GtkLabel *sender_label, *list_label, *all_label;
        GtkRadioButton *style_default, *style_attach, *style_inline, *style_quote, *style_no_quote;
-       GtkToggleButton *html_format;
+       EActionComboBox *mode_combo;
+       GtkRadioAction *radio_action, *html_mode_radio_action;
        GtkToggleButton *bottom_posting;
        GtkToggleButton *top_signature;
        GtkCheckButton *apply_template;
@@ -4226,8 +4228,21 @@ em_utils_reply_alternative (GtkWindow *parent,
        widget = gtk_label_new (" ");
        gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0);
 
-       html_format = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Format message in HTML")));
-       gtk_box_pack_start (vbox, GTK_WIDGET (html_format), FALSE, FALSE, 0);
+       hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6));
+       gtk_box_pack_start (vbox, GTK_WIDGET (hbox), FALSE, FALSE, 0);
+
+       /* Translators: The text is followed by the format combo with values like 'Plain Text', 'HTML' and so 
on */
+       widget = gtk_label_new_with_mnemonic (_("_Format message in"));
+       gtk_box_pack_start (hbox, widget, FALSE, FALSE, 0);
+
+       mode_combo = e_html_editor_util_new_mode_combobox ();
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (mode_combo));
+       gtk_box_pack_start (hbox, GTK_WIDGET (mode_combo), FALSE, FALSE, 0);
+
+       html_mode_radio_action = e_action_combo_box_get_action (mode_combo);
+       radio_action = gtk_radio_action_new ("unknown", _("Use global setting"), NULL, NULL, 
E_CONTENT_EDITOR_MODE_UNKNOWN);
+       gtk_radio_action_join_group (radio_action, html_mode_radio_action);
+       e_action_combo_box_update_model (mode_combo);
 
        bottom_posting = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Start _typing at the 
bottom")));
        gtk_box_pack_start (vbox, GTK_WIDGET (bottom_posting), FALSE, FALSE, 0);
@@ -4366,7 +4381,7 @@ em_utils_reply_alternative (GtkWindow *parent,
                break;
        }
 
-       emcu_three_state_set_value (html_format, g_settings_get_enum (settings, "alt-reply-html-format"));
+       e_action_combo_box_set_current_value (mode_combo, g_settings_get_enum (settings, 
"alt-reply-format-mode"));
        emcu_three_state_set_value (bottom_posting, g_settings_get_enum (settings, "alt-reply-start-bottom"));
        emcu_three_state_set_value (top_signature, g_settings_get_enum (settings, "alt-reply-top-signature"));
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), g_settings_get_boolean (settings, 
"alt-reply-template-apply"));
@@ -4375,7 +4390,6 @@ em_utils_reply_alternative (GtkWindow *parent,
        if (!gtk_widget_get_sensitive (GTK_WIDGET (templates)))
                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), FALSE);
 
-       emcu_connect_three_state_changer (html_format);
        emcu_connect_three_state_changer (bottom_posting);
        emcu_connect_three_state_changer (top_signature);
 
@@ -4434,6 +4448,7 @@ em_utils_reply_alternative (GtkWindow *parent,
        if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
                GtkTreeIter iter;
                AltReplyContext *context;
+               EContentEditorMode mode;
                EThreeState three_state;
                CamelFolder *template_folder = NULL;
                gchar *template_message_uid = NULL;
@@ -4461,13 +4476,28 @@ em_utils_reply_alternative (GtkWindow *parent,
                else
                        context->flags = context->flags & (~E_MAIL_REPLY_FLAG_FORCE_STYLE);
 
-               three_state = emcu_three_state_get_value (html_format);
-               g_settings_set_enum (settings, "alt-reply-html-format", three_state);
+               mode = e_action_combo_box_get_current_value (mode_combo);
+               g_settings_set_enum (settings, "alt-reply-format-mode", mode);
 
-               if (three_state == E_THREE_STATE_ON)
-                       context->flags |= E_MAIL_REPLY_FLAG_FORMAT_HTML;
-               else if (three_state == E_THREE_STATE_OFF)
+               switch (mode) {
+               case E_CONTENT_EDITOR_MODE_UNKNOWN:
+                       break;
+               case E_CONTENT_EDITOR_MODE_PLAIN_TEXT:
                        context->flags |= E_MAIL_REPLY_FLAG_FORMAT_PLAIN;
+                       break;
+               case E_CONTENT_EDITOR_MODE_HTML:
+                       context->flags |= E_MAIL_REPLY_FLAG_FORMAT_HTML;
+                       break;
+               case E_CONTENT_EDITOR_MODE_MARKDOWN:
+                       context->flags |= E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN;
+                       break;
+               case E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT:
+                       context->flags |= E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN;
+                       break;
+               case E_CONTENT_EDITOR_MODE_MARKDOWN_HTML:
+                       context->flags |= E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML;
+                       break;
+               }
 
                three_state = emcu_three_state_get_value (bottom_posting);
                g_settings_set_enum (settings, "alt-reply-start-bottom", three_state);
@@ -4597,8 +4627,34 @@ em_utils_reply_to_message (EMsgComposer *composer,
 
        cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer));
 
-       if ((reply_flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) {
-               e_content_editor_set_html_mode (cnt_editor, (reply_flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 
0);
+       flags = reply_flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN |
+                               E_MAIL_REPLY_FLAG_FORMAT_HTML |
+                               E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN |
+                               E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN |
+                               E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML);
+       if (flags != 0) {
+               EContentEditorMode mode = E_CONTENT_EDITOR_MODE_UNKNOWN;
+
+               switch (flags) {
+               case E_MAIL_REPLY_FLAG_FORMAT_PLAIN:
+                       mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+                       break;
+               case E_MAIL_REPLY_FLAG_FORMAT_HTML:
+                       mode = E_CONTENT_EDITOR_MODE_HTML;
+                       break;
+               case E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN:
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN;
+                       break;
+               case E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_PLAIN:
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT;
+                       break;
+               case E_MAIL_REPLY_FLAG_FORMAT_MARKDOWN_HTML:
+                       mode = E_CONTENT_EDITOR_MODE_MARKDOWN_HTML;
+                       break;
+               }
+
+               if (mode != E_CONTENT_EDITOR_MODE_UNKNOWN)
+                       e_html_editor_set_mode (e_msg_composer_get_editor (composer), mode);
        }
 
        em_utils_update_by_reply_flags (cnt_editor, reply_flags);
diff --git a/src/mail/mail-config.ui b/src/mail/mail-config.ui
index 890837162a..646909865b 100644
--- a/src/mail/mail-config.ui
+++ b/src/mail/mail-config.ui
@@ -211,14 +211,26 @@
                     <property name="can_focus">False</property>
                     <property name="spacing">8</property>
                     <child>
-                      <object class="GtkCheckButton" id="chkSendHTML">
-                        <property name="label" translatable="yes">For_mat messages in HTML</property>
+                      <object class="GtkBox" id="hboxSendMode">
+                        <property name="orientation">horizontal</property>
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">False</property>
-                        <property name="use_underline">True</property>
-                        <property name="xalign">0.5</property>
-                        <property name="draw_indicator">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">8</property>
+                        <child>
+                          <object class="GtkLabel" id="lblSendMode">
+                            <property name="label" translatable="yes" comments="Translators: The text is 
followed by the format combo with values like 'Plain Text', 'HTML' and so on">For_mat messages in</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="use_underline">True</property>
+                            <property name="justify">center</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
diff --git a/src/modules/composer-to-meeting/e-meeting-to-composer.c 
b/src/modules/composer-to-meeting/e-meeting-to-composer.c
index 0335a9cdde..63437521d7 100644
--- a/src/modules/composer-to-meeting/e-meeting-to-composer.c
+++ b/src/modules/composer-to-meeting/e-meeting-to-composer.c
@@ -298,11 +298,22 @@ meeting_to_composer_composer_created_cb (GObject *source_object,
                if (text && *text) {
                        EHTMLEditor *html_editor;
                        EContentEditor *cnt_editor;
+                       EContentEditorMode mode;
+                       GSettings *settings;
+
+                       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+                       mode = g_settings_get_enum (settings, "composer-mode");
+                       g_clear_object (&settings);
+
+                       /* Let the markdown be allowed, otherwise use the plain text mode */
+                       if (mode != E_CONTENT_EDITOR_MODE_MARKDOWN &&
+                           mode != E_CONTENT_EDITOR_MODE_MARKDOWN_PLAIN_TEXT)
+                               mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
 
                        html_editor = e_msg_composer_get_editor (composer);
                        cnt_editor = e_html_editor_get_content_editor (html_editor);
 
-                       e_content_editor_set_html_mode (cnt_editor, FALSE);
+                       e_html_editor_set_mode (html_editor, mode);
                        e_content_editor_insert_content (cnt_editor, text, 
E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
                }
 
diff --git a/src/modules/mail/e-mail-shell-backend.c b/src/modules/mail/e-mail-shell-backend.c
index 0cb48eb40e..2e393baa2e 100644
--- a/src/modules/mail/e-mail-shell-backend.c
+++ b/src/modules/mail/e-mail-shell-backend.c
@@ -720,9 +720,12 @@ static void
 set_preformatted_block_format_on_load_finished_cb (EContentEditor *cnt_editor,
                                                   gpointer user_data)
 {
+       EHTMLEditor *editor = user_data;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
        g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
 
-       if (!e_content_editor_get_html_mode (cnt_editor)) {
+       if (e_html_editor_get_mode (editor) != E_CONTENT_EDITOR_MODE_HTML) {
                e_content_editor_set_block_format (cnt_editor, E_CONTENT_EDITOR_BLOCK_FORMAT_PRE);
                e_content_editor_set_changed (cnt_editor, FALSE);
                e_content_editor_clear_undo_redo_history (cnt_editor);
@@ -756,23 +759,24 @@ mail_shell_backend_window_added_cb (GtkApplication *application,
        /* This applies to both the composer and signature editor. */
        if (editor != NULL) {
                EContentEditor *cnt_editor;
+               EContentEditorMode mode;
                GSettings *settings;
-               gboolean use_html, use_preformatted;
+               gboolean use_preformatted;
 
                cnt_editor = e_html_editor_get_content_editor (editor);
 
                settings = e_util_ref_settings ("org.gnome.evolution.mail");
 
-               use_html = g_settings_get_boolean (settings, "composer-send-html");
+               mode = g_settings_get_enum (settings, "composer-mode");
                use_preformatted = g_settings_get_boolean (settings, 
"composer-plain-text-starts-preformatted");
 
                g_object_unref (settings);
 
-               e_content_editor_set_html_mode (cnt_editor, use_html);
+               e_html_editor_set_mode (editor, mode);
 
                if (use_preformatted) {
-                       g_signal_connect (cnt_editor, "load-finished",
-                               G_CALLBACK (set_preformatted_block_format_on_load_finished_cb), NULL);
+                       g_signal_connect_object (cnt_editor, "load-finished",
+                               G_CALLBACK (set_preformatted_block_format_on_load_finished_cb), editor, 0);
                }
        }
 
diff --git a/src/modules/mail/em-composer-prefs.c b/src/modules/mail/em-composer-prefs.c
index 721c07e6e2..24b6734a80 100644
--- a/src/modules/mail/em-composer-prefs.c
+++ b/src/modules/mail/em-composer-prefs.c
@@ -1056,6 +1056,45 @@ emcp_free (EConfig *ec,
        g_slist_free (items);
 }
 
+static gboolean
+emcp_composer_mode_to_current_value_cb (GValue *value,
+                                       GVariant *variant,
+                                       gpointer user_data)
+{
+       const gchar *str;
+       gint mode = E_CONTENT_EDITOR_MODE_UNKNOWN;
+
+       str = g_variant_get_string (variant, NULL);
+
+       if (!e_enum_from_string (E_TYPE_CONTENT_EDITOR_MODE, str, &mode))
+               mode = E_CONTENT_EDITOR_MODE_UNKNOWN;
+
+       if (mode == E_CONTENT_EDITOR_MODE_UNKNOWN)
+               mode = E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
+
+       g_value_set_int (value, mode);
+
+       return TRUE;
+}
+
+static GVariant *
+emcp_current_value_to_composer_mode_cb (const GValue *value,
+                                       const GVariantType *expected_type,
+                                       gpointer user_data)
+{
+       const gchar *str;
+       gint mode;
+
+       mode = g_value_get_int (value);
+
+       str = e_enum_to_string (E_TYPE_CONTENT_EDITOR_MODE, mode);
+
+       if (!str)
+               str = e_enum_to_string (E_TYPE_CONTENT_EDITOR_MODE, E_CONTENT_EDITOR_MODE_PLAIN_TEXT);
+
+       return g_variant_new_string (str);
+}
+
 static void
 em_composer_prefs_construct (EMComposerPrefs *prefs,
                              EShell *shell)
@@ -1063,7 +1102,9 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
        GtkWidget *toplevel, *widget, *info_pixmap;
        GtkWidget *container;
        GSettings *settings;
+       EActionComboBox *action_combo_box;
        ESourceRegistry *registry;
+       GtkRadioAction *radio_action;
        GtkTreeView *view;
        GtkListStore *store;
        GtkTreeSelection *selection;
@@ -1106,11 +1147,21 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
 
        /* Default Behavior */
 
-       widget = e_builder_get_widget (prefs->builder, "chkSendHTML");
-       g_settings_bind (
-               settings, "composer-send-html",
-               widget, "active",
-               G_SETTINGS_BIND_DEFAULT);
+       container = e_builder_get_widget (prefs->builder, "hboxSendMode");
+       action_combo_box = e_html_editor_util_new_mode_combobox ();
+       widget = GTK_WIDGET (action_combo_box);
+       gtk_widget_show (widget);
+       gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+       widget = e_builder_get_widget (prefs->builder, "lblSendMode");
+       gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (action_combo_box));
+       radio_action = e_action_combo_box_get_action (action_combo_box);
+       g_settings_bind_with_mapping (
+               settings, "composer-mode",
+               radio_action, "current-value",
+               G_SETTINGS_BIND_DEFAULT,
+               emcp_composer_mode_to_current_value_cb,
+               emcp_current_value_to_composer_mode_cb,
+               NULL, NULL);
 
        widget = e_builder_get_widget (prefs->builder, "chkInheritThemeColors");
        g_settings_bind (
@@ -1359,8 +1410,8 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
                G_CALLBACK (gtk_application_add_window), shell);
 
        g_settings_bind (
-               settings, "composer-send-html",
-               widget, "prefer-html",
+               settings, "composer-mode",
+               widget, "prefer-mode",
                G_SETTINGS_BIND_GET);
 
        /* Send Account override */
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 1042cc1bcd..0fc8b47afa 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -44,7 +44,7 @@ enum {
        PROP_CAN_UNDO,
        PROP_CHANGED,
        PROP_EDITABLE,
-       PROP_HTML_MODE,
+       PROP_MODE,
        PROP_SPELL_CHECK_ENABLED,
        PROP_SPELL_CHECKER,
        PROP_START_BOTTOM,
@@ -82,7 +82,7 @@ struct _EWebKitEditorPrivate {
        GHashTable *scheme_handlers; /* const gchar *scheme ~> EContentRequest */
        GCancellable *cancellable;
 
-       gboolean html_mode;
+       EContentEditorMode mode;
        gboolean changed;
        gboolean can_copy;
        gboolean can_cut;
@@ -545,7 +545,7 @@ webkit_editor_dialog_utils_get_attribute_with_unit (EWebKitEditor *wk_editor,
 
        *out_unit = E_CONTENT_EDITOR_UNIT_AUTO;
 
-       if (!wk_editor->priv->html_mode)
+       if (wk_editor->priv->mode != E_CONTENT_EDITOR_MODE_HTML)
                return default_value;
 
        value = webkit_editor_dialog_utils_get_attribute (wk_editor, selector, name);
@@ -596,29 +596,6 @@ webkit_editor_dialog_utils_has_attribute (EWebKitEditor *wk_editor,
                FALSE);
 }
 
-static gboolean
-e_webkit_editor_three_state_to_bool (EThreeState value,
-                                    const gchar *mail_key)
-{
-       gboolean res = FALSE;
-
-       if (value == E_THREE_STATE_ON)
-               return TRUE;
-
-       if (value == E_THREE_STATE_OFF)
-               return FALSE;
-
-       if (mail_key && *mail_key) {
-               GSettings *settings;
-
-               settings = e_util_ref_settings ("org.gnome.evolution.mail");
-               res = g_settings_get_boolean (settings, mail_key);
-               g_clear_object (&settings);
-       }
-
-       return res;
-}
-
 EWebKitEditor *
 e_webkit_editor_new (void)
 {
@@ -838,8 +815,8 @@ formatting_changed_cb (WebKitUserContentManager *manager,
        if (jsc_value && jsc_value_is_number (jsc_value)) {
                gint value = jsc_value_to_int32 (jsc_value);
 
-               if ((value ? 1 : 0) != (wk_editor->priv->html_mode ? 1 : 0)) {
-                       wk_editor->priv->html_mode = value;
+               if ((value ? 1 : 0) != (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML ? 1 : 0)) {
+                       wk_editor->priv->mode = value ? E_CONTENT_EDITOR_MODE_HTML : 
E_CONTENT_EDITOR_MODE_PLAIN_TEXT;
                        changed = TRUE;
                }
        }
@@ -850,7 +827,7 @@ formatting_changed_cb (WebKitUserContentManager *manager,
                webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor));
                webkit_editor_style_updated (wk_editor, FALSE);
 
-               g_object_notify (object, "html-mode");
+               g_object_notify (object, "mode");
        }
 
        changed = FALSE;
@@ -1135,6 +1112,16 @@ webkit_editor_show_inspector (EWebKitEditor *wk_editor)
        webkit_web_inspector_show (inspector);
 }
 
+static gboolean
+webkit_editor_supports_mode (EContentEditor *content_editor,
+                            EContentEditorMode mode)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (content_editor), FALSE);
+
+       return mode == E_CONTENT_EDITOR_MODE_PLAIN_TEXT ||
+               mode == E_CONTENT_EDITOR_MODE_HTML;
+}
+
 static void
 webkit_editor_initialize (EContentEditor *content_editor,
                           EContentEditorInitializedCallback callback,
@@ -1194,7 +1181,7 @@ webkit_editor_update_styles (EContentEditor *editor)
                ms = pango_font_description_from_string ("monospace 10");
        }
 
-       if (wk_editor->priv->html_mode) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML) {
                if (use_custom_font) {
                        font = g_settings_get_string (
                                wk_editor->priv->mail_settings, "variable-width-font");
@@ -1315,7 +1302,7 @@ webkit_editor_update_styles (EContentEditor *editor)
                "  vertical-align: top;\n"
                "}\n");
 
-       if (wk_editor->priv->html_mode) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML) {
                g_string_append (
                        stylesheet,
                        "body ul > li.-x-evo-align-center,ol > li.-x-evo-align-center "
@@ -1564,7 +1551,7 @@ webkit_editor_update_styles (EContentEditor *editor)
                "  -webkit-margin-after: 0em; \n"
                "}\n");
 
-       if (wk_editor->priv->html_mode) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML) {
                g_string_append (
                        stylesheet,
                        "a "
@@ -1715,7 +1702,7 @@ webkit_editor_page_get_text_color (EContentEditor *editor,
 {
        EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (wk_editor->priv->html_mode &&
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML &&
            wk_editor->priv->body_fg_color) {
                *color = *wk_editor->priv->body_fg_color;
        } else {
@@ -1736,7 +1723,7 @@ webkit_editor_page_get_background_color (EContentEditor *editor,
 {
        EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (wk_editor->priv->html_mode &&
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML &&
            wk_editor->priv->body_bg_color) {
                *color = *wk_editor->priv->body_bg_color;
        } else {
@@ -1757,7 +1744,7 @@ webkit_editor_page_get_link_color (EContentEditor *editor,
 {
        EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (wk_editor->priv->html_mode &&
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML &&
            wk_editor->priv->body_link_color) {
                *color = *wk_editor->priv->body_link_color;
        } else {
@@ -1781,7 +1768,7 @@ webkit_editor_page_get_visited_link_color (EContentEditor *editor,
 {
        EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (wk_editor->priv->html_mode &&
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML &&
            wk_editor->priv->body_vlink_color) {
                *color = *wk_editor->priv->body_vlink_color;
        } else {
@@ -1808,7 +1795,7 @@ webkit_editor_page_get_font_name (EContentEditor *editor)
 {
        EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (!wk_editor->priv->html_mode)
+       if (wk_editor->priv->mode != E_CONTENT_EDITOR_MODE_HTML)
                return NULL;
 
        return wk_editor->priv->body_font_name;
@@ -1871,7 +1858,7 @@ webkit_editor_style_updated (EWebKitEditor *wk_editor,
        style_context = gtk_widget_get_style_context (GTK_WIDGET (wk_editor));
        backdrop = (state_flags & GTK_STATE_FLAG_BACKDROP) != 0;
 
-       if (wk_editor->priv->html_mode && !inherit_theme_colors) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML && !inherit_theme_colors) {
                /* Default to white background when not inheriting theme colors */
                bgcolor.red = 1.0;
                bgcolor.green = 1.0;
@@ -1884,7 +1871,7 @@ webkit_editor_style_updated (EWebKitEditor *wk_editor,
                gdk_rgba_parse (&bgcolor, E_UTILS_DEFAULT_THEME_BASE_COLOR);
        }
 
-       if (wk_editor->priv->html_mode && !inherit_theme_colors) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML && !inherit_theme_colors) {
                /* Default to black text color when not inheriting theme colors */
                fgcolor.red = 0.0;
                fgcolor.green = 0.0;
@@ -1942,10 +1929,10 @@ webkit_editor_style_updated_cb (EWebKitEditor *wk_editor)
        webkit_editor_style_updated (wk_editor, FALSE);
 }
 
-static gboolean
-webkit_editor_get_html_mode (EWebKitEditor *wk_editor)
+static EContentEditorMode
+webkit_editor_get_mode (EWebKitEditor *wk_editor)
 {
-       return wk_editor->priv->html_mode;
+       return wk_editor->priv->mode;
 }
 
 static gboolean
@@ -1966,7 +1953,7 @@ show_lose_formatting_dialog (EWebKitEditor *wk_editor)
 
        if (!lose) {
                /* Nothing has changed, but notify anyway */
-               g_object_notify (G_OBJECT (wk_editor), "html-mode");
+               g_object_notify (G_OBJECT (wk_editor), "mode");
                return FALSE;
        }
 
@@ -1974,17 +1961,18 @@ show_lose_formatting_dialog (EWebKitEditor *wk_editor)
 }
 
 static void
-webkit_editor_set_html_mode (EWebKitEditor *wk_editor,
-                             gboolean html_mode)
+webkit_editor_set_mode (EWebKitEditor *wk_editor,
+                       EContentEditorMode mode)
 {
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (mode == E_CONTENT_EDITOR_MODE_PLAIN_TEXT || mode == E_CONTENT_EDITOR_MODE_HTML);
 
-       if (html_mode == wk_editor->priv->html_mode)
+       if (mode == wk_editor->priv->mode)
                return;
 
-       wk_editor->priv->html_mode = html_mode;
+       wk_editor->priv->mode = mode;
 
-       if (html_mode) {
+       if (mode == E_CONTENT_EDITOR_MODE_HTML) {
                e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
                        "EvoEditor.SetMode(EvoEditor.MODE_HTML);");
        } else {
@@ -2041,11 +2029,11 @@ webkit_editor_insert_content (EContentEditor *editor,
                }
 
                /* Only convert messages that are in HTML */
-               if (!(wk_editor->priv->html_mode)) {
+               if (wk_editor->priv->mode != E_CONTENT_EDITOR_MODE_HTML) {
                        if (strstr (content, "<!-- text/html -->") &&
                            !strstr (content, "<!-- disable-format-prompt -->")) {
                                if (!show_lose_formatting_dialog (wk_editor)) {
-                                       webkit_editor_set_html_mode (wk_editor, TRUE);
+                                       webkit_editor_set_mode (wk_editor, E_CONTENT_EDITOR_MODE_HTML);
                                        e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), 
wk_editor->priv->cancellable,
                                                "EvoEditor.LoadHTML(%s);", content);
                                        if (cleanup_sig_id)
@@ -2467,7 +2455,7 @@ webkit_editor_set_start_bottom (EWebKitEditor *wk_editor,
 
        e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
                "EvoEditor.START_BOTTOM = %x;",
-               e_webkit_editor_three_state_to_bool (value, "composer-reply-start-bottom"));
+               e_content_editor_util_three_state_to_bool (value, "composer-reply-start-bottom"));
 
        g_object_notify (G_OBJECT (wk_editor), "start-bottom");
 }
@@ -2595,7 +2583,7 @@ webkit_editor_get_current_signature_uid (EContentEditor *editor)
 static gchar *
 webkit_editor_insert_signature (EContentEditor *editor,
                                 const gchar *content,
-                                gboolean is_html,
+                                EContentEditorMode editor_mode,
                                gboolean can_reposition_caret,
                                 const gchar *signature_id,
                                 gboolean *set_signature_from_message,
@@ -2607,8 +2595,12 @@ webkit_editor_insert_signature (EContentEditor *editor,
 
        g_return_val_if_fail (E_IS_WEBKIT_EDITOR (editor), NULL);
 
-       if (!is_html && content && *content) {
-               tmp = camel_text_to_html (content, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
+       if (editor_mode != E_CONTENT_EDITOR_MODE_HTML && content && *content) {
+               if (editor_mode == E_CONTENT_EDITOR_MODE_MARKDOWN_HTML)
+                       tmp = e_markdown_utils_text_to_html (content, -1);
+
+               if (!tmp)
+                       tmp = camel_text_to_html (content, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
 
                if (tmp)
                        content = tmp;
@@ -2617,15 +2609,15 @@ webkit_editor_insert_signature (EContentEditor *editor,
        jsc_value = webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor),
                "EvoEditor.InsertSignature(%s, %x, %x, %s, %x, %x, %x, %x, %x, %x);",
                content ? content : "",
-               is_html,
+               editor_mode == E_CONTENT_EDITOR_MODE_HTML,
                can_reposition_caret,
                signature_id,
                *set_signature_from_message,
                *check_if_signature_is_changed,
                *ignore_next_signature_change,
-               e_webkit_editor_three_state_to_bool (e_content_editor_get_start_bottom (editor), 
"composer-reply-start-bottom"),
-               e_webkit_editor_three_state_to_bool (e_content_editor_get_top_signature (editor), 
"composer-top-signature"),
-               !e_webkit_editor_three_state_to_bool (E_THREE_STATE_INCONSISTENT, 
"composer-no-signature-delim"));
+               e_content_editor_util_three_state_to_bool (e_content_editor_get_start_bottom (editor), 
"composer-reply-start-bottom"),
+               e_content_editor_util_three_state_to_bool (e_content_editor_get_top_signature (editor), 
"composer-top-signature"),
+               !e_content_editor_util_three_state_to_bool (E_THREE_STATE_INCONSISTENT, 
"composer-no-signature-delim"));
 
        g_free (tmp);
 
@@ -3477,7 +3469,7 @@ webkit_editor_get_font_name (EWebKitEditor *wk_editor)
 {
        g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
 
-       if (!wk_editor->priv->html_mode)
+       if (wk_editor->priv->mode != E_CONTENT_EDITOR_MODE_HTML)
                return NULL;
 
        return wk_editor->priv->font_name;
@@ -3506,7 +3498,7 @@ webkit_editor_get_font_color (EWebKitEditor *wk_editor)
 {
        g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
 
-       if (!wk_editor->priv->html_mode || !wk_editor->priv->font_color)
+       if (wk_editor->priv->mode != E_CONTENT_EDITOR_MODE_HTML || !wk_editor->priv->font_color)
                return &black;
 
        return wk_editor->priv->font_color;
@@ -4474,10 +4466,10 @@ webkit_editor_set_property (GObject *object,
                                g_value_get_boolean (value));
                        return;
 
-               case PROP_HTML_MODE:
-                       webkit_editor_set_html_mode (
+               case PROP_MODE:
+                       webkit_editor_set_mode (
                                E_WEBKIT_EDITOR (object),
-                               g_value_get_boolean (value));
+                               g_value_get_enum (value));
                        return;
 
                case PROP_NORMAL_PARAGRAPH_WIDTH:
@@ -4683,9 +4675,9 @@ webkit_editor_get_property (GObject *object,
                                E_WEBKIT_EDITOR (object)));
                        return;
 
-               case PROP_HTML_MODE:
-                       g_value_set_boolean (
-                               value, webkit_editor_get_html_mode (
+               case PROP_MODE:
+                       g_value_set_enum (
+                               value, webkit_editor_get_mode (
                                E_WEBKIT_EDITOR (object)));
                        return;
 
@@ -4970,7 +4962,7 @@ webkit_editor_load_changed_cb (EWebKitEditor *wk_editor,
                "EvoEditor.UNICODE_SMILEYS = %x;"
                "EvoEditor.WRAP_QUOTED_TEXT_IN_REPLIES = %x;",
                wk_editor->priv->normal_paragraph_width,
-               e_webkit_editor_three_state_to_bool (wk_editor->priv->start_bottom, 
"composer-reply-start-bottom"),
+               e_content_editor_util_three_state_to_bool (wk_editor->priv->start_bottom, 
"composer-reply-start-bottom"),
                wk_editor->priv->magic_links,
                wk_editor->priv->magic_smileys,
                wk_editor->priv->unicode_smileys,
@@ -5081,7 +5073,7 @@ webkit_editor_paste_clipboard_targets_cb (GtkClipboard *clipboard,
         * with SRCSET attribute in clipboard correctly). And if this fails the
         * source application can cancel the content and we could not fallback
         * to at least some content. */
-       if (wk_editor->priv->html_mode) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML) {
                if (e_targets_include_html (targets, n_targets)) {
                        content = e_clipboard_wait_for_html (clipboard);
                        is_html = TRUE;
@@ -5096,7 +5088,7 @@ webkit_editor_paste_clipboard_targets_cb (GtkClipboard *clipboard,
                }
        }
 
-       if (wk_editor->priv->html_mode &&
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML &&
            gtk_targets_include_image (targets, n_targets, TRUE) &&
            (!content || !*content || !is_libreoffice_content (targets, n_targets))) {
                gchar *uri;
@@ -5441,7 +5433,7 @@ paste_primary_clipboard_quoted (EContentEditor *editor)
                gdk_display_get_default (),
                GDK_SELECTION_PRIMARY);
 
-       if (wk_editor->priv->html_mode) {
+       if (wk_editor->priv->mode == E_CONTENT_EDITOR_MODE_HTML) {
                if (e_clipboard_wait_is_html_available (clipboard))
                        e_clipboard_request_html (clipboard, clipboard_html_received_for_paste_quote, editor);
                else if (gtk_clipboard_wait_is_text_available (clipboard))
@@ -5608,7 +5600,7 @@ e_webkit_editor_class_init (EWebKitEditorClass *class)
        g_object_class_override_property (
                object_class, PROP_CHANGED, "changed");
        g_object_class_override_property (
-               object_class, PROP_HTML_MODE, "html-mode");
+               object_class, PROP_MODE, "mode");
        g_object_class_override_property (
                object_class, PROP_EDITABLE, "editable");
        g_object_class_override_property (
@@ -5821,7 +5813,7 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
                g_settings, "changed::composer-inherit-theme-colors",
                G_CALLBACK (webkit_editor_style_settings_changed_cb), wk_editor);
 
-       wk_editor->priv->html_mode = TRUE;
+       wk_editor->priv->mode = E_CONTENT_EDITOR_MODE_HTML;
        wk_editor->priv->changed = FALSE;
        wk_editor->priv->can_copy = FALSE;
        wk_editor->priv->can_cut = FALSE;
@@ -5845,6 +5837,7 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
 static void
 e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
 {
+       iface->supports_mode = webkit_editor_supports_mode;
        iface->initialize = webkit_editor_initialize;
        iface->update_styles = webkit_editor_update_styles;
        iface->insert_content = webkit_editor_insert_content;


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