[evolution] Add a simple EMarkdownEditor widget



commit 4c25b2ebdeaf772357ab9464b24d3a1ede2a6162
Author: Milan Crha <mcrha redhat com>
Date:   Tue Jan 25 18:31:27 2022 +0100

    Add a simple EMarkdownEditor widget
    
    Related to https://gitlab.gnome.org/GNOME/evolution/-/issues/449

 data/icons/CMakeLists.txt                          |  18 +
 ...hicolor_actions_scalable_markdown-bold-dark.svg |  19 +
 .../hicolor_actions_scalable_markdown-bold.svg     |  18 +
 ...olor_actions_scalable_markdown-bullets-dark.svg |  31 ++
 .../hicolor_actions_scalable_markdown-bullets.svg  |  31 ++
 ...hicolor_actions_scalable_markdown-code-dark.svg |  28 +
 .../hicolor_actions_scalable_markdown-code.svg     |  26 +
 ...color_actions_scalable_markdown-header-dark.svg |  23 +
 .../hicolor_actions_scalable_markdown-header.svg   |  22 +
 ...hicolor_actions_scalable_markdown-help-dark.svg |  34 ++
 .../hicolor_actions_scalable_markdown-help.svg     |  33 ++
 ...color_actions_scalable_markdown-italic-dark.svg |  16 +
 .../hicolor_actions_scalable_markdown-italic.svg   |  16 +
 ...hicolor_actions_scalable_markdown-link-dark.svg |  30 ++
 .../hicolor_actions_scalable_markdown-link.svg     |  27 +
 ...olor_actions_scalable_markdown-numbers-dark.svg |  40 ++
 .../hicolor_actions_scalable_markdown-numbers.svg  |  40 ++
 ...icolor_actions_scalable_markdown-quote-dark.svg |  25 +
 .../hicolor_actions_scalable_markdown-quote.svg    |  25 +
 .../evolution-mail-composer-docs.sgml.in           |   4 +
 .../evolution-util/evolution-util-docs.sgml.in     |   5 +
 po/POTFILES.in                                     |   1 +
 src/e-util/CMakeLists.txt                          |   5 +
 src/e-util/e-markdown-editor.c                     | 585 +++++++++++++++++++++
 src/e-util/e-markdown-editor.h                     |  57 ++
 src/e-util/e-util.h                                |   1 +
 src/e-util/test-markdown-editor.c                  |  81 +++
 27 files changed, 1241 insertions(+)
---
diff --git a/data/icons/CMakeLists.txt b/data/icons/CMakeLists.txt
index 2d445c9695..d6cbfb2b3f 100644
--- a/data/icons/CMakeLists.txt
+++ b/data/icons/CMakeLists.txt
@@ -92,6 +92,24 @@ set(private_icons
        hicolor_actions_32x32_view-calendar-month.png
        hicolor_actions_32x32_view-calendar-week.png
        hicolor_actions_32x32_view-calendar-workweek.png
+       hicolor_actions_scalable_markdown-bold.svg
+       hicolor_actions_scalable_markdown-bold-dark.svg
+       hicolor_actions_scalable_markdown-bullets.svg
+       hicolor_actions_scalable_markdown-bullets-dark.svg
+       hicolor_actions_scalable_markdown-code.svg
+       hicolor_actions_scalable_markdown-code-dark.svg
+       hicolor_actions_scalable_markdown-header.svg
+       hicolor_actions_scalable_markdown-header-dark.svg
+       hicolor_actions_scalable_markdown-help.svg
+       hicolor_actions_scalable_markdown-help-dark.svg
+       hicolor_actions_scalable_markdown-italic.svg
+       hicolor_actions_scalable_markdown-italic-dark.svg
+       hicolor_actions_scalable_markdown-link.svg
+       hicolor_actions_scalable_markdown-link-dark.svg
+       hicolor_actions_scalable_markdown-numbers.svg
+       hicolor_actions_scalable_markdown-numbers-dark.svg
+       hicolor_actions_scalable_markdown-quote.svg
+       hicolor_actions_scalable_markdown-quote-dark.svg
        hicolor_actions_scalable_view-calendar-day.svg
        hicolor_actions_scalable_view-calendar-list.svg
        hicolor_actions_scalable_view-calendar-month.svg
diff --git a/data/icons/hicolor_actions_scalable_markdown-bold-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-bold-dark.svg
new file mode 100644
index 0000000000..e56fb34935
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-bold-dark.svg
@@ -0,0 +1,19 @@
+<?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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><g
+     id="g33418"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.4184891,-0.76629467)"
+     style="fill:#e6e6e6;stroke:#e6e6e6"><path
+       d="m 3.7098313,15.21086 h 5.5434688 c 3.0378319,0 4.8709559,-1.203315 4.8709559,-3.185358 0,-1.364885 
-0.924079,-2.4066304 -2.615885,-2.8913388 1.456277,-0.3522586 2.207967,-1.1591661 2.207967,-2.3925413 
0,-1.8345634 -1.690803,-2.8030402 -4.8539172,-2.8030402 H 3.771971 L 3.709831,4.5989496 C 4.258064,4.7605188 
4.5868035,4.9361785 4.8062972,5.1268679 V 14.007545 C 4.4775581,14.315654 4.2269945,14.433074 
3.7719711,14.520434 Z M 7.452249,8.7377548 V 5.0395077 c 0.3608114,-0.059179 0.7045847,-0.07327 
1.174642,-0.07327 1.456276,0 2.286141,0.645338 2.286141,1.804504 0,1.3066452 -0.829865,1.9670129 
-2.4585287,1.9670129 z m 1.2377841,5.4304202 c -0.3918814,0 -0.7677269,-0.02912 -1.2377841,-0.05824 V 
9.794534 h 0.9711844 c 1.8631896,0 2.8193406,0.763697 2.8193406,2.260092 0,1.453185 -0.798796,2.113552 
-2.5527409,2.113552 z"
+       
style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#e6e6e6;stroke-width:0.727722;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path33420" /></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-bold.svg 
b/data/icons/hicolor_actions_scalable_markdown-bold.svg
new file mode 100644
index 0000000000..39a46d8331
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-bold.svg
@@ -0,0 +1,18 @@
+<?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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><g
+     id="g33418"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.4184891,-0.76629467)"><path
+       d="m 3.7098313,15.21086 h 5.5434688 c 3.0378319,0 4.8709559,-1.203315 4.8709559,-3.185358 0,-1.364885 
-0.924079,-2.4066304 -2.615885,-2.8913388 1.456277,-0.3522586 2.207967,-1.1591661 2.207967,-2.3925413 
0,-1.8345634 -1.690803,-2.8030402 -4.8539172,-2.8030402 H 3.771971 L 3.709831,4.5989496 C 4.258064,4.7605188 
4.5868035,4.9361785 4.8062972,5.1268679 V 14.007545 C 4.4775581,14.315654 4.2269945,14.433074 
3.7719711,14.520434 Z M 7.452249,8.7377548 V 5.0395077 c 0.3608114,-0.059179 0.7045847,-0.07327 
1.174642,-0.07327 1.456276,0 2.286141,0.645338 2.286141,1.804504 0,1.3066452 -0.829865,1.9670129 
-2.4585287,1.9670129 z m 1.2377841,5.4304202 c -0.3918814,0 -0.7677269,-0.02912 -1.2377841,-0.05824 V 
9.794534 h 0.9711844 c 1.8631896,0 2.8193406,0.763697 2.8193406,2.260092 0,1.453185 -0.798796,2.113552 
-2.5527409,2.113552 z"
+       
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:0.727722;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path33420" /></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-bullets-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-bullets-dark.svg
new file mode 100644
index 0000000000..fa2bf472e6
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-bullets-dark.svg
@@ -0,0 +1,31 @@
+<?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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   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:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7,6 H 21"
+     id="path33702" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 3,6 H 5"
+     id="path33702-9" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 3,12 H 5"
+     id="path33702-9-1" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 3,18 H 5"
+     id="path33702-9-7" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7,12 H 20.99999"
+     id="path33704" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7,18 H 21"
+     id="path33706" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-bullets.svg 
b/data/icons/hicolor_actions_scalable_markdown-bullets.svg
new file mode 100644
index 0000000000..fe13db36e8
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-bullets.svg
@@ -0,0 +1,31 @@
+<?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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   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:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7,6 H 21"
+     id="path33702" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 3,6 H 5"
+     id="path33702-9" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 3,12 H 5"
+     id="path33702-9-1" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 3,18 H 5"
+     id="path33702-9-7" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7,12 H 20.99999"
+     id="path33704" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7,18 H 21"
+     id="path33706" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-code-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-code-dark.svg
new file mode 100644
index 0000000000..23b6dcf3ff
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-code-dark.svg
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg51326"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs51330" /><g
+     id="g51332"
+     transform="scale(1.3333333)"
+     style="fill:#e6e6e6"><g
+       id="g51334"
+       transform="translate(-0.12899983,0.66800028)"
+       style="fill:#e6e6e6"><path
+         d="M 6.879,5.004 2.035,7.879 v 0.812 l 4.844,2.985 V 10.582 L 3.035,8.285 6.879,6.004 Z"
+         style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path51336" /><path
+         d="M 7.48,13.395 H 8.371 L 10.934,3.145 H 9.98 Z"
+         style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path51338" /><path
+         d="M 11.551,11.613 16.395,8.738 V 7.91 L 11.551,4.941 v 1.094 l 3.828,2.281 -3.828,2.282 z"
+         style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path51340" /></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-code.svg 
b/data/icons/hicolor_actions_scalable_markdown-code.svg
new file mode 100644
index 0000000000..ad3aa41fd4
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-code.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg51326"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs51330" /><g
+     id="g51332"
+     transform="scale(1.3333333)"><g
+       id="g51334"
+       transform="translate(-0.12899983,0.66800028)"><path
+         d="M 6.879,5.004 2.035,7.879 v 0.812 l 4.844,2.985 V 10.582 L 3.035,8.285 6.879,6.004 Z"
+         style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path51336" /><path
+         d="M 7.48,13.395 H 8.371 L 10.934,3.145 H 9.98 Z"
+         style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path51338" /><path
+         d="M 11.551,11.613 16.395,8.738 V 7.91 L 11.551,4.941 v 1.094 l 3.828,2.281 -3.828,2.282 z"
+         style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path51340" /></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-header-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-header-dark.svg
new file mode 100644
index 0000000000..d5dcf32db1
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-header-dark.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg4631"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs4635" /><g
+     id="g4637"
+     transform="scale(1.3333333)"
+     style="stroke:none"><g
+       id="g4639"
+       style="stroke:none"><g
+         id="g4641"
+         style="stroke:none"><path
+           d="m 4.902,3.543 h 1.485 v 4.484 h 5.375 V 3.543 h 1.484 V 14.48 H 11.762 V 9.277 H 6.387 V 14.48 
H 4.902 Z"
+           
style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="path4643" /></g></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-header.svg 
b/data/icons/hicolor_actions_scalable_markdown-header.svg
new file mode 100644
index 0000000000..e509013d69
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-header.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg4631"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs4635" /><g
+     id="g4637"
+     transform="scale(1.3333333)"><g
+       id="g4639"
+       style="stroke:none;fill:#1a1a1a"><g
+         id="g4641"
+         style="stroke:none;fill:#1a1a1a"><path
+           d="m 4.902,3.543 h 1.485 v 4.484 h 5.375 V 3.543 h 1.484 V 14.48 H 11.762 V 9.277 H 6.387 V 14.48 
H 4.902 Z"
+           
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="path4643" /></g></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-help-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-help-dark.svg
new file mode 100644
index 0000000000..fcd2864596
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-help-dark.svg
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg11209"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs11213" /><path
+     d="m 7.5679998,11.765333 h 3.1453332 v -0.478667 c 0,-1 2.313333,-1.5839996 2.313333,-4.1253328 
0,-2.0826667 -1.542666,-3.2493333 -4.2719996,-3.2493333 -1.3333333,0 -2.6866666,0.2906667 -3.8333332,0.812 l 
0.7093333,2.3546666 c 0.7493333,-0.3333333 1.5413333,-0.48 2.3333333,-0.48 1.1866666,0 1.8333333,0.3546667 
1.8333333,1 0,1.2079999 -2.2293333,1.9999999 -2.2293333,3.6879995 z m 1.5413333,5 c 1.1039999,0 
1.8959999,-0.792 1.8959999,-1.916 0,-1.166667 -0.770667,-1.98 -1.8959999,-1.98 -1.1453333,0 
-1.9159999,0.813333 -1.9159999,1.98 0,1.124 0.7906666,1.916 1.9159999,1.916 z"
+     style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path11219" /><g
+     id="g11221"
+     transform="matrix(1.3333333,1.23672,-1.23672,1.3333333,0,0)"
+     style="stroke:#e6e6e6"><path
+       d="M 12.671,-3.092 12.67,0.205"
+       
style="fill:none;stroke:#e6e6e6;stroke-width:0.549878;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path11223" /></g><g
+     id="g11225"
+     transform="matrix(1.3333333,1.23672,-1.23672,1.3333333,0,0)"
+     style="stroke:#e6e6e6"><path
+       d="m 11.571,-1.444 1.1,-2.2 1.099,2.2"
+       
style="fill:none;stroke:#e6e6e6;stroke-width:0.549878;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path11227" /></g><g
+     id="g11229"
+     transform="scale(1.3333333)"
+     style="stroke:#e6e6e6"><path
+       d="m 12.75,10.5 h -3 v 3 h 4.5 v -2.25"
+       
style="fill:none;stroke:#e6e6e6;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path11231" /></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-help.svg 
b/data/icons/hicolor_actions_scalable_markdown-help.svg
new file mode 100644
index 0000000000..6be86d3402
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-help.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg11209"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs11213" /><g
+     id="g11215"
+     transform="scale(1.3333333)"><g
+       id="g11217"><path
+         d="M 5.676,8.824 H 8.035 V 8.465 c 0,-0.75 1.735,-1.188 1.735,-3.094 0,-1.562 -1.157,-2.437 
-3.204,-2.437 -1,0 -2.015,0.218 -2.875,0.609 l 0.532,1.766 c 0.562,-0.25 1.156,-0.36 1.75,-0.36 0.89,0 
1.375,0.266 1.375,0.75 0,0.906 -1.672,1.5 -1.672,2.766 z m 1.156,3.75 c 0.828,0 1.422,-0.594 1.422,-1.437 
0,-0.875 -0.578,-1.485 -1.422,-1.485 -0.859,0 -1.437,0.61 -1.437,1.485 0,0.843 0.593,1.437 1.437,1.437 z"
+         style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path11219" /><g
+         id="g11221"
+         transform="matrix(1,0.92754,-0.92754,1,0,0)"><path
+           d="M 12.671,-3.092 12.67,0.205"
+           
style="fill:none;stroke:#1a1a1a;stroke-width:0.549878;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="path11223" /></g><g
+         id="g11225"
+         transform="matrix(1,0.92754,-0.92754,1,0,0)"><path
+           d="m 11.571,-1.444 1.1,-2.2 1.099,2.2"
+           
style="fill:none;stroke:#1a1a1a;stroke-width:0.549878;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="path11227" /></g><g
+         id="g11229"><path
+           d="m 12.75,10.5 h -3 v 3 h 4.5 v -2.25"
+           
style="fill:none;stroke:#1a1a1a;stroke-width:0.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           id="path11231" /></g></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-italic-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-italic-dark.svg
new file mode 100644
index 0000000000..87e0445fae
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-italic-dark.svg
@@ -0,0 +1,16 @@
+<?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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><path
+     d="m 8.0000001,20 h 5.9658889 l 0.09764,-0.894667 C 13.340012,19.021333 12.655299,18.876 
12.049447,18.48 L 13.98592,5.5 C 14.611801,5.1253333 15.178848,4.98 15.902365,4.876 L 16.000005,4 h -5.828201 
l -0.117665,0.876 c 0.743546,0.104 1.388202,0.2706667 1.936473,0.624 l -1.936473,12.98 c -0.6258802,0.416 
-1.2129558,0.541333 -1.9364732,0.625333 z"
+     style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.2919"
+     id="path33416" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-italic.svg 
b/data/icons/hicolor_actions_scalable_markdown-italic.svg
new file mode 100644
index 0000000000..3b64797ad3
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-italic.svg
@@ -0,0 +1,16 @@
+<?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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><path
+     d="m 8.0000001,20 h 5.9658889 l 0.09764,-0.894667 C 13.340012,19.021333 12.655299,18.876 
12.049447,18.48 L 13.98592,5.5 C 14.611801,5.1253333 15.178848,4.98 15.902365,4.876 L 16.000005,4 h -5.828201 
l -0.117665,0.876 c 0.743546,0.104 1.388202,0.2706667 1.936473,0.624 l -1.936473,12.98 c -0.6258802,0.416 
-1.2129558,0.541333 -1.9364732,0.625333 z"
+     style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.2919"
+     id="path33416" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-link-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-link-dark.svg
new file mode 100644
index 0000000000..5cafddcdf8
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-link-dark.svg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg51326"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs51330" /><g
+     id="g51332"
+     transform="matrix(1.2391158,0.4923106,-0.4923106,1.2391158,5.1915138,-3.3935698)"
+     style="stroke:#e6e6e6"><g
+       id="g54244"
+       transform="rotate(38.936484,7.400522,4.6312859)"
+       style="stroke:#e6e6e6"><path
+         
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 8.226042,6.6881392 c -0.250375,-1 -0.250375,-2.5 -3.7e-4,-3.5003801 0.2500051,-1.00038 
0.749995,-1.50037 1.2503751,-1.75037 0.50038,-0.25 1.0003699,-0.25 1.5003749,3.8e-4 0.500005,0.25038 
0.999995,0.75037 1.24962,1.7503851 0.249625,1.000015 0.249625,2.499985 -3.8e-4,3.4996149 -0.250005,0.99963 
-0.749995,1.4996199 -1.237528,1.7496199 -0.487533,0.25 -1.0117169,0.25 -1.5117219,3.7e-4 C 8.976407,8.1881291 
8.476417,7.6881391 8.226042,6.6881392 Z"
+         id="path53269" /><g
+         id="g53872"
+         style="stroke:#e6e6e6"><path
+           
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 9.5279035,9.4814178 c 0.031913,0.432314 0.063768,0.8638272 0.1454647,1.2909192 
0.081697,0.427092 0.2183089,0.866695 0.4123258,1.219544 0.194017,0.35285 0.455937,0.636559 0.735758,0.83268 
0.279821,0.19612 0.581495,0.309578 0.900979,0.327199 0.319483,0.01762 0.645557,-0.05746 0.932875,-0.233203 
0.287317,-0.175739 0.530677,-0.450964 0.727124,-0.786131 0.196448,-0.335166 0.346086,-0.730613 
0.436312,-1.189182 0.09023,-0.458568 0.120921,-0.980524 0.125016,-1.4233537 0.0041,-0.4428297 
-0.0185,-0.806597 -0.05852,-1.1349214 -0.04003,-0.3283244 -0.09759,-0.6212587 -0.21279,-0.914902 C 
13.55725,7.1764236 13.384075,6.8820681 13.210376,6.586821"
+           id="path53746" /><path
+           
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 9.7374115,7.5753467 c 0.077527,-0.2420881 0.1552029,-0.4846389 0.2831265,-0.6976481 
0.127924,-0.2130092 0.316247,-0.406603 0.524908,-0.5641978 0.208661,-0.1575949 0.442518,-0.2843205 
0.610091,-0.3612449 0.167572,-0.076924 0.277147,-0.1063432 0.381942,-0.134479"
+           id="path53750" /></g></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-link.svg 
b/data/icons/hicolor_actions_scalable_markdown-link.svg
new file mode 100644
index 0000000000..e7bfbbbb9f
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-link.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg51326"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs51330" /><g
+     id="g51332"
+     transform="matrix(1.2391158,0.4923106,-0.4923106,1.2391158,5.1915138,-3.3935698)"><g
+       id="g54244"
+       transform="rotate(38.936484,7.400522,4.6312859)"><path
+         
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         d="m 8.226042,6.6881392 c -0.250375,-1 -0.250375,-2.5 -3.7e-4,-3.5003801 0.2500051,-1.00038 
0.749995,-1.50037 1.2503751,-1.75037 0.50038,-0.25 1.0003699,-0.25 1.5003749,3.8e-4 0.500005,0.25038 
0.999995,0.75037 1.24962,1.7503851 0.249625,1.000015 0.249625,2.499985 -3.8e-4,3.4996149 -0.250005,0.99963 
-0.749995,1.4996199 -1.237528,1.7496199 -0.487533,0.25 -1.0117169,0.25 -1.5117219,3.7e-4 C 8.976407,8.1881291 
8.476417,7.6881391 8.226042,6.6881392 Z"
+         id="path53269" /><g
+         id="g53872"><path
+           
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 9.5279035,9.4814178 c 0.031913,0.432314 0.063768,0.8638272 0.1454647,1.2909192 
0.081697,0.427092 0.2183089,0.866695 0.4123258,1.219544 0.194017,0.35285 0.455937,0.636559 0.735758,0.83268 
0.279821,0.19612 0.581495,0.309578 0.900979,0.327199 0.319483,0.01762 0.645557,-0.05746 0.932875,-0.233203 
0.287317,-0.175739 0.530677,-0.450964 0.727124,-0.786131 0.196448,-0.335166 0.346086,-0.730613 
0.436312,-1.189182 0.09023,-0.458568 0.120921,-0.980524 0.125016,-1.4233537 0.0041,-0.4428297 
-0.0185,-0.806597 -0.05852,-1.1349214 -0.04003,-0.3283244 -0.09759,-0.6212587 -0.21279,-0.914902 C 
13.55725,7.1764236 13.384075,6.8820681 13.210376,6.586821"
+           id="path53746" /><path
+           
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+           d="m 9.7374115,7.5753467 c 0.077527,-0.2420881 0.1552029,-0.4846389 0.2831265,-0.6976481 
0.127924,-0.2130092 0.316247,-0.406603 0.524908,-0.5641978 0.208661,-0.1575949 0.442518,-0.2843205 
0.610091,-0.3612449 0.167572,-0.076924 0.277147,-0.1063432 0.381942,-0.134479"
+           id="path53750" /></g></g></g></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-numbers-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-numbers-dark.svg
new file mode 100644
index 0000000000..f79f5f551d
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-numbers-dark.svg
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg824"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs828" /><g
+     id="g834"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.56799981,-0.08799955)"
+     style="stroke:#e6e6e6;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none"><path
+       d="m 5.676,4.566 h 10.5"
+       
style="fill:none;stroke:#e6e6e6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path836" /></g><g
+     id="g838"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.56799981,-0.08799955)"
+     style="stroke:#e6e6e6;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none"><path
+       d="m 5.676,9.066 h 10.5"
+       
style="fill:none;stroke:#e6e6e6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path840" /></g><g
+     id="g842"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.56799981,-0.08799955)"
+     style="stroke:#e6e6e6;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none"><path
+       d="m 5.676,13.566 h 10.5"
+       
style="fill:none;stroke:#e6e6e6;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path844" /></g><path
+     d="M 4.4932328,7.9143159 H 5.1385661 V 4.4356494 H 4.7438995 l -1.3133333,0.6866666 0.2506666,0.4386667 
0.812,-0.3546667 z"
+     style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path846" /><path
+     d="m 3.5726414,13.833474 h 2.3333333 v -0.562666 h -1.396 c 0.896,-0.792 1.2706666,-1.354666 
1.2706666,-1.937333 0,-0.625333 -0.4586666,-1.021333 -1.1666666,-1.021333 -0.4373333,0 -0.8333333,0.146666 
-1.1453333,0.396 l 0.1866666,0.48 c 0.272,-0.209334 0.5626667,-0.313334 0.8333334,-0.313334 0.3959999,0 
0.6253333,0.229334 0.6253333,0.562667 0,0.478667 -0.4786667,1 -1.5413333,1.958666 z"
+     style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path848" /><path
+     d="m 4.7419027,20.015858 c 0.8533333,0 1.4159999,-0.396 1.4159999,-1 0,-0.437333 -0.2919999,-0.770666 
-0.7706666,-0.854666 0.3546667,-0.104 0.5826667,-0.396 0.5826667,-0.790667 0,-0.562667 -0.4573334,-0.917333 
-1.1453334,-0.917333 -0.3959999,0 -0.8119999,0.104 -1.1453333,0.292 l 0.1453334,0.478666 c 0.312,-0.125333 
0.604,-0.208 0.8546666,-0.208 0.396,0 0.6453333,0.166667 0.6453333,0.437334 0,0.312 -0.312,0.521333 
-0.7506666,0.521333 H 4.3245694 v 0.5 h 0.292 c 0.5413333,0 0.8746666,0.208 0.8746666,0.541333 0,0.292 
-0.2706667,0.458667 -0.7293333,0.458667 -0.292,0 -0.6666667,-0.084 -0.9786667,-0.209333 l -0.1466666,0.5 c 
0.3546666,0.166666 0.7293333,0.250666 1.1053333,0.250666 z"
+     style="fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path850" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-numbers.svg 
b/data/icons/hicolor_actions_scalable_markdown-numbers.svg
new file mode 100644
index 0000000000..7ca25fb951
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-numbers.svg
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   version="1.1"
+   id="svg824"
+   xml:space="preserve"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs828" /><g
+     id="g834"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.56799981,-0.08799955)"
+     style="stroke:#1a1a1a;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none"><path
+       d="m 5.676,4.566 h 10.5"
+       
style="fill:none;stroke:#1a1a1a;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path836" /></g><g
+     id="g838"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.56799981,-0.08799955)"
+     style="stroke:#1a1a1a;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none"><path
+       d="m 5.676,9.066 h 10.5"
+       
style="fill:none;stroke:#1a1a1a;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path840" /></g><g
+     id="g842"
+     transform="matrix(1.3333333,0,0,1.3333333,-0.56799981,-0.08799955)"
+     style="stroke:#1a1a1a;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none"><path
+       d="m 5.676,13.566 h 10.5"
+       
style="fill:none;stroke:#1a1a1a;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path844" /></g><path
+     d="M 4.4932328,7.9143159 H 5.1385661 V 4.4356494 H 4.7438995 l -1.3133333,0.6866666 0.2506666,0.4386667 
0.812,-0.3546667 z"
+     style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path846" /><path
+     d="m 3.5726414,13.833474 h 2.3333333 v -0.562666 h -1.396 c 0.896,-0.792 1.2706666,-1.354666 
1.2706666,-1.937333 0,-0.625333 -0.4586666,-1.021333 -1.1666666,-1.021333 -0.4373333,0 -0.8333333,0.146666 
-1.1453333,0.396 l 0.1866666,0.48 c 0.272,-0.209334 0.5626667,-0.313334 0.8333334,-0.313334 0.3959999,0 
0.6253333,0.229334 0.6253333,0.562667 0,0.478667 -0.4786667,1 -1.5413333,1.958666 z"
+     style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path848" /><path
+     d="m 4.7419027,20.015858 c 0.8533333,0 1.4159999,-0.396 1.4159999,-1 0,-0.437333 -0.2919999,-0.770666 
-0.7706666,-0.854666 0.3546667,-0.104 0.5826667,-0.396 0.5826667,-0.790667 0,-0.562667 -0.4573334,-0.917333 
-1.1453334,-0.917333 -0.3959999,0 -0.8119999,0.104 -1.1453333,0.292 l 0.1453334,0.478666 c 0.312,-0.125333 
0.604,-0.208 0.8546666,-0.208 0.396,0 0.6453333,0.166667 0.6453333,0.437334 0,0.312 -0.312,0.521333 
-0.7506666,0.521333 H 4.3245694 v 0.5 h 0.292 c 0.5413333,0 0.8746666,0.208 0.8746666,0.541333 0,0.292 
-0.2706667,0.458667 -0.7293333,0.458667 -0.292,0 -0.6666667,-0.084 -0.9786667,-0.209333 l -0.1466666,0.5 c 
0.3546666,0.166666 0.7293333,0.250666 1.1053333,0.250666 z"
+     style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.33333"
+     id="path850" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-quote-dark.svg 
b/data/icons/hicolor_actions_scalable_markdown-quote-dark.svg
new file mode 100644
index 0000000000..2b57fc1e60
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-quote-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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><path
+     
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 4,3.0000001 V 20.999999"
+     id="path33601" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7.0000095,6.0000002 15,6"
+     id="path33702-3" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7.0000095,12 H 20.999999"
+     id="path33704-6" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#e6e6e6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7.0000095,18 H 15"
+     id="path33706-7" /></svg>
diff --git a/data/icons/hicolor_actions_scalable_markdown-quote.svg 
b/data/icons/hicolor_actions_scalable_markdown-quote.svg
new file mode 100644
index 0000000000..3b301881d3
--- /dev/null
+++ b/data/icons/hicolor_actions_scalable_markdown-quote.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="24"
+   height="24"
+   viewBox="0 0 24 24"
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:svg="http://www.w3.org/2000/svg";><defs
+     id="defs33410" /><path
+     
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 4,3.0000001 V 20.999999"
+     id="path33601" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7.0000095,6.0000002 15,6"
+     id="path33702-3" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7.0000095,12 H 20.999999"
+     id="path33704-6" /><path
+     
style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+     d="M 7.0000095,18 H 15"
+     id="path33706-7" /></svg>
diff --git a/docs/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml.in 
b/docs/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml.in
index ea4fca7cd8..82059231af 100644
--- a/docs/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml.in
+++ b/docs/reference/evolution-mail-composer/evolution-mail-composer-docs.sgml.in
@@ -29,6 +29,10 @@
     <title>API Index</title>
     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-3-44" role="3.44">
+    <title>Index of new symbols in 3.44</title>
+    <xi:include href="xml/api-index-3.44.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-3-42" role="3.42">
     <title>Index of new symbols in 3.42</title>
     <xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
diff --git a/docs/reference/evolution-util/evolution-util-docs.sgml.in 
b/docs/reference/evolution-util/evolution-util-docs.sgml.in
index 779f3ae6c4..7f7b3bb5ab 100644
--- a/docs/reference/evolution-util/evolution-util-docs.sgml.in
+++ b/docs/reference/evolution-util/evolution-util-docs.sgml.in
@@ -280,6 +280,7 @@
     <xi:include href="xml/e-interval-chooser.xml"/>
     <xi:include href="xml/e-mail-identity-combo-box.xml"/>
     <xi:include href="xml/e-map.xml"/>
+    <xi:include href="xml/e-markdown-editor.xml"/>
     <xi:include href="xml/e-menu-tool-action.xml"/>
     <xi:include href="xml/e-menu-tool-button.xml"/>
     <xi:include href="xml/e-mktemp.xml"/>
@@ -326,6 +327,10 @@
     <title>Index</title>
     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-3-44" role="3.44">
+    <title>Index of new symbols in 3.44</title>
+    <xi:include href="xml/api-index-3.44.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-3-42" role="3.42">
     <title>Index of new symbols in 3.42</title>
     <xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6faa645e63..b003bb2dad 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -237,6 +237,7 @@ src/e-util/e-mail-signature-editor.c
 src/e-util/e-mail-signature-manager.c
 src/e-util/e-mail-signature-script-dialog.c
 src/e-util/e-map.c
+src/e-util/e-markdown-editor.c
 src/e-util/e-misc-utils.c
 src/e-util/e-name-selector-dialog.c
 src/e-util/e-name-selector-entry.c
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 7048adf146..e81570ad3c 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -168,6 +168,7 @@ set(SOURCES
        e-mail-signature-script-dialog.c
        e-mail-signature-tree-view.c
        e-map.c
+       e-markdown-editor.c
        e-marshal.c
        e-menu-tool-action.c
        e-menu-tool-button.c
@@ -443,6 +444,7 @@ set(HEADERS
        e-mail-signature-script-dialog.h
        e-mail-signature-tree-view.h
        e-map.h
+       e-markdown-editor.h
        e-menu-tool-action.h
        e-menu-tool-button.h
        e-misc-utils.h
@@ -651,6 +653,7 @@ target_include_directories(evolution-util PUBLIC
        ${GEO_INCLUDE_DIRS}
        ${GNOME_PLATFORM_INCLUDE_DIRS}
        ${GSPELL_INCLUDE_DIRS}
+       ${MARKDOWN_INCLUDE_DIRS}
 )
 
 target_link_libraries(evolution-util
@@ -663,6 +666,7 @@ target_link_libraries(evolution-util
        ${GSPELL_LDFLAGS}
        ${ICONV_LIBS}
        ${MATH_LDFLAGS}
+       ${MARKDOWN_LDFLAGS}
 )
 
 if(HAVE_LDAP)
@@ -816,6 +820,7 @@ add_private_programs_simple(
        test-dateedit
        test-html-editor
        test-mail-signatures
+       test-markdown-editor
        test-name-selector
        test-preferences-window
        test-proxy-preferences
diff --git a/src/e-util/e-markdown-editor.c b/src/e-util/e-markdown-editor.c
new file mode 100644
index 0000000000..3325ea6395
--- /dev/null
+++ b/src/e-util/e-markdown-editor.c
@@ -0,0 +1,585 @@
+/* -*- 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 <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserverui/libedataserverui.h>
+
+#include "e-misc-utils.h"
+#include "e-spell-text-view.h"
+#include "e-web-view.h"
+
+#include "e-markdown-editor.h"
+
+struct _EMarkdownEditorPrivate {
+       GtkTextView *text_view;
+       EWebView *web_view;
+       GtkToolbar *action_toolbar;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (EMarkdownEditor, e_markdown_editor, GTK_TYPE_BOX)
+
+static void
+e_markdown_editor_get_selection (EMarkdownEditor *self,
+                                GtkTextIter *out_start,
+                                GtkTextIter *out_end,
+                                gchar **out_selected_text)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+
+       if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end) && out_selected_text) {
+               *out_selected_text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+       } else if (out_selected_text) {
+               *out_selected_text = NULL;
+       }
+
+       if (out_start)
+               *out_start = start;
+
+       if (out_end)
+               *out_end = end;
+}
+
+static void
+e_markdown_editor_surround_selection (EMarkdownEditor *self,
+                                     gboolean whole_lines,
+                                     const gchar *prefix,
+                                     const gchar *suffix)
+{
+       GtkTextIter start, end;
+       GtkTextBuffer *buffer;
+
+       e_markdown_editor_get_selection (self, &start, &end, NULL);
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+
+       gtk_text_buffer_begin_user_action (buffer);
+
+       if (whole_lines) {
+               gint to_line, ii;
+
+               to_line = gtk_text_iter_get_line (&end);
+
+               for (ii = gtk_text_iter_get_line (&start); ii <= to_line; ii++) {
+                       GtkTextIter iter;
+
+                       gtk_text_buffer_get_iter_at_line (buffer, &iter, ii);
+
+                       if (prefix && *prefix)
+                               gtk_text_buffer_insert (buffer, &iter, prefix, -1);
+
+                       if (suffix && *suffix) {
+                               gtk_text_iter_forward_to_line_end (&iter);
+                               gtk_text_buffer_insert (buffer, &iter, suffix, -1);
+                       }
+               }
+       } else {
+               gint end_offset = gtk_text_iter_get_offset (&end);
+
+               if (prefix && *prefix) {
+                       gtk_text_buffer_insert (buffer, &start, prefix, -1);
+                       /* Keep the cursor where it is, move it only when the suffix is used */
+                       end_offset += strlen (prefix);
+                       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
+               }
+
+               if (suffix && *suffix) {
+                       gtk_text_buffer_insert (buffer, &end, suffix, -1);
+                       /* Place the cursor before the suffix */
+                       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
+                       gtk_text_buffer_select_range (buffer, &end, &end);
+               }
+       }
+
+       gtk_text_buffer_end_user_action (buffer);
+}
+
+static void
+e_markdown_editor_add_bold_text_cb (GtkToolButton *button,
+                                   gpointer user_data)
+{
+       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)
+{
+       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_insert_quote_cb (GtkToolButton *button,
+                                  gpointer user_data)
+{
+       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)
+{
+       EMarkdownEditor *self = user_data;
+       GtkTextIter start, end;
+       gchar *selection = NULL;
+
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+
+       e_markdown_editor_get_selection (self, &start, &end, &selection);
+
+       if (selection && strchr (selection, '\n')) {
+               GtkTextBuffer *buffer;
+               GtkTextIter iter;
+               gint start_line, end_line;
+
+               buffer = gtk_text_view_get_buffer (self->priv->text_view);
+
+               gtk_text_buffer_begin_user_action (buffer);
+
+               start_line = gtk_text_iter_get_line (&start);
+               end_line = gtk_text_iter_get_line (&end);
+
+               gtk_text_buffer_get_iter_at_line (buffer, &iter, start_line);
+               gtk_text_buffer_insert (buffer, &iter, "```\n", -1);
+
+               /* One line added above + 1 for the end line itself */
+               end_line = end_line + 2;
+               gtk_text_buffer_get_iter_at_line (buffer, &iter, end_line);
+               if (gtk_text_iter_is_end (&iter) && gtk_text_iter_get_line_offset (&iter) > 0) {
+                       gtk_text_buffer_insert (buffer, &iter, "\n```\n", -1);
+               } else {
+                       if (gtk_text_iter_is_end (&iter))
+                               end_line--;
+                       gtk_text_buffer_insert (buffer, &iter, "```\n", -1);
+               }
+
+               /* Place the cursor before the suffix */
+               gtk_text_buffer_get_iter_at_line (buffer, &iter, end_line);
+               gtk_text_buffer_select_range (buffer, &iter, &iter);
+
+               gtk_text_buffer_end_user_action (buffer);
+       } else {
+               e_markdown_editor_surround_selection (self, FALSE, "`", "`");
+       }
+
+       g_free (selection);
+}
+
+static void
+e_markdown_editor_add_link_cb (GtkToolButton *button,
+                              gpointer user_data)
+{
+       EMarkdownEditor *self = user_data;
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+       gchar *selection = NULL;
+       gint offset;
+
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+
+       e_markdown_editor_get_selection (self, &start, &end, &selection);
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       offset = gtk_text_iter_get_offset (&start);
+
+       gtk_text_buffer_begin_user_action (buffer);
+
+       if (selection && *selection) {
+               gint end_offset = gtk_text_iter_get_offset (&end);
+
+               if (g_ascii_strncasecmp (selection, "http:", 5) == 0 ||
+                   g_ascii_strncasecmp (selection, "https:", 6) == 0) {
+                       gtk_text_buffer_insert (buffer, &start, "[](", -1);
+                       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset + 3);
+                       gtk_text_buffer_insert (buffer, &end, ")", -1);
+                       gtk_text_buffer_get_iter_at_offset (buffer, &start, offset + 1);
+                       end = start;
+               } else {
+                       gtk_text_buffer_insert (buffer, &start, "[", -1);
+                       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset + 1);
+                       gtk_text_buffer_insert (buffer, &end, "](https://)", -1);
+                       gtk_text_buffer_get_iter_at_offset (buffer, &start, end_offset + 1 + 2);
+                       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset + 1 + 10);
+               }
+
+               gtk_text_buffer_select_range (buffer, &start, &end);
+       } else {
+               gtk_text_buffer_insert (buffer, &start, "[](https://)", -1);
+
+               /* skip "[](" */
+               offset += 3;
+
+               gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
+
+               /* after the "https://"; text */
+               gtk_text_buffer_get_iter_at_offset (buffer, &end, offset + 8);
+
+               gtk_text_buffer_select_range (buffer, &start, &end);
+       }
+
+       gtk_text_buffer_end_user_action (buffer);
+}
+
+static void
+e_markdown_editor_add_bullet_list_cb (GtkToolButton *button,
+                                     gpointer user_data)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       EMarkdownEditor *self = user_data;
+       GtkWidget *toplevel;
+
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+
+       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+
+       e_show_uri (GTK_IS_WINDOW (toplevel) ? GTK_WINDOW (toplevel) : NULL, "https://commonmark.org/help/";);
+}
+
+#ifdef HAVE_MARKDOWN
+static void
+e_markdown_editor_switch_page_cb (GtkNotebook *notebook,
+                                 GtkWidget *page,
+                                 guint page_num,
+                                 gpointer user_data)
+{
+       EMarkdownEditor *self = user_data;
+       gchar *converted;
+       gchar *html;
+
+       g_return_if_fail (E_IS_MARKDOWN_EDITOR (self));
+
+       gtk_widget_set_visible (GTK_WIDGET (self->priv->action_toolbar), page_num != 1);
+
+       /* Not the Preview page */
+       if (page_num != 1)
+               return;
+
+       converted = e_markdown_editor_dup_html (self);
+
+       html = g_strconcat ("<div class=\"-e-web-view-background-color -e-web-view-text-color\" 
style=\"border: none; padding: 0px; margin: 0;\">",
+               converted ? converted : "",
+               "</div>",
+               NULL);
+
+       e_web_view_load_string (self->priv->web_view, html);
+
+       g_free (converted);
+       g_free (html);
+}
+#endif /* HAVE_MARKDOWN */
+
+static gboolean
+e_markdown_editor_is_dark_theme (EMarkdownEditor *self)
+{
+       GdkRGBA rgba;
+       gdouble brightness;
+
+       e_utils_get_theme_color (GTK_WIDGET (self), "theme_text_color,theme_fg_color", 
E_UTILS_DEFAULT_THEME_TEXT_COLOR, &rgba);
+
+       brightness =
+               (0.2109 * 255.0 * rgba.red) +
+               (0.5870 * 255.0 * rgba.green) +
+               (0.1021 * 255.0 * rgba.blue);
+
+       return brightness > 140;
+}
+
+static void
+e_markdown_editor_constructed (GObject *object)
+{
+       struct _items {
+               const gchar *label;
+               const gchar *icon_name;
+               const gchar *icon_name_dark;
+               GCallback callback;
+       } 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 (NULL, "", NULL),
+               ITEM (N_("Open online common mark documentation"), "markdown-help", G_CALLBACK 
(e_markdown_editor_markdown_syntax_cb))
+               #undef ITEM
+       };
+       EMarkdownEditor *self = E_MARKDOWN_EDITOR (object);
+       GtkWidget *widget;
+       GtkNotebook *notebook;
+       GtkScrolledWindow *scrolled_window;
+       gboolean is_dark_theme;
+       guint ii;
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_markdown_editor_parent_class)->constructed (object);
+
+       widget = gtk_notebook_new ();
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "show-border", FALSE,
+               "show-tabs", TRUE,
+               NULL);
+       gtk_box_pack_start (GTK_BOX (self), widget, TRUE, TRUE, 0);
+
+       notebook = GTK_NOTEBOOK (widget);
+
+       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_notebook_append_page (notebook, widget, gtk_label_new_with_mnemonic (_("_Write")));
+
+       scrolled_window = GTK_SCROLLED_WINDOW (widget);
+
+       widget = gtk_text_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "margin", 4,
+               "monospace", TRUE,
+               "wrap-mode", GTK_WRAP_WORD_CHAR,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+
+       self->priv->text_view = GTK_TEXT_VIEW (widget);
+
+       e_buffer_tagger_connect (self->priv->text_view);
+       e_spell_text_view_attach (self->priv->text_view);
+
+       #ifdef HAVE_MARKDOWN
+       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_notebook_append_page (notebook, widget, gtk_label_new_with_mnemonic (_("_Preview")));
+
+       scrolled_window = GTK_SCROLLED_WINDOW (widget);
+
+       widget = e_web_view_new ();
+       g_object_set (G_OBJECT (widget),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               "margin", 4,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
+
+       self->priv->web_view = E_WEB_VIEW (widget);
+       #endif /* HAVE_MARKDOWN */
+
+       widget = gtk_toolbar_new ();
+       gtk_widget_show (widget);
+       gtk_notebook_set_action_widget (notebook, widget, GTK_PACK_END);
+
+       self->priv->action_toolbar = GTK_TOOLBAR (widget);
+
+       is_dark_theme = e_markdown_editor_is_dark_theme (self);
+
+       for (ii = 0; ii < G_N_ELEMENTS (items); ii++) {
+               GtkToolItem *item;
+
+               if (items[ii].callback) {
+                       GtkWidget *icon;
+                       const gchar *icon_name;
+
+                       icon_name = is_dark_theme ? items[ii].icon_name_dark : items[ii].icon_name;
+                       icon = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_LARGE_TOOLBAR);
+                       gtk_widget_show (GTK_WIDGET (icon));
+                       item = gtk_tool_button_new (icon, _(items[ii].label));
+                       gtk_tool_item_set_tooltip_text (item, _(items[ii].label));
+                       g_signal_connect_object (item, "clicked", items[ii].callback, self, 0);
+               } else {
+                       item = gtk_separator_tool_item_new ();
+               }
+
+               gtk_widget_show (GTK_WIDGET (item));
+               gtk_toolbar_insert (self->priv->action_toolbar, item, -1);
+       }
+
+       #ifdef HAVE_MARKDOWN
+       g_signal_connect_object (notebook, "switch-page", G_CALLBACK (e_markdown_editor_switch_page_cb), 
self, 0);
+       #endif
+}
+
+static void
+e_markdown_editor_class_init (EMarkdownEditorClass *klass)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_markdown_editor_constructed;
+}
+
+static void
+e_markdown_editor_init (EMarkdownEditor *self)
+{
+       self->priv = e_markdown_editor_get_instance_private (self);
+}
+
+/**
+ * e_markdown_editor_new:
+ *
+ * Creates a new #EMarkdownEditor
+ *
+ * Returns: (transfer full): a new #EMarkdownEditor
+ *
+ * Since: 3.44
+ */
+GtkWidget *
+e_markdown_editor_new (void)
+{
+       return g_object_new (E_TYPE_MARKDOWN_EDITOR, NULL);
+}
+
+/**
+ * e_markdown_editor_dup_text:
+ * @self: an #EMarkdownEditor
+ *
+ * Get the markdown text entered in the @self. To get
+ * the HTML version of it use e_markdown_editor_dup_html().
+ * Free the returned string with g_free(), when no longer needed.
+ *
+ * Returns: (transfer full): the markdown text
+ *
+ * Since: 3.44
+ **/
+gchar *
+e_markdown_editor_dup_text (EMarkdownEditor *self)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+
+       g_return_val_if_fail (E_IS_MARKDOWN_EDITOR (self), NULL);
+
+       buffer = gtk_text_view_get_buffer (self->priv->text_view);
+       gtk_text_buffer_get_bounds (buffer, &start, &end);
+
+       return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+/**
+ * e_markdown_editor_dup_html:
+ * @self: an #EMarkdownEditor
+ *
+ * Get the HTML version of the markdown text entered in the @self.
+ * To get the markdown text use e_markdown_editor_dup_text().
+ * Free the returned string with g_free(), when no longer needed.
+ *
+ * Note: The function can return %NULL when was not built
+ *    with the markdown support.
+ *
+ * Returns: (transfer full) (nullable): the markdown text converted
+ *    into HTML, or %NULL, when was not built with the markdown support
+ *
+ * Since: 3.44
+ **/
+gchar *
+e_markdown_editor_dup_html (EMarkdownEditor *self)
+{
+       #ifdef HAVE_MARKDOWN
+       GString *html;
+       gchar *text, *converted;
+       #endif
+
+       g_return_val_if_fail (E_IS_MARKDOWN_EDITOR (self), NULL);
+
+       #ifdef HAVE_MARKDOWN
+       text = e_markdown_editor_dup_text (self);
+       converted = cmark_markdown_to_html (text ? text : "", text ? strlen (text) : 0,
+               CMARK_OPT_VALIDATE_UTF8 | CMARK_OPT_UNSAFE);
+
+       html = e_str_replace_string (converted, "<blockquote>", "<blockquote type=\"cite\">");
+
+       g_free (converted);
+       g_free (text);
+
+       return g_string_free (html, FALSE);
+       #else
+       return NULL;
+       #endif
+}
diff --git a/src/e-util/e-markdown-editor.h b/src/e-util/e-markdown-editor.h
new file mode 100644
index 0000000000..bdc429c8b9
--- /dev/null
+++ b/src/e-util/e-markdown-editor.h
@@ -0,0 +1,57 @@
+/* -*- 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_EDITOR_H
+#define E_MARKDOWN_EDITOR_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_MARKDOWN_EDITOR \
+       (e_markdown_editor_get_type ())
+#define E_MARKDOWN_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_MARKDOWN_EDITOR, EMarkdownEditor))
+#define E_MARKDOWN_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_MARKDOWN_EDITOR, EMarkdownEditorClass))
+#define E_IS_MARKDOWN_EDITOR(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_MARKDOWN_EDITOR))
+#define E_IS_MARKDOWN_EDITOR_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_MARKDOWN_EDITOR))
+#define E_MARKDOWN_EDITOR_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_MARKDOWN_EDITOR, EMarkdownEditorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EMarkdownEditor EMarkdownEditor;
+typedef struct _EMarkdownEditorClass EMarkdownEditorClass;
+typedef struct _EMarkdownEditorPrivate EMarkdownEditorPrivate;
+
+struct _EMarkdownEditor {
+       GtkBox parent;
+       EMarkdownEditorPrivate *priv;
+};
+
+struct _EMarkdownEditorClass {
+       GtkBoxClass parent_class;
+};
+
+GType          e_markdown_editor_get_type              (void) G_GNUC_CONST;
+GtkWidget *    e_markdown_editor_new                   (void);
+gchar *                e_markdown_editor_dup_text              (EMarkdownEditor *self);
+gchar *                e_markdown_editor_dup_html              (EMarkdownEditor *self);
+
+G_END_DECLS
+
+#endif /* E_MARKDOWN_EDITOR_H */
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index c430fed8bd..666363e9e8 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -152,6 +152,7 @@
 #include <e-util/e-mail-signature-script-dialog.h>
 #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-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-markdown-editor.c b/src/e-util/test-markdown-editor.c
new file mode 100644
index 0000000000..88d439dd7e
--- /dev/null
+++ b/src/e-util/test-markdown-editor.c
@@ -0,0 +1,81 @@
+/* -*- 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"
+
+#include <glib/gi18n-lib.h>
+#include <e-util/e-util.h>
+
+static gboolean
+window_delete_event_cb (GtkWidget *widget,
+                       GdkEvent *event,
+                       gpointer user_data)
+{
+       gtk_main_quit ();
+
+       return FALSE;
+}
+
+static gint
+on_idle_create_widget (gpointer user_data)
+{
+       GtkWidget *window, *editor;
+
+       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
+
+       g_signal_connect (
+               window, "delete-event",
+               G_CALLBACK (window_delete_event_cb), NULL);
+
+       editor = e_markdown_editor_new ();
+
+       g_object_set (G_OBJECT (editor),
+               "halign", GTK_ALIGN_FILL,
+               "hexpand", TRUE,
+               "valign", GTK_ALIGN_FILL,
+               "vexpand", TRUE,
+               "visible", TRUE,
+               NULL);
+
+       gtk_container_add (GTK_CONTAINER (window), editor);
+
+       gtk_widget_show (window);
+
+       return FALSE;
+}
+
+gint
+main (gint argc,
+      gchar **argv)
+{
+       GList *modules;
+
+       bindtextdomain (GETTEXT_PACKAGE, EVOLUTION_LOCALEDIR);
+       bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+       textdomain (GETTEXT_PACKAGE);
+
+       gtk_init (&argc, &argv);
+
+       e_util_init_main_thread (NULL);
+       e_passwords_init ();
+
+       g_setenv ("EVOLUTION_SOURCE_WEBKITDATADIR", EVOLUTION_SOURCE_WEBKITDATADIR, FALSE);
+
+       gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), EVOLUTION_ICONDIR);
+       gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), E_DATA_SERVER_ICONDIR);
+
+       modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR);
+       g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse);
+
+       g_idle_add ((GSourceFunc) on_idle_create_widget, NULL);
+
+       gtk_main ();
+
+       e_misc_util_free_global_memory ();
+
+       return 0;
+}


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